Skip to content

1.什么是Vite ​

Vite 下一代前端开发与构建工具,用来构建前端工程,特点:很快!很快!很快!!!

vue-cli 也是一种基于webpack 的构建工具,相比 Vite 他比较慢 ,所以慢慢的用的人不多了 ,所以 我们来学习Vite吧 ,大大提高效率

💡 极速的服务启动⚡️ 轻量快速的热重载🛠️ 丰富的功能
使用原生 ESM 文件,无需打包!无论应用程序大小如何,都始终极快的模块热重载(HMR)对 TypeScript、JSX、CSS 等支持开箱即用。
📦 优化的构建🔩 通用的插件🔑 完全类型化的API
可选 “多页应用” 或 “库” 模式的预配置 Rollup 构建在开发和构建之间共享 Rollup-superset 插件接口。灵活的 API 和完整 TypeScript 类型。

2.基于Vite搭建Vue3项目 ​

方式1

shell
npm init vite@latest projectname  -- --template vue

参数说明:

initvite@latestprojectname--template vue
初始化项目使用Vite的最新版本项目名称项目类型是vue

image-20230708171933902

方式2【推荐】

这种方式 会让你选择 前端框架 , js 还是 ts

shell
npm create vite

image-20230708171947310

3.安装 router ​

shell
npm install vue-router@4 -S

在 src 下面创建 router 文件夹 , router文件夹下创建 index.js

js
import { 
    createRouter,
    createWebHistory
} from 'vue-router'
import Home from "../views/Home.vue"

const routes = [{
    path:"/",
    name:"Home",
    component:Home
},{
    path:"/about",
    name:"About",
    component:()=>import("../views/About.vue")
}]

const router = createRouter({
    history:createWebHistory(),
    routes
})

export default router

在main.js 进行配置

js
import { createApp } from 'vue'
import App from './App.vue'
import router from "./router"


createApp(App).use(router).mount('#app')

修改App.vue

vue
<script setup>

</script>

<template>
	<!-- 路由视口 -->
  <router-view></router-view>
</template>

<style scoped>

</style>

细节1:vite构件的工程 的 vue 文件的 template 中 不用再定义唯一的 根元素了,下面的写法 没问题

vue
<template>
    <div>
        Home
    </div>
    <h1>123</h1>
</template>

细节2:vite 中 引入 vue文件时 ,需要 写.vue 后缀 , import Home from "../views/Home.vue" Home 后面的.vue不能省

细节3: 导入 js 文件时 , .js 后缀可以省

4.配置src的别名 ​

修改vite.config.js

js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path"
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve:{
    alias:{
      "@":path.resolve(__dirname,"src")
    }
  },
})

5.vue3 代码风格 ​

Vue3对Vue2代码风格做了兼容 但是 不是完全兼容 优化的地方还是比较多的, 比如 setup 语法糖, vue2 的 js 部分的写法 层次比较分明,叫选项式API , vue3的写法 是 组合式 API,一大堆东西 写在 这里面,看不到 vue2 的 data,methods 等 这些元素

响应式方式1 ref 需要 .value

vue
<template>
    <div>
        Home
        {{ str }}

        <button @click="fun1">按钮</button>
    </div>
</template>
<script setup>
import {ref} from 'vue';
    // let str = "hello";
    let str = ref("hello");
    function fun1(){
        str.value = "world"
    }
</script>

响应式方式2 reactive 只能修饰 对象 或 数组

vue
<template>
    <div>
        Home
        {{ str }}

        <button @click="fun1">按钮</button>
    </div>
</template>

<script setup>
import {ref,reactive} from 'vue';
    // let str = "hello";
    let str = reactive({a:"hello"});

    function fun1(){
        str.a = "world"
    }

</script>

6.setup 语法糖插件 ​

解决 import {ref,reactive,....} from 'vue' 引入 过长的问题

安装插件

shell
npm i unplugin-auto-import -D

在vite.config.js 中 导入

shell
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import path from "path"

export default defineConfig({
  plugins: [
    vue(),
    AutoImport({
      imports:['vue','vue-router']
    })
  ],
  resolve:{
    alias:{
      "@":path.resolve(__dirname,"src")
    }
  },
})

配置好插件后 ,就不用在 vue 页面中 手动导入了 可以去掉这句话

shell
// import {ref,reactive} from 'vue';

7.toRefs ​

vue
<template>
    <div>
        <!-- {{ obj.name }}
        {{ obj.age }} -->

        {{ name }}
        {{ age }}

        <button @click="fun1">按钮</button>
    </div>
</template>

<script setup>
import { reactive } from "vue";

    let obj = reactive({
        name:'zs',
        age:19
    });
	// toRefs 结构响应式数据
    let {name,age} = toRefs(obj);
    function fun1(){
        // obj.name = "ls";
        name.value = "ls"
    }
</script>

8.computed ​

vue
<template>
<div>
    vue3的计算属性:

    {{ changeStr }}
</div>
</template>

<script setup>
   // import {ref,computed} from 'vue'    因为 咱们安装了插件 所以 这行代码可以注释掉
    let str = ref('这是数据')
    //计算属性
    let changeStr = computed(()=>{
        return str.value + 2
    })
</script>
<style scoped>
/* //@import url();引入公共css类 */

</style>

9.watch ​

vue
<template>
    <div>
        <input type="text" v-model="str">
        <input type="text" v-model="num">

        {{ obj }}
        <button @click="fun1">按钮</button>
    </div>
</template>

<script setup>
import { reactive, watch, watchEffect } from "vue";

    let str = ref("这是一个数据")
    let num = ref(100)
    let obj = reactive({
        a:1,
        b:2,
        m:{
            c:3
        }
    })
    // 监听某一个属性
    watch(str,(newVal,oldVal)=>{
        console.log(newVal,oldVal);
    })
    // 同时监听多个属性
    watch([str,num],(newVal,oldVal)=>{
        console.log(newVal,oldVal);
    })

    // 页面一初始化 就监听   上来就监听
    watch([str,num],(newVal,oldVal)=>{
        console.log(newVal,oldVal);
    },{
        immediate:true
    })


    const fun1 = ()=>{
        obj.a="a"
        // obj.m.c = 888
    }

    // 监听一个对象
    watch(obj,(newVal,oldVal)=>{
        console.log(newVal)
    })

     // 只监听一个对象的一个属性
     watch(()=>obj.a,(newVal,oldVal)=>{
        console.log(newVal,oldVal)
    })

    // 可以监听所有   相当于 立即执行
    watchEffect(()=>{
        console.log("11111"+str.value)
        console.log("22222"+obj.a)
    })
</script>

监听路由

vue
// import { useRouter,useRoute } from "vue-router";   插件中有  所以不需要再导入
// 监听路由
let router = useRouter();

watch(()=>router.currentRoute.value,(newVal)=>{
    console.log(newVal)
},{
    immediate:true
})

10.钩子函数 ​

image-20230709103920763

vue
<template>
    <div>
        {{ str }}

        <button @click="fun">修改</button>
        <router-link to="/">跳转</router-link>
    </div>
</template>

<script setup>
import { onBeforeUnmount, onUnmounted } from "vue"

// import {ref, onBeforeMount, onMounted, onUpdated } from "vue";
let str = ref("hello")
const fun = ()=>{
    str.value = "world"
}

onBeforeMount(()=>{
    console.log("页面挂载之前,可以在这里请求接口")
})

onMounted(()=>{
    console.log("页面挂载完毕")
})

onBeforeUpdate(() => {
    console.log("修改前")
})

onUpdated(()=>{
    console.log("修改后")
})

onBeforeUnmount(()=>{
    console.log("销毁前")
})

onUnmounted(()=>{
    console.log("销毁后")
})
</script>

11.路由 ​

vue2用的router版本是3.x vue3 用的router版本是4.x

vue
<template>
    <div>
        <button @click="goAbout">跳转</button>
    </div>
</template>

<script setup>
    // import { useRouter,useRoute } from "vue-router";   有插件

    // 等同于 vue2  的  this.$router
    let router = useRouter();

    // 等同于 vue2  的  this.$route
    let route = useRoute();

    const goAbout=()=>{
        router.push('/about')
    }
</script>

导航守卫 ​

全局路由守卫 ​

shell
beforeEach(to, from, next) 全局前置守卫,

路由跳转前触发 beforeResolve(to, from, next) 全局解析守卫 在所有组件内守卫和异步路由组件被解析之后触发 

afterEach(to, from) 全局后置守卫,路由跳转完成后触发

路由独享守卫 ​

shell
beforeEnter(to,from,next) 路由对象单个路由配置 ,单个路由进入前触发

组件路由守卫 ​

shell
beforeRouteEnter(to,from,next) 在组件生命周期beforeCreate阶段触发 

beforeRouteUpdadte(to,from,next) 当前路由改变时触发 

beforeRouteLeave(to,from,next) 导航离开该组件的对应路由时触发

参数 ​

shell
to: 即将要进入的目标路由对象 

from: 即将要离开的路由对象 

next(Function):是否可以进入某个具体路由,或者是某个具体路由的路径

demo

image-20230709113116296

12.组件 ​

父组件 调用 子组件 并传递数据

vue
<template>
    
    <div>
        父组件
        <zizujian :msg="msg"></zizujian>
    </div>
</template>

<script setup>
import Zizujian from '../components/zizujian.vue'
let msg = ref("这是父组件传递过去的数据")

</script>
vue
<template>
    <div>
            子组件
            {{ msg }}
    </div>
</template>

<script setup>
    // import {defineProps} from 'vue'    有插件

    defineProps({
        msg:{
            type:String,
            default:"默认值"

        }
    })
</script>

子组件传递给父组件

子组件写法 这个是 ts的写法

vue
<template>
    <div>
        子组件

        {{ msg }}

        <button @click="method1">调用父组件的函数</button>
    </div>
</template>

<script setup lang="ts">
    import {ref,defineProps} from 'vue'    //有插件

    defineProps({
        msg:{
            type:String,
            default:"默认值"

        }
    })

    let num = ref(200);

    const emit = defineEmits<{
        (e: 'fn',id:number): void
    }>()

    const method1 = ()=>{
        emit('fn',num)
    }

</script>

子组件写法 这个是 js的写法

vue
<template>
    <div>
        子组件

        {{ msg }}

        <button @click="method1">调用父组件的函数</button>
    </div>
</template>

<script setup >
    import {ref,defineProps} from 'vue'    //有插件

    defineProps({
        msg:{
            type:String,
            default:"默认值"
        }
    })

    let num = ref(200);

    const emit = defineEmits(['fn']);

    const method1 = ()=>{
        emit('fn',num)
    }

</script>

父组件写法

vue
<template>
    
    <div>
        父组件
        <zizujian :msg="msg" @fn="method1"></zizujian>
    </div>
</template>

<script setup>
import Zizujian from '../components/zizujian.vue'
let msg = ref("这是父组件传递过去的数据")

const method1=(n)=>{
    console.log("子组件传过来的参数"+n.value)
}
</script>

13.父子组件使用v-model ​

父组件

vue
<zizujian2 v-model:num="num"></zizujian2>

let num = ref(99)

子组件

vue
<template>
    <div>
        子组件2
        {{ num }}
        <button @click="change">修改</button>
    </div>
</template>

<script setup>
    const props = defineProps({
        num:{
            type:Number,
            default: 100
        }
    })
    const emit = defineEmits(['update:num'])
	const change=()=>{
        emit('update:num',199)
    }
</script>

14.兄弟组件数据传递 ​

A组件传递数据给B组件

shell
npm install mitt -S

创建@/plugins/Bus.js

js
import mitt from "mitt";
const emitter = mitt();

export default emitter;

A组件

vue
<template>
    <div>
        <h1>A组件</h1>
        <button @click="fun">按钮</button>
    </div>
</template>

<script setup>
import emitter from '../plugins/Bus'

let str = ref("这是A组件的数据")

const fun = ()=>{
    emitter.emit('fn',str)
}
</script>

B组件

vue
<template>
    <div>
        <h1>B组件</h1>
        {{ s }}
    </div>
</template>

<script setup>
    import { onBeforeMount } from 'vue';
    import emitter from '../plugins/Bus'

    let s = ref('');

    onBeforeMount(()=>{
        emitter.on('fn',e=>{
            console.log("A组件传递过来的数据:"+e.value)
            s.value = e.value
        })
    })
</script>

15.插槽 slot ​

具名插槽

调用方 zujian.vue

vue
<C> 
    <template v-slot:xxx>
        这是沙发
    </template>
     <!-- v-slot 可以简写为# -->
    <template #yyy>
        这是床
    </template>
</C>

被调用组件C

vue
<template>
    <div>
        <header>
            <slot name="xxx"></slot>
        </header>
        <h1>插槽</h1>
        <footer>
           <slot name="yyy"></slot>
        </footer>
    </div>
</template>

<script setup>

</script>

作用域插槽

调用方 zujian.vue

vue
<template>
<div>
    父组件
    <br>
    子组件1 <zizujian :msg="msg" @fn="method1"></zizujian>

    <br>
    子组件2
    <zizujian2 v-model:num="num"></zizujian2>

    <br>
    <A></A>
    <B></B>

    <br>
    <C> 
        <template v-slot:xxx>
            这是沙发
        </template>
        <!-- v-slot 可以简写为# -->
        <template #yyy>
            这是床
        </template>

        <!-- <template v-slot="{data}">  v-slot可以简写为  #default    -->
        <template #default="{data}">
           {{ data.name }} -- {{ data.age }}
        </template>
    </C>
    </div>
</template>

<script setup>
import Zizujian from '../components/zizujian.vue'
import Zizujian2 from '../components/zizujian2.vue'
import A from '../components/A.vue'
import B from '../components/B.vue'
import C from '../components/C.vue'


let msg = ref("这是父组件传递过去的数据")

const method1=(n)=>{
    console.log("子组件传过来的参数"+n.value)
}

let num = ref(99)

</script>

被调用方

vue
<template>
    <div>
        <h1>具名插槽</h1>
        <header>
            <slot name="xxx"></slot>
        </header>
       
        <footer>
           <slot name="yyy"></slot>     

        </footer>

        <h1>作用域插槽</h1>

        <section>
            <div v-for="item in list" :key="item.id">
                <slot :data="item"></slot>
            </div>
        </section>

    </div>
</template>

<script setup>
    let list = ref([
        {id:1,name:'zs',age:18},
        {id:2,name:'ls',age:19},
        {id:3,name:'ww',age:21},
        {id:4,name:'zl',age:20},
    ])
</script>

动态插槽 说白了 就是通过变量 动态传入 插槽名字

调用方

vue
 <template #[zzz]>
动态插槽
</template>
<script setup>
	let zzz = ref('yyy')
</script>

16.Taleport ​

传送 把某一个 布局 或者 盒子 传送到另外一个地方

比如 D组件 传递 内容 到 teleport.vue

teleport.vue

vue
<template>
    <div>
      
       <div id="main">
            这是main
       </div>

       <div class="container">
        这是container
       </div>
       
    </div>
    
    <D></D>
</template>


<script setup>
    import D from '../components/D.vue'
</script>

D.vue

vue
<template>
    <div>
        <h1>D组件</h1>
        <!-- 传送到id 为 main 的地方 -->
        <teleport to="#main">
            <div>这是传送</div>
        </teleport>
    </div>
</template>

<script setup>

</script>

可以用 id 或 类别选择器 等 方式 指定传送位置

vue
<div id="main">
    这是main
</div>

<div class="container">
    这是container
</div>

<teleport to="#main">传送内容</teleport>
<teleport to=".container">传送内容</teleport>

注意 这里 的书写位置 有讲究, 传送的目的地 得 先于 teleport 元素 而存在 比如 下面的写法 就有问题

vue
这里 要传送到 id 为 main 的元素 ,结果  id为main 的 元素 还没加载   会报警告
<teleport to="#main">传送内容</teleport>
<teleport to=".container">传送内容</teleport>


<div id="main">
    这是main
</div>

<div class="container">
这是container
</div>

17.动态组件 ​

dongtaizujian.vue

vue
<template>
    <div>
        动态组件
        <ul>
            <li v-for="item in tabList" :key="item.id" @click="change(item.id)">
            {{ item.name }}</li>
        </ul>

        <keep-alive>
            <component :is="currentComponent.com"></component>
        </keep-alive>
       
    </div>
</template>


<script setup>
    import { reactive } from 'vue'
    import E from '../components/E.vue'
    import F from '../components/F.vue'
    import G from '../components/G.vue'

    let tabList= reactive([
        {name:'E准备面试题',id:1,com:markRaw(E)},
        {name:'F准备简历',id:2,com:markRaw(F)},
        {name:'G准备项目',id:3,com:markRaw(G)},
    ])

    let currentComponent = reactive({
        com:E
    })

    const change= (id)=>{
        currentComponent.com = tabList[id-1].com 
    }
</script>

18.异步组件 ​

yibuzujian.vue

vue
<template>
    <div>
        异步组件
        <E></E>
        <F></F>
        <div ref="target">
            <G v-if="targetIsVisible"></G>
        </div>
        
    </div>
</template>

<script setup>
    // import { defineAsyncComponent } from 'vue'    有插件
    import {useIntersectionObserver} from '@vueuse/core'

    import E from '../components/E.vue'
    import F from '../components/F.vue'
    // import G from '../components/G.vue'

    const G = defineAsyncComponent(()=>import('../components/G.vue'))

    const target = ref(null);
    const targetIsVisible = ref(false);

    const { stop } = useIntersectionObserver(
        target,
        ([{isIntersecting}])=>{
            if(isIntersecting){
                targetIsVisible.value = isIntersecting
            }
        }
    )

</script>

安装一个插件

shell
npm i @vueuse/core -S

在页面 yibuzujian.vue 配置

vue
import {useIntersectionObserver} from '@vueuse/core'
const G = defineAsyncComponent(()=>import('../components/G.vue'))

const target = ref(null);
const targetIsVisible = ref(false);

const { stop } = useIntersectionObserver(
    target,
    ([{isIntersecting}])=>{
        if(isIntersecting){
            targetIsVisible.value = isIntersecting
        }
    }
)

观察效果

拉动页面,拉动到有图片的组件时 才加载那个图片地址

image-20230711003516513image-20230711003536953