一、Vue3的变化

  1. main.js的内容

    import { createApp } from 'vue'
    import App from './App.vue'
    import './index.css'
    
    const app = createApp(App)
    app.mount('#app')
  2. template的变化:可以不使用div包裹内容

    <template>
      <h1>{{ msg }}</h1>
      <h2>HelloWorld</h2>
    </template>

二、常用composition API

2.1、setup

vue3可以使用vue2的 datamethod 的方法,但更推荐使用setup

export default {
    name:'App',
    setup(){
        // 定义数据
        let name = 'zhang'
        let age = 18
        // 定义函数
        function showAge(){
            console.log(age)    // 直接使用数据,无需使用this指向
        }
        
        // 返回一个对象
        return {
            name,
            age,
            showAge
        }
    }
}
  • 尽量不要与vue2配置混用
  • vue2的配置可以访问到setup中的属性、方法,反过来则不行,setup的优先级更高

2.2、setup注意点

  • setup执行的时机:在beforeCreate之前执行一次,此时this是undefined
  • setup的参数:

    • props:值为对象,包含外部传递过来的组件以及组件内部声明接收了的属性
    • context:上下文对象
    • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs
    • slots: 插槽, 相当于 this.$slots
    • emit: 分发自定义事件的函数, 相当于 this.$emit

2.3、实现响应式

  1. ref函数(了解)

    vue3数据的响应式需要自己进行添加

    import {ref} from 'vue'
    export default {
        name:'App',
        setup(){
            let name = ref('zhang')
            let age = ref(18)
    
            function changeName(){
                name.value = 'Lishi'
            }
            
            // 返回一个对象
            return {
                name,
                age,
                showAge
            }
        }
    }
    • ref将数据包装成对象,在对其修改值时需要通过 xxx.value 来修改,模板中使用无需.value
    • 对于基本类型数据,响应式依然是靠Object.defineProperty()来完成的
  2. reactive函数(常用)

    • reactive不能处理基本类型的数据,定义的响应式数据是“深层次的”
    • 基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作

      对于数组类型,可以使用索引进行修改和读取值

      setup(){
          let num = reactive(['1','2','3'])
      
          function changeNum(){
              num[0] = '0'
          }
      
          return {
              num,
              changeNum
          }
      }
  3. ref对比reactive

    • 从定义数据角度对比

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

      • ref通过类中的的gettersetter来实现响应式(数据劫持)
      • reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据
    • 从使用角度对比

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

2.4、计算属性

  • 计算属性的使用要 按需引入

    import { computed } from 'vue'
  • computed的使用

    let fullname = computed(function() {
        ......
        return
    })
    
    //完整写法
    let fullname = computed(function() {
        get(){
            return
        },
        set(value){
            ......
        }
    })

2.5、监视属性

  1. 监视ref的属性

    • immediate:true 刷新时调用一下handler语句
    • deep:true 开启深度监视
    // 监视单个属性或对象
    watch( sum , (newValue,oldValue) => {
        console.log('sum改变了',newValue,oldValue)
    },{immediate:true}
          
    // 监视多个属性或对象
    watch( [sum,msg] , (newValue,oldValue)=>{
        console.log('sum或msg变化了',newValue,oldValue)
    }) 
  2. 监视reactive的属性

    • 监视reactive定义的响应式数据时,无法正确获得oldValue且强制为深度监视
    • 若监视的是reactive定义的对象中的某个属性,此时deep配置有效,否则deep配置无效
    // 监视单个属性或对象整体
    watch( person , (newValue,oldValue) => {
        console.log('person变化了',newValue,oldValue)
    },{immediate:true,deep:false}) //deep配置无效
    
    // 监视对象内的某个属性
    watch( ()=>person.name ,(newValue,oldValue)=>{
        console.log('name变化了',newValue,oldValue)
    }
    
    // 监视对象内的一堆属性
    watch( [()=>person.name , ()=>person.age],(newValue,oldValue)=>{
        console.log('name或age变化了',newValue,oldValue)
    }
  3. watchEffect函数

    • watch:既要监视属性,也要监视属性的回调
    • watchEffect:无需指明监视哪个属性,监视的回调中用到哪些属性就监视谁

      watchEffect( ()=>{
          const a = person.name
          const b = sum.value
          console.log('回调执行了')
      })    // 此时监视person.name 和 sum.value

2.6、生命周期

vue3生命周期
vue3生命周期

  • vue3与vue2对比,有两个被更名:

    • beforeDestroy改名为 beforeUnmount
    • destroyed改名为 unmounted
  • 组合式API里的钩子会比配置项的钩子先执行

2.7、自定义hook

  • 创建一个hooks文件夹,里面创建文件自定义函数名.js
  • 本质上是封装,类似vue2的mixin
  • 使用:

    //---------useXXX.js--------------//
    import { ...... } from "vue";
    export default function() {
      //数据
      ......
      //方法
      ......
      //生命周期钩子
      ......
      return xxx;
    }
    
    //------组件内使用--------//
    import useXXX from '../hooks/useXXX'

2.8、toRef与toRefs

  • 创建一个 ref 对象,其value值指向另一个对象中的某个属性(类似与指针指向地址的值,并为ref对象的形式)
  • 用法:

    setup(){
        let num = {
            a:1,
            b:2,
            sum:{
                c:10
            }
        }
    
        return {
            a:toRef(num , 'a'), // 单独暴露出a,此时在模板里面可以直接用a而不是num.a
            c:toRef(num.sum.c , 'c'),// 单独暴露出c,此时在模板里面可以直接用c而不是num.sum.c
            ...toRefs(num), //此时深度暴露num内所有数据,在模板里面可以直接用a、b,但是对于c,需要使用sum.c
        }
    }

三、其他API

3.1、shallowReactiveshallowRef

  • 用于处理浅层次的响应式
  • shallowReactive只处理对象最外层属性的响应式,shallowRef只处理基本数据类型的响应式

3.2、readonlyshallowReadonly

  • readonly: 让一个响应式的数据深层次的变为只读
  • shallowReadonly:让一个响应式数据浅层次的变为只读

    setup(){
        let num = {
            a:1,
            b:2,
            sum:{
                c:10
            }
        
        num = readonly(num) // 此时num内部所有属性均为只读
        num = shallowReadonly(num) // 此时num内部除了c,其他均为只读
            
        return{
            ...toRefs(num)
        }
    }

3.3、toRawmarkRaw

  • toRaw

    • 作用:将一个由reactive生成的响应式对象转为普通对象(去响应式)
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,比如还原json响应式的原数据
  • markRaw

    • 作用:标记一个对象,使其永远不会再成为响应式对象
    • 应用场景:

      1. 有些值不应被设置为响应式的,例如复杂的第三方类库等
      2. 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能

3.4、customRef

  • 作用:创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制
  • 用例:
function useDebouncedRef(value, delay = 200) {
  let timeout
  
  return customRef((track, trigger) => {
    return {
      // 获取数据怎么做
      get() {
        track(); // 通知Vue追踪value的变化
        return value
      },
      // 如何交出数据---设置延时再显示数据
      set(newValue) {
        clearTimeout(timeout)
        timeout = setTimeout(() => {
          value = newValue
          trigger() // 通知Vue去重新解析模板
        }, delay)
      }
    }
  })
}

// 使用
let num = useDebouncedRef(18)

3.5、provideinject

  • 组件间通信,实现祖组件与后代组件间通信
  • 用例:

    祖组件

    import { reactive , provide } from 'vue'
    setup(){
        ......
        let num2 = reactive({a:1,b:2})
        provide('num2',num2) // 给后代组件传递数据
    }

    后代组件

    import { reactive , inject } from 'vue'
    setup(props,context){
        const num2 = inject('num2') // 获取祖组件的数据
        return {num2}
    }

3.6、响应式数据的判断

  • isRef: 检查一个值是否为一个 ref 对象
  • isReactive: 检查一个对象是否是由 reactive 创建的响应式代理
  • isReadonly: 检查一个对象是否是由 readonly 创建的只读代理
  • isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理

四、新组件

4.1、Teleport

  • 作用:能够将组件html结构移动到指定位置
  • 用例:

    <teleport to="移动位置"> //to后直接使用html元素或元素选择器
        <div id="box1">
            <h3>测试文字--box1</h3>
        </div>
    </teleport>

4.2、Suspense

  • 等待异步组件时渲染一些额外内容,先加载完成的先出来
  • 用例:

    // 引入
    import {defineAsyncComponent} from 'vue'
    const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
    <template>
        <div class="app">
            <h3>App</h3>
            <Suspense>
                // default:就是组件要显示的内容
                <template v-slot:default>
                    <Demo />
                </template>
                // fallback:组件没加载出来时显示的内容
                <template v-slot:fallback>
                    <h3>正在加载.....</h3>
                </template>
                
            </Suspense>
        </div>
    </template>