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