• 【vue3】需要了解哪些【全】


    一.vue3入门

    1. vue3带来了什么

    1. 更快的渲染速度:Vue3使用了Proxy代理对象,可以更快地跟踪数据变化,从而提高渲染速度。

    2. 更小的体积:Vue3的体积比Vue2更小,同时也支持按需加载,减少了页面加载时间。

    3. 更好的TypeScript支持:Vue3对TypeScript的支持更加完善,可以更好地进行类型检查和代码提示。

    4. 更好的组件封装:Vue3引入了Composition API,可以更好地封装组件逻辑,使得组件更加可复用和易维护。

    5. 更好的响应式系统:Vue3的响应式系统进行了重构,可以更好地处理嵌套对象和数组的变化,同时也提供了更多的API来处理响应式数据。

      总之,Vue3带来了更好的性能、更小的体积、更好的TypeScript支持、更好的组件封装和更好的响应式系统,使得开发者可以更加高效地开发Vue应用。

    2. vue3工程的创建

    1. 使用vue-cli创建

    vue-cli官网

    2. 使用vite创建

    vite官网

    vite与webpack的区别

    1. 底层原理:vite通过go语言实现,性能好,打包速度快很多,比webpack快10-100倍
    2. vite采用预构建,会把所有的模块做预编译,打包构建时只更新有修改的模块,webpack项目越大打包速度越慢
    3. vite有缓存,工具模块和包依赖等采用强缓存,直接缓存到浏览器,不会再重新加载。对可能发生变化的模块做协商缓存,只编译有变化的部分

    二、常用Composition API

    vue3中的组合API都是函数,使用时需要先引入(setup不需要引入)

    1. setup(函数和语法糖)

    1. 是vue3的一个新配置项,值是一个函数
    2. 执行时间:在beforeCreate之前,即组件创建之前
    3. 这就意味着在setup函数中 this 还不是组件实例,this 此时是 undefined
    4. 在模版中需要使用的数据和函数,需要在 setup 返回
    5. setup的参数:
      • props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
      • context:上下文对象
        • attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。
        • slots: 收到的插槽内容, 相当于 this.$slots。
        • emit: 分发自定义事件的函数, 相当于 this.$emit。
    6. 组件中所用到的:数据(vue2中的data)、方法(vue2中的methods)、生命周期等,均需要写在setup中,且需要被返回后才可以在模板中使用
    7. etup有两种返回值
      1. 返回一个对象,则对象中的属性、方法,在模板中可以直接使用
      2. 返回一个渲染函数,则可以自定义渲染的内容
    // import {h} from 'vue'
    export default {
    name: 'App',
    setup() {
      // 数据(相当于vue2中的data)
      let name = 'dudu'
      // 方法(相当于vue2中的methods)
      const sayHi = () => {
        console.log(`hi,${name}`)
      }
      // 返回一个对象
      return {
        name,
        sayHi
      }
      // 返回一个渲染函数
      // return () => {
      //   return h('要返回的元素','要返回的内容')
      // }
    },
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. 尽量不要vue2、vue3混用
    • vue3可以向下兼容vue2的data、methods,vue2的data、methods可以通过this获取setup中数据和方法
    • setup里不可以读取data、methods中数据、方法
    • 如果vue2、vue3有重名,setup优先
    1. setup不能是一个async函数,如果使用了async返回的就不是一个对象,而是promise,模板中也就不能使用。但是可以和Suspense、异步组件搭配使用async
    2. setup语法糖可以直接在script标签中写,不需要返回值
    <script setup>
    // 按需引入
    import { ref, reactive, onMounted } from "vue";
    import { ElMessage } from "element-plus";
    import axios from "axios";
    // 定义响应式数据
    const showButton = ref(false);
    const value1 = ref("");
    const inputValue = ref("");
    // 定义函数
    const handleCommand = (command) => {
      ElMessage(`click on item ${command}`);
    };
    const handleFocus = () => {
      showButton.value = true;
    };
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2. ref 和reactive

    • ref
      • 作用:定义基本类型的响应式数据
      • JS操作数据: xxx.value
      • 模板中读取数据不需要 .value
      • 接收的数据(initValue)可以是:基本类型、也可以是对象类型
      • 基本类型的数据:响应式是通过Object.defineProperty()的get与set完成
      • 对象类型的数据:通过vue3的新函数——reactive函数实现(proxy)
    • reactive作用:定义对象类型的响应式数据(基本类型用ref)
      • 接收一个对象或数组,返回一个代理对象(proxy对象)
      • reactive定义的响应式数据是深层次的
      • 内部基于ES6的proxy实现,通过代理对象对源对象内部数据进行操作
    • 区别
      • 定义
        ref :定义基本数据类型
        reactive:定义对象或数组类型的数据
      • 原理
        ref:通过Object.defineProperty()的get和set来实现响应式(数据劫持)
        reactive:通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部数据
      • 使用:
        ref定义的数据:操作数据需要 .value ,读取数据时模板中不需要 .value
        reactive定义的数据:操作和读取都不需要 .value
        一般使用 reactive 较多,可以将基本类型的数据封装在对象里,然后再使用reactive

    3. vue3的响应式原理

    1. Vue2 的响应式原理

    • 实现原理
    • 通过Object.defineProperty()方法对数据进行劫持,实现对数据的监听和拦截。在Vue2中,每个组件实例都有一个对应的Watcher实例,当数据发生变化时,Watcher实例会通知组件重新渲染。-
    • Watcher实例会在初始化时将自己添加到Dep实例的订阅者列表中,通过Dep类实现发布订阅模式,Dep类维护一个订阅者列表,当数据发生变化时,通知所有订阅者更新视图
    • 对象类型:通过Object.defineProperty()对属性读取、修改进行拦截(数据劫持)
    • 数组类型:通过重写更新数组的一系列方法来实现拦截(对数组的变更方法进行了包裹)
    • 存在的问题
      • 新增属性、删除属性,界面不会更新
      • 直接通过下表修改数组,界面不会自动更新

    2.vue3的的响应式原理

    • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
    • 通过Reflect(反射): 对源对象的属性进行操作

    4.computed

    • 与Vue2中computed配置功能一致
    • 使用vuex中的state时,需要用到响应式
    • 给computed传入函数,返回值就是计算属性的值
      给computed传入对象,get获取计算属性的值,set监听计算属性改变。这种写法适用于需要读写计算属性的情况
    setup(){
          ...
      	//计算属性——简写
          let fullName = computed(()=>{
              return person.firstName + '-' + person.lastName
          })
          //计算属性——完整
          let fullName = computed({
          	// 读
              get(){
                  return person.firstName + '-' + person.lastName
              },
              // 写
              set(value){
                  const nameArr = value.split('-')
                  person.firstName = nameArr[0]
                  person.lastName = nameArr[1]
              }
          })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    5.watch

    • 监视单个数据
    // 监视(简单写法)
        /**
         * 第一个参数:要监视的内容
         * 第二个参数:监视的值:newValue、oldValue
         */
      watch(sum,(newVal,oldVal) => {
        console.log('sum值发生了变化')
      })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 监视多个数据(可以写多个watch,也可以写成数组形式)
    watch([xxx,xxxxx],(newVal,oldVal) => {
          console.log('监视多个内容!',newVal,oldVal)
        })
    
    
    • 1
    • 2
    • 3
    • 4
    • 参数问题
      • 第一个参数:要监视的内容
      • 第二个参数:监视的值:newValue、oldValue
      • watch的第三个参数为相关配置
        immediate:是否立即监听
        deep:深度监听
    watch([xxx,xxxxxx],(newVal,oldVal) => {
          console.log('监视多个内容!',newVal,oldVal)
        },{
          immediate:true,
          deep:true
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意有坑!!!

    1. 坑1: 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
    /* 监视reactive定义的响应式数据
      	若watch监视的是reactive定义的响应式数据,则无法正确获得oldValue!!
      	若watch监视的是reactive定义的响应式数据,则强制开启了深度监视 
      */
      watch(person,(newValue,oldValue)=>{
      	console.log('person变化了',newValue,oldValue)
      },{immediate:true,deep:false}) //此处的deep配置不再奏效
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 坑2: 监视reactive定义的响应式数据中某个或某些属性时:deep配置有效
      监视某个或某些属性要写成函数形式,返回需要监视的属性
    //监视reactive定义的响应式数据中的某些属性
      watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
      	console.log('person的job变化了',newValue,oldValue)
      },{immediate:true,deep:true}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • watch监视ref时的value问题
      基本类型数据:监视的是数据对应的实例对象,加上了.value 表示监视ref的值
      对象或数组:需要加上 .value 或者开启深度监视

    6. watchEffect函数

    • watchEffect函数中直接写回调函数,不需要指定监视的内容
    • 回调函数内部使用的数据就是被监视的数据

    watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性

    7.watch 和 computed 的区别

    • 缓存
      computed支持缓存,相依赖的数据发生改变才会重新计算;
      watch不支持缓存,只要监听的数据变化就会触发相应操作
    • 异步
      computed不支持异步,当computed内有异步操作时是无法监听数据变化的;
      watch支持异步操作
    • 数据
      computed属性的属性值是一函数,函数返回值为属性的属性值,computed中每个属性都可以设置set与get方法。
      watch监听的数据必须是data中声明过或父组件传递过来的props中的数据,当数据变化时,触发监听器

    8.watch和watchEffect的区别

    watchwatchEffect 都是 Vue 3 中的响应式 API,它们的主要区别在于:

    1. watch 是一个有返回值的函数,需要手动停止监听,而 watchEffect 是一个自动清理的函数,不需要手动停止监听。
    2. watch 可以监听多个数据源,而 watchEffect 只能监听一个数据源
    3. watch 可以通过第三个参数 options 来配置监听行为,比如是否立即执行回调函数、是否深度监听等,而 watchEffect 没有这些配置选项。
    4. watch具有一定的惰性lazy 第一次页面展示的时候不会执行,只有数据变化的时候才会执行(设置immediate: true时可以变为非惰性,页面首次加载就会执行)。watchEffect立即执行,没有惰性,页面的首次加载就会执行

    9. watchEffect与computed

    • computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。

    • 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。

    • computed若是值没有被使用时不会调用,但是watchEffect始终会调用一次

    10. vue的生命周期

    vue2 的生命周期

    包含创建 、运行、销毁三个阶段

    • beforeCreate():实例在内存中被创建出来,还没有初始化好data和methods属性
    • create():实例已经在内存中创建,已经初始化好data和method,此时还没有开始编译模版。
    • beforeMount():已经完成了模版的编译,还没有挂载到页面中。
    • mounted():将编译好的模版挂在到页面指定的容器中显示。
    • beforeUpdate():状态更新之前执行函数,此时data中的状态值是最新的,但是界面上显示的数据还是旧的,因为还没有开始重新渲染DOM节点。
    • updated():此时data中的状态值和界面上显示的数据都已经完成了更新,界面已经被重新渲染好了!
    • beforeDestroy():实例被销毁之前。
    • destroyed():实例销毁后调用,Vue实例指示的所有东西都会解绑,所有的事件监听器都会被移除,所有的子实例也都会被销毁。组件已经被完全销毁,此时组件中所有的data、methods以及过滤器,指令等,都已经不可用了。

    vue3的生命周期

    • setup():开始创建组件之前,在beforeCreated和created之前执行,创建的是data和method
    • onBeforeMount():组件挂载到节点上之前执行的函数;
    • onMounted():组件挂载完成后执行的函数;
    • onBeforeUpdate():组件更新之前执行的函数;
    • onUpdate():组件更新完成之后执行的函数;
    • onBeforeUnmount():组件卸载之前执行的函数;
    • onUnmounted():组件卸载完成后执行的函数;
    • onActivated():被包含在< keep-alive >中的组件,会多出两个生命周期钩子函数,被激活时执行;
    • onDeactivated():比如从A组件,切换到B组件,A组件消失时执行;
    • onErrorCaptured():当捕获一个来自子孙组件的异常时激活钩子函数。

    vue2和vue3的生命周期对比

    vue2的生命周期ue3的生命周期
    beforeCreatesetup()
    createdsetup()
    beforeMountonBeforeMount(() => {})
    mountedonMounted(() => {})
    beforeMountonBeforeUpdate(() => {})
    updatedonUpdated(() => {})
    beforeDestroyonBeforeUnmount(() => {})
    destroyedonUnmounted(() => {})

    11. toRef 和 toRefs 函数

    toReftoRefs 是 Vue 3 中的两个响应式函数,它们的作用是将一个普通的 JavaScript 对象或者数组转换成响应式对象或数组。

    • toRef 函数将一个普通的 JavaScript 对象的属性转换成一个响应式对象的属性,返回一个 Ref 对象。
    • toRefs 函数将一个普通的 JavaScript 对象的所有属性转换成响应式对象的属性,返回一个包含所有 Ref 对象的对象。
    • toRefs 函数返回的是一个包含所有 Ref 对象的对象,而不是一个响应式对象。如果需要将这个对象传递给子组件,需要使用 toRef 函数将其转换成响应式对象。

    toRef 函数将一个普通的 JavaScript 对象的属性转换成一个响应式对象的属性,返回一个 Ref 对象。Ref 对象是一个包装器,可以通过 .value 属性获取或设置值。当原始对象的属性值发生变化时,Ref 对象的值也会相应地更新。

    import { reactive, toRef } from 'vue'
    
    const state = reactive({
      count: 0
    })
    
    const countRef = toRef(state, 'count')
    
    console.log(countRef.value) // 0
    
    state.count++
    
    console.log(countRef.value) // 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    toRefs 函数将一个普通的 JavaScript 对象的所有属性转换成响应式对象的属性,返回一个包含所有 Ref 对象的对象。这个函数的主要作用是将一个响应式对象解构成普通的 JavaScript 对象,方便在模板中使用。

    import { reactive, toRefs } from 'vue'
    
    const state = reactive({
      count: 0,
      message: 'Hello, world!'
    })
    
    const refs = toRefs(state)
    
    console.log(refs.count.value) // 0
    console.log(refs.message.value) // 'Hello, world!'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    需要注意的是,toRefs 函数返回的是一个包含所有 Ref 对象的对象,而不是一个响应式对象。如果需要将这个对象传递给子组件,需要使用 toRef 函数将其转换成响应式对象。

    import { reactive, toRefs, toRef } from 'vue'
    
    const state = reactive({
      count: 0,
      message: 'Hello, world!'
    })
    
    const refs = toRefs(state)
    
    export default {
      setup() {
        return {
          count: toRef(refs, 'count'),
          message: toRef(refs, 'message')
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    12. shallowReactive 与 shallowRef

    shallowReactiveshallowRef 是 Vue 3 中的两个响应式 API,它们都可以用来创建响应式数据,但是它们的使用场景和行为有所不同。

    shallowReactive 用于创建一个浅层响应式对象,它只会对对象的第一层属性进行响应式处理,而不会对嵌套的对象进行处理。这意味着,如果你修改了嵌套对象的属性,那么这个修改不会触发响应式更新。例如:

    import { shallowReactive } from 'vue'
    
    const state = shallowReactive({
      name: '张三',
      age: 18,
      address: {
        province: '广东',
        city: '深圳'
      }
    })
    
    // 修改嵌套对象的属性,不会触发响应式更新
    state.address.province = '北京'
    
    // 修改第一层属性,会触发响应式更新
    state.name = '李四'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    shallowRef 用于创建一个浅层响应式引用,它可以将任何类型的数据转换为响应式数据。与 shallowReactive 不同的是,shallowRef 不会对对象进行浅层处理,而是直接将整个对象转换为响应式数据。这意味着,如果你修改了嵌套对象的属性,那么这个修改会触发响应式更新。例如:

    import { shallowRef } from 'vue'
    
    const state = shallowRef({
      name: '张三',
      age: 18,
      address: {
        province: '广东',
        city: '深圳'
      }
    })
    
    // 修改嵌套对象的属性,会触发响应式更新
    state.value.address.province = '北京'
    
    // 修改第一层属性,会触发响应式更新
    state.value.name = '李四'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    总的来说,shallowReactiveshallowRef 都是用于创建响应式数据的 API,但是它们的使用场景和行为有所不同。如果你需要对嵌套对象进行响应式处理,那么应该使用 shallowRef;如果你只需要对第一层属性进行响应式处理,那么可以使用 shallowReactive

    13. readonly 与 shallowReadonly

    readonlyshallowReadonly 都是 Vue3 中提供的响应式 API,用于创建只读的响应式对象。它们的区别在于:

    1. readonly 可以递归地将一个对象转换为只读的响应式对象,而 shallowReadonly 只会将对象的第一层属性转换为只读的响应式对象,不会递归转换嵌套的对象。

    2. readonly 返回的对象是完全只读的,无法修改对象的属性值,也无法添加或删除属性。而 shallowReadonly 返回的对象只是第一层属性只读,如果对象的属性是一个对象,那么这个对象的属性仍然可以修改。

    readonly 的使用方法如下:

    import { reactive, readonly } from 'vue'
    
    const state = reactive({
      count: 0,
      obj: {
        name: '张三',
        age: 18
      }
    })
    
    const readonlyState = readonly(state)
    
    console.log(readonlyState.count) // 0
    console.log(readonlyState.obj.name) // 张三
    
    // 尝试修改只读对象的属性值
    readonlyState.count = 1 // 报错,无法修改只读对象的属性值
    readonlyState.obj.name = '李四' // 成功,只读对象的属性值可以修改
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    从上面的例子可以看出,readonly 可以将一个响应式对象转换为只读的响应式对象,可以通过访问只读对象的属性值来获取数据,但是无法修改只读对象的属性值。

    shallowReadonly 的使用方法如下:

    import { reactive, shallowReadonly } from 'vue'
    
    const state = reactive({
      count: 0,
      obj: {
        name: '张三',
        age: 18
      }
    })
    
    const readonlyState = shallowReadonly(state)
    
    console.log(readonlyState.count) // 0
    console.log(readonlyState.obj.name) // 张三
    
    // 尝试修改只读对象的属性值
    readonlyState.count = 1 // 报错,无法修改只读对象的属性值
    readonlyState.obj.name = '李四' // 成功,只读对象的属性值可以修改
    
    // 尝试修改只读对象的嵌套对象的属性值
    readonlyState.obj.age = 20 // 成功,只读对象的嵌套对象的属性值可以修改
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    从上面的例子可以看出,shallowReadonly 可以将一个响应式对象的第一层属性转换为只读的响应式对象,可以通过访问只读对象的属性值来获取数据,但是无法修改只读对象的第一层属性值,而嵌套对象的属性值可以修改。

    14. toRaw 与 markRaw

    toRawmarkRaw 是 Vue.js 3.x 中的两个 API,用于处理响应式数据。

    • toRaw 方法用于获取一个响应式对象的原始数据,即非响应式的数据
    • markRaw 方法用于标记一个对象为“非响应式的”,即使这个对象被包含在响应式对象中,也不会被转换为响应式数据
    • markRaw 方法只能标记对象本身为非响应式的,而不能标记对象的属性为非响应式的。如果需要标记对象的属性为非响应式的,可以使用 markRaw 方法嵌套对象。

    toRaw使用:

    在下面的例子中,toRaw 方法将响应式对象 state 转换为了原始数据 rawState,并将其输出到控制台。

    import { reactive, toRaw } from 'vue'
    
    const state = reactive({
      count: 0
    })
    
    const rawState = toRaw(state)
    
    console.log(rawState) // { count: 0 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    markRaw使用:
    在下面的例子中,markRaw 方法将对象 { name: 'John', age: 30 } 标记为非响应式的,并将其作为 state 对象的一个属性。即使 state 对象是响应式的,data 属性也不会被转换为响应式数据。因此,当我们修改 data 属性的值时,不会触发响应式更新。

    import { reactive, markRaw } from 'vue'
    
    const state = reactive({
      count: 0,
      data: markRaw({
        name: 'John',
        age: 30
      })
    })
    
    console.log(state.data.name) // 'John'
    
    state.data.name = 'Mike'
    
    console.log(state.data.name) // 'Mike'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在下面的例子中,我们使用 markRaw 方法将对象 { name: 'John', age: 30 } 标记为非响应式的,并将其作为 state 对象的一个属性 data.info。这样,即使 state 对象是响应式的,data.info 属性也不会被转换为响应式数据。因此,当我们修改 data.info 属性的值时,不会触发响应式更新。

    import { reactive, markRaw } from 'vue'
    
    const state = reactive({
      count: 0,
      data: {
        info: markRaw({
          name: 'John',
          age: 30
        })
      }
    })
    
    console.log(state.data.info.name) // 'John'
    
    state.data.info.name = 'Mike'
    
    console.log(state.data.info.name) // 'Mike'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    15.customRef 自定义ref使用

    在 Vue 3 中,customRef 是一个新的 API,它允许我们创建一个自定义的 ref。

    使用 customRef,我们可以自定义 ref 的读取和写入行为,从而实现更加灵活的数据绑定。

    下面是一个使用 customRef 的示例:

    import { customRef } from 'vue';
    
    function useCustomRef(initialValue) {
      let value = initialValue;
    
      return customRef((track, trigger) => ({
        get() {
          track();
          return value;
        },
        set(newValue) {
          value = newValue;
          trigger();
        }
      }));
    }
    
    export default {
      setup() {
        const customRef = useCustomRef('initial value');
    
        return {
          customRef
        };
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在上面的示例中,我们定义了一个 useCustomRef 函数,它接受一个初始值,并返回一个自定义的 ref。

    customRef 的工厂函数中,我们定义了 getset 方法,它们分别对应 ref 的读取和写入操作。在 get 方法中,我们使用 track 函数来追踪依赖,以便在数据变化时更新组件。在 set 方法中,我们使用 trigger 函数来触发更新。

    最后,在组件中使用 useCustomRef 函数创建一个自定义的 ref,并将其返回。

    使用自定义的 ref,我们可以实现更加灵活的数据绑定,例如在 set 方法中添加一些额外的逻辑,或者在 get 方法中返回一个经过计算的值。

    16.provide 与 inject

    在 Vue 3 中,provideinject 仍然可以用来实现父组件向子组件传递数据,但是与 Vue 2 中的用法略有不同。

    provideinject 都是在组件实例上定义的属性,provide 定义在父组件中,inject 定义在子组件中。provide 可以是一个对象或者一个函数,inject 可以是一个数组或者一个对象。

    下面是一个使用 provideinject 实现父组件向子组件传递数据的例子:

    <template>
      <div>
        <child-component></child-component>
      </div>
    </template>
    
    <script>
    import { provide, inject } from 'vue'
    
    const MyProvideComponent = {
      setup() {
        const data = 'Hello, World!'
        provide('myData', data)
      }
    }
    
    const ChildComponent = {
      setup() {
        const data = inject('myData')
        return { data }
      },
      template: '
    {{ data }}
    '
    } export default { components: { ChildComponent }, extends: MyProvideComponent } </script>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    在上面的例子中,我们在父组件中定义了一个 provide,将数据 Hello, World!myData 的键名提供给子组件使用。在子组件中,我们使用 inject 获取父组件提供的数据,并将其绑定到模板中。

    需要注意的是,provideinject 并不是响应式的,如果需要在子组件中响应式地使用父组件提供的数据,可以使用 reactiveref 等响应式 API 进行包装。

    17.响应式数据的判断

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

    18.vue3中的插槽及其使用

    在Vue3中,插槽可以分为以下两种类型:

    1. 普通插槽(Slot)

    普通插槽是Vue2中的插槽的升级版,使用方式与Vue2中的插槽类似。在父组件中使用标签,子组件中使用标签的name属性来定义插槽,父组件中使用子组件时,可以在子组件标签中使用