Vue3基础知识

202 阅读12分钟

前言

该文章记录一些Vue3的一些基本知识,后续将会逐步增加,所有内容均从网上整理而来,加上自己得理解做一个整合,方便工作中使用。

一、Vue3简介

1. vue3优点

  • 打包体积减小
  • 渲染速度加快
  • 内存减少

2. 源码升级

  • 使用Proxy代替defineProperty实现响应式
  • 重写虚拟DOM的实现和Tree-Shaking
  • 支持TypeScript

3. 新特性

  1. Composition API(组合api)
    • setup 配置
    • ref与reactive
    • watch与watchEffect
    • provide与inject
  2. 新的内置组件
    • Fragment
    • Teleport
    • Suspense
  3. 其他改变
    • 新的生命周期钩子
    • data 选项应始终被声明为一个函数
    • 移除keyCode支持作为 v-on 的修饰符

4. vscode安装新插件

Vue Languague Features (Volar)

二、Vue3创建

1.使用 vue-cli 创建

官方文档

2. 安装步骤

## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create <project-name>
## 启动
cd <project-name>
npm run serve

2.使用 vite 创建

官方文档

## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev

三、常用 Composition API

3-1. setup函数

  • Vue3.0中一个新的配置项,值为一个函数
  • 组件中所用到的:数据、方法等等,均要配置在setup中
  • setup函数的两种返回值:
    1. 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
    2. 若返回一个渲染函数:则可以自定义渲染内容。(了解)
  • 注意点:
    1. 不要与Vue2.x配置混用
      • Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法
      • 但在setup中不能访问到Vue2.x配置(data、methos、computed...)
      • 如果有重名, setup优先
    2. setup不能是一个async函数(有特殊情况可以:使用Suspense和异步加载组件配合使用)
//传统
<template>
    <!-- vue3可以不用一个根目录包裹 -->
    <div>{{name}}</div>
    <button @click="sayHello">打招呼</button>
</template>

<script>
  //setup函数如果想返回渲染函数,需要引入h函数
  //import { h } from 'vue'
  export default {
    name: 'App',
    //此处只是展示一下setup,暂不考虑数据响应式
    setup() {
      //在setup中配置data
      let name = '胡歌'
      
      //在setup中配置methods
      function sayHello() {
        console.log(`${name}向你问好!`)
      }
      
      //setup函数需要返回值,在模板中均可以直接使用
      return {
        name,
        sayHello
      }
      
      //如果要返回一个渲染函数
      //需要引入h函数
      //return () => h('h1', '返回渲染函数') //渲染一个H1标签,内容为'返回渲染函数'
    }
  }
</script>

使用语法糖,不需要再return变量

<script setup>
  //相当于就是再setup函数中书写代码
  import { ref } from 'vue';

  let student = ref({
    name: '胡歌',
    age:20
  })

  const changeName = () => {
    student.value.name='李逍遥'
  }
  </script>

  <template>
    <div>{{ student.name }}</div>
    <button @click="changeName">修改姓名</button>
  </template>

3-2. ref函数

  • 作用:将setup中定义的数据变量,变成引用对象

  • 语法:

    • 先按需引入:import { ref } from 'vue'
    • 使用ref函数包裹值:let name = ref('胡歌')
    • 操作变量:name.value = '李斯'
    • 模板中读取改变量,不需要.value,直接使用
    <template>
      <div>{{name}}</div>
      <button @click="changeInfo">修改姓名</button>
    </template>
    
    <script>
    import { ref } from 'vue'
    export default {
      name: 'App',
      setup() {
        //使用ref函数包裹值
        let name = ref('胡歌')
        let student = ref({
            no:1,
            sex:'男'
        })
        
        //操作变量需要.value
        function changeInfo() {
          name.value = '李斯'
          student.value.sex='女'
        }
        return {
          name,
          changeInfo
        }
      }
    }
    </script>
    

    语法糖

    <script setup>
    import { ref } from 'vue';
    
    let student = ref({
      name: '胡歌',
      age:20
    })
    
    const changeName = () => {
      student.value.name='李逍遥'
    }
    </script>
    
    <template>
      <div>{{ student.name }}</div>
      <button @click="changeName">修改姓名</button>
    </template>
    
  • 补充:

    • 接收的数据可以是:基本类型、也可以是对象类型。

    • 基本类型的数据:响应式依然是靠Object.defineProperty()的get和set实现

    • 复杂类型的数据:内部使用了vue3新函数reactive函数

3-3. reactive函数

  • 作用:定义一个对象类型的响应式数据(基本类型用ref函数)

  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)

  • reactive定义的响应式数据是“深层次的”

  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作

    <template>
       <div>{{person.name}}</div>
       <div>{{person.job.type}}</div>
       <div>{{person.job.salary}}</div>
       <button @click="changeInfo">修改信息</button>
    </template>
    
    <script>
        import { reactive } from 'vue'
        export default {
          name: 'App',
          setup() {
            let person = reactive({
              name: '胡歌',
              age: 18,
              job: {
                type: '演员',
                salary: '30K'
              }
            })
    
            function changeInfo() {
              person.age = '20'
              person.job.type = '爸爸'
              person.job.salary = '60k'
            }
    
            return {
              person,
              changeInfo
            }
          }
        }
    </script>
    

    语法糖

    <script setup>
    import { reactive, ref } from 'vue';
    
    let student = reactive({
      name: '胡歌',
      age:20
    })
    
    const changeName = () => {
      student.name = '李逍遥'
      student.age=18
    }
    </script>
    
    <template>
      <div>{{ student.name }}</div>
      <div>{{ student.age }}</div>
      <button @click="changeName">修改姓名</button>
    </template>
    

3-4. vue3响应式原理

  • vue2响应式实现原理:

    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)
    • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)
    Object.defineProperty(data, 'count', {
        get () {}, 
        set () {}
    })
    
    • 存在问题:
      • 新增属性、删除属性, 界面不会更新。
      • 直接通过下标修改数组, 界面不会自动更新。
  • vue3响应式实现原理

    • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
    • 通过Reflect(反射): 对源对象的属性进行操作。
    • MDN文档-Proxy
    • MDN文档-Reflect
    new Proxy(data, {
        // 拦截读取属性值
        get (target, prop) {
            return Reflect.get(target, prop)
        },
        // 拦截设置属性值或添加新属性
        set (target, prop, value) {
            return Reflect.set(target, prop, value)
        },
        // 拦截删除属性
        deleteProperty (target, prop) {
            return Reflect.deleteProperty(target, prop)
        }
    })
    

3-5.reactive对比ref

  • 从定义数据角度对比:

    • ref用来定义:基本类型数据
    • reactive用来定义:对象(或数组)类型数据
    • 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
  • 从原理角度对比:

    • ref通过Object.defineProperty()getset来实现响应式(数据劫持)。
    • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
  • 从使用角度对比:

    • ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value

    • reactive定义的数据:操作数据与读取数据:均不需要.value

3-6.setup函数注意项

  • setup执行的时机

    • 在beforeCreate之前执行一次,this是undefined。
  • setup的参数

    • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
    • context:上下文对象
      • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
      • slots: 收到的插槽内容, 相当于 this.$slots
      • emit: 分发自定义事件的函数, 相当于 this.$emit
      //父组件
      <template>
        <!-- 自定义事件 props传参-->
        <HelloWorld @change="change" msg='胡歌' sex='男'>
          <!-- 添加插槽 -->
          <template v-slot:job>
            <span>{{job}}</span>
          </template>
        </HelloWorld>
      </template>
    
      <script>
          import { ref } from 'vue'
          import HelloWorld from './components/HelloWorld.vue'
          export default {
            name: 'App',
            components: {
              HelloWorld
            },
            setup() {
              let job = ref('男演员')
              //接收自定义事件返回值,修改变量
              function change(value) {
                job.value = value
              }
              return {
                job,
                change
              }
            }
          }
      </script>
      
      
      //子组件
      <template>
        <div>
          <!-- 接收插槽 -->
          <slot name="job"></slot>
          <button @click="change">点击换职业</button>
        </div>
      </template>
    
      <script>
          export default {
            props: ['msg', 'sex'],
            
            //自定义事件配置项
            emits: ['changInfo'],
            
            setup(props, content) {
              //输出props
              console.log(props) //Proxy {msg: '胡歌', sex: '男'}
              
              //自定义事件 使用content.emit方法
              function change() {
                content.emit('change', '父亲')
              }
              return {
                change
              }
            },
            
          }
      </script>
    

3-7. 计算属性-computed函数

<template>
     <div>
       <p>姓:{{person.firstName}}</p>
       <p>名:{{person.lastName}}</p>
       <!--计算属性 -->
       <p>姓名:{{fullName1}}</p>
       <!--计算属性 传递参数 -->
       <p>姓名:{{fullName2('-男演员')}}</p>
     </div>
</template>

<script>
   // 计算属性需要引入 computed 函数
   import { reactive, computed } from 'vue'
   export default {
     setup() {
       let person = reactive({
         firstName: '胡',
         lastName: '歌'
       })
       //计算属性
       let fullName1 = computed(() => {
         return person.firstName + person.lastName
       })
       //计算属性-接收参数
       let fullName2 = computed(() => value => {
         return person.firstName + person.lastName + value
       })
       
       //计算属性完整写法-读和写
       let fullName3 =computed({
           get(){
               return xxx
           },
           set(value){
               //xxxxx
           }
       })
       
       return {
         person,
         fullName1,
         fullName2
       }
     }
   }
</script>

3-8. 监听-watch函数

  1. 监听 ref()定义的响应式数据 ,ref一般用于基本类型数据

    • 语法: watch(监听对象,回调函数,配置项)
    • 单独监听/合并监听
    <template>
     <div>
       <!-- 第一个监听对象 -->
       <div class="one">
         <p>{{sum}}</p>
         <button @click="sum++">点我加1</button>
       </div>
       <!-- 第二个监听对象 -->
       <div class="two">
         <p>{{name}}</p>
         <button @click="name+='!'">点我加1</button>
       </div>
     </div>
    </template>
    
    <script>
       // 监视需要引入 watch 函数
       import { ref, watch } from 'vue'
       export default {
         setup() {
           let sum = ref(0)
           let name = ref('胡歌')
    
           // 情况一,监听单独监听一个变量 watch(监听对象,回调函数,配置项)
           // 监听sum变化 
           watch(sum,(newValue, oldValue) => {
               console.log(newValue, oldValue)
             },
             { immediate: true }
           )
    
           // 监听name变化 
           watch(name,(newValue, oldValue) => {
               console.log(newValue, oldValue)
             },
             { immediate: true }
           )
    
           // 情况二,同时监听多个变量
           // 监听合并写法 watch([监听对象1,监听对象2,···],回调函数,配置项)
           watch([sum, name],
             (newValue, oldValue) => {
               console.log(newValue, oldValue)
             },
             { immediate: true }
           )
           // 合并监听-返回值不一样, 以数组的方式返回所有被监听的对象,无论是否发生改变
           // 立即监听-输出:    (2) [0, '胡歌'] []
           // 点击sum增加-输出: (2) [1, '胡歌'] (2) [0, '胡歌']
           // 点击name增加-输出:(2) [1, '胡歌!'] (2) [1, '胡歌']
    
           return {
             sum,
             score
           }
         }
       }
    </script>
    
  2. 监听 reactive()定义的响应式数据 ,reactive一般用于复杂类型数据

    reactive监听复杂类型数据有特殊情况

    <template>
         <div>
           <!-- 监听复杂类型数据 -->
           <p>姓名:{{person.name}}</p>
           <p>年龄:{{person.age}}</p>
           <p>工资:{{person.job.jobOne.money}}</p>
           <button @click="person.name+='~'">修改姓名</button>
           <button @click="person.age++">修改年龄</button>
           <button @click="person.job.jobOne.money++">修改工资</button>
         </div>
    </template>
    
    <script>
       // 监视需要引入 watch 函数
       import { reactive, watch } from 'vue'
       export default {
         setup() {
           //定义person
           let person = reactive({
             name: '胡歌',
             age: 18,
             job: {
               jobOne: {
                 money: 2000
               }
             }
           })
           return {
             person
           }
         }
       }
    </script>
    
    • 情况一:监听整个person,只要有修改,都监听
    watch(person, (newValue, oldValue) => {
      console.log(newValue, oldValue)
    })
    //存在的问题:
    //1.监听整个复杂类型数据,默认强制开启深度监视,{deep:true},无法修改
    //2.监听整个复杂类型数据,无法获取oldValue
    
    • 情况二:监听person中单个属性(基础类型的数据)
    // 被监听的属性需要用函数返回
    watch(() => person.name,(newValue, oldValue) => {
        console.log(newValue, oldValue)
    })
    //监听复杂类型数据中单个属性(基础类型的数据):
    //1.深度监视不需要配置
    //2.可以获取oldValue
    
    • 情况三:监听person中单个属性(复杂类型的数据)
    // 被监听的属性需要用函数返回
    watch(() => person.job,(newValue, oldValue) => {
        console.log(newValue, oldValue)
    })
    //监听复杂类型数据中单个属性(复杂类型的数据):
    //1.深度监视必须手动开启{deep:true}
    //2.不可以获取oldValue
    
    • 情况四:监听person中多个属性
    watch(
     //第一个参数 被监听对象数组括起来
     [() => person.job, () => person.name], 
     //第二个参数,回调函数
     (newValue, oldValue) => {
       console.log(newValue, oldValue)
     },
     //第三个参数,配置项(如果有复杂类型的数据,必须要开启deep)
     { 
       deep: true 
     }
    )
    //监听多个属性
    //1.如果有复杂类型的数据,深度监视必须手动开启{deep:true}
    //2.复杂类型的数据不可以获取oldValue,简单类型的数据可以获取oldValue
    

3-9. watchEffect函数

  • watch的用法:既要指明监视的属性,也要指明监视的回调
  • watchEffect的用法:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
  • watchEffect和computed区别:
    • computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值
    • watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
watchEffect(() => {
 //自动调用一次
 //如果没有使用变量,什么都不监视
 //使用了哪些变量,当其变量改变时,调用一次回调函数

 //注意如果是复杂类型,没有深度监视,必须使用具体属性
 console.log(person.age)
 console.log('监视生效了')
})

9. vue3生命周期

  • Vue3中的生命周期可以继续使用vue2中生命周期钩子函数,有两个更名

    • beforeDestroy改名为 beforeUnmount
    • destroyed改名为 unmounted
  • Vue3也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下

    • beforeCreate===>setup()
    • created=======>setup()
    • beforeMount ===>onBeforeMount
    • mounted=======>onMounted
    • beforeUpdate===>onBeforeUpdate
    • updated =======>onUpdated
    • beforeUnmount ==>onBeforeUnmount
    • unmounted =====>onUnmounted
    // 监视需要引入 watch 函数
    import { 
        onBeforeMount, 
        onMounted, 
        onBeforeUpdate, 
        onUpdated, 
        onBeforeUnmount, 
        onUnmounted 
    } from 'vue'
    export default {
      setup() {
        //setup 相当于 beforeCreate 和 created
        console.log('---setup---')
        onBeforeMount(() => {
          console.log('---onBeforeMount---')
        })
        onMounted(() => {
          console.log('---onMounted---')
        })
        onBeforeUpdate(() => {
          console.log('---onBeforeUpdate---')
        })
        onUpdated(() => {
          console.log('---onUpdated---')
        })
        onBeforeUnmount(() => {
          console.log('---onBeforeUnmount---')
        })
        onUnmounted(() => {
          console.log('---onUnmounted---')
        })
        return {}
      }
    }
    //写在setup函数中,怎么使用async···await将异步变成同步?
    

3-10. 自定义hook函数

  • 什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
  • 类似于vue2.x中的mixin。
  • 自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
//hook函数---usePoint.js
import { reactive, onMounted, onBeforeUnmount } from 'vue'
export default function () {
  let point = reactive({
    x: 0,
    y: 0
  })
  //执行事件
  function savePoint(event) {
    point.x = event.x
    point.y = event.y
    console.log(event.x)
  }
  //绑定事件
  onMounted(() => {
    window.addEventListener('click', savePoint)
  })
  //取消绑定事件
  onBeforeUnmount(() => {
    window.removeEventListener('click', savePoint)
  })

  return point
}

//使用hook函数的组件
<template>
  <div>
    鼠标点击位置:x:{{point.x}};y:{{point.y}}
  </div>
</template>

<script>
    // 引入hook函数
    import usePoint from '@/hooks/usePoint'
    export default {
      setup() {
        const point = usePoint()
        return { point }
      }
    }
</script>

3-11. toRef函数

  • 作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
  • 语法:const name = toRef(person,'name')
  • 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
  • 扩展:toRefs 与toRef功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
<template>
  <div>
    <!-- 
      不使用toRef函数
      <p>姓名:{{person.name}}</p>
      <p>年龄:{{person.age}}</p>
      <p>工资:{{person.job.one.salary}}</p> 
    -->
    
    <!-- 使用toRef函数,减少使用person -->
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}</p>
    <p>工资:{{job.one.salary}}</p>
  </div>
</template>

<script>
    import { reactive, toRef, toRefs } from 'vue'
    export default {
      setup() {
        let person = reactive({
          name: '胡歌',
          age: 19,
          job: {
            one: {
              salary: 1000
            }
          }
        })

        //使用toRef
        // let name = toRef(person, 'name')
        // console.log(name) //name变成ObjectRefImpl对象

        //使用toRefs
        // let newperson = toRefs(person)
        // console.log(newperson) //newperson对象的第一层都变成ObjectRefImpl对象
        return {
          //不使用toRef
          // person
          
          //使用toRefs函数
          ...toRefs(person)
        }
      }
    }
</script>

四、其他 Composition API

4-1. shallowReactive 与 shallowRef

  • shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
  • shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
  • 什么时候使用?
    • 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
    • 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。

4-2. readonly 与 shallowReadonly

  • readonly: 让一个响应式数据变为只读的(深只读)。
  • shallowReadonly:让一个响应式数据变为只读的(浅只读)。
  • 应用场景: 不希望数据(尤其是这个数据是来自于其他组件时)被修改时。
<template>
 <div>
   <p>姓名:{{name}}</p>
   <p>年龄:{{age}}</p>
   <p>工资:{{job.one.salary}}</p>
   <!-- 
     //shallowReadonly,第一层age不能修改, salary可以修改
     <button @click="job.one.salary++">加工资</button>
     <button @click="age++">加年纪</button> 
   -->

   <!-- 
     //readonly 都不能修改,vue警告
     <button @click="job.one.salary++">加工资</button>
     <button @click="age++">加年纪</button> 
   -->
 </div>
</template>

<script>
   import { reactive, toRefs, shallowReadonly, readonly } from 'vue'
   export default {
     setup() {
       let person = reactive({
         name: '胡歌',
         age: 19,
         job: {
           one: {
             salary: 1000
           }
         }
       })
       //第一层数据只读,深层可以修改
       // person = shallowReadonly(person)

       //全部只读
       // person = readonly(person)
       return {
         ...toRefs(person)
       }
     }
   }
</script>

4-3. toRaw 与 markRaw

  • toRaw:
    • 作用:将一个由reactive生成的响应式对象转为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
  • markRaw:
    • 作用:标记一个对象,使其永远不会再成为响应式对象。
    • 应用场景:
      1. 有些值不应被设置为响应式的,例如复杂的第三方类库等。
      2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。

4-4. customRef

  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。

  • 实现防抖效果:

    <template>
      <div>
        <input type="text" v-model="keyword">
        <h4>{{keyword}}</h4>
      </div>
    </template>
    
    <script>
        import { ref, customRef } from 'vue'
        export default {
          setup() {
            //自定义一个myRef
            function myRef(value) {
              let timer = null
              //通过customRef去实现自定义
              return customRef((track, trigger) => {
                return {
                  get() {
                    track() //告诉Vue这个value值是需要被“追踪”的
                    return value
                  },
                  set(newValue) {
                    //生成一个定时器,晚500ms更新,实现防抖节流效果
                    clearTimeout(timer)
                    timer = setTimeout(() => {
                      value = newValue
                      trigger() //告诉Vue去更新模板
                    }, 500)
                  }
                }
              })
            }
            let keyword = myRef('胡歌')
            return {
              keyword
            }
          }
        }
    </script>
    

4-5. provide(提供) 与 inject(注入)

  • 实现祖组件与任意后代组件的通信

  • 具体用法:

    //祖组件中引入provide
    import { provide} from 'vue'
    
    setup(){
       ......
       let car = reactive({name:'奔驰',price:'40万'})
       //祖组件使用provide
       provide('car',car)
       ......
    }
    
    //任意后代组件引入inject
    import { inject } from 'vue'
    
    setup(){
       ......
       //可以理解为inject接收来自祖组件的数据,如果传的是响应式数据,收到的还是响应式数据
       const car = inject('car')
       return {car}
       ......
    }
    

4-6. 响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象

  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理

  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理

  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

五、Composition API 的优势

5-1. Options API 存在的问题

  • 使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改

5-2. Composition API 的优势

  • 让相关功能的代码更加有序的组织在一起

六、新的组件

6-1. Fragment

  • 在Vue2中: 组件必须有一个根标签
  • 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处: 减少标签层级, 减小内存占用

6-2. Teleport(移动、传送)

  • Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。
<!-- <teleport to="移动位置,可以是Html标签,也可以是选择器"> -->
<teleport to="body"> <!-- 将下面结构挂载body上 -->
 <div v-if="isShow" class="mask">
 	<div class="dialog">
 		<h3>我是一个弹窗</h3>
 		<button @click="isShow = false">关闭弹窗</button>
 	</div>
 </div>
</teleport>

6-3. Suspense

  • 等待异步组件时渲染提示内容,让应用有更好的用户体验
  • 使用步骤
    1. 异步引入组件
        import {defineAsyncComponent} from 'vue'
        const Child = defineAsyncComponent(()=>import('组件地址'))
    
    1. 使用Suspense包裹组件,并配置好default 与 fallback
    <template>
       <div class="app">
           <h3>我是App组件</h3>
           <Suspense>
               <!-- v-slot:default 真正要展示的组件-->
               <template v-slot:default>
                   <Child/>
               </template>
               <!-- v-slot:fallback 等待时间中的提示组件-->
               <template v-slot:fallback>
                   <h3>加载中.....</h3>
               </template>
           </Suspense>
       </div>
    </template>
    <script >
       import { defineAsyncComponent } from 'vue'
       const Child = defineAsyncComponent(() => import('./Child.vue'))
       export default {
         components: { Child },
         setup() {
           return {}
         }
       }
    </script>
    
    //Child组件
    <template>
         <div class="child">
           我是异步引入组件---Child--{{num}}
         </div>
    </template>
    <script>
       import { ref } from 'vue'
       export default {
         name: 'Child',
         //setup函数可以用async,但是必须配合suspense组件和defineAsyncComponent方法
         async setup() {
           let num = ref(10)
           //这里是等异步任务3s完成后才渲染该组件,一般是axios调接口完成之后才渲染
           return await new Promise((resolve, reject) => {
             setTimeout(() => {
               resolve({ num })
             }, 3000)
           })
         }
       }
     </script>
    

七、vue3其他改变

7-1. 移除v-on.native修饰符

  • 父组件中绑定自定义事件和原生事件

    //close作为自定义事件,而click作为<my-component>组件的原生事件
    <my-component
         @close="close"
         @click="click"
    />
    
  • 子组件中声明自定义事件

       <script>
         export default {
           //emits:声明了就是自定义事件
           emits: ['close']
         }
       </script>
    

7-2. 移除过滤器(filter)

7-3. 移除keyCode作为v-on的修饰符

八、路由的变化

路由的变化

九、使用less

使用less

十、vuex

vuex