• Vue3学习笔记:ref函数、reactive函数等常用Composition API、生命周期,Fragment、teleport等新的组件用法记录


    一、快速上手Vue3.0

    1、使用vue-cli

    2、使用vite

    开发环境中,无需打包

    轻量快速热重载

    npm init vite-app vue3_test_vite

    二、常用Composition API(组合式API)

    1、Setup

    组件中所用到的:数据、方法等等,均要配置在setup中。

    在setup中不能访问到vue2配置

    
    
    
    • 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
    • 32
    • 33
    • 34
    • 35

    2、ref函数

    作用:定义一个响应式的数据

    语法: const xxx = ref(initValue)

    • 创建一个引用对象
    • JS中操作数据: xxx.value
    • 模板中读取数据不需要.value

    接收的基本类型数据:响应式依然是靠 Object.defineProperty() 的get 与set完成的

    对象类型的数据(reactive)

    
    
    
    
    • 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
    • 32
    • 33
    • 34
    • 35

    3、reactive函数

    作用:定义一个对象类型的响应式数据

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

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

    
    
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36

    4、Vue3响应式

    实现vue2的响应式原理:

      let person = {
                name:'张三',
                age:18
            }
    // 模拟Vue2中实现响应式
            let p = {}
            Object.defineProperty(p,'name',{
                get(){
                    //有人读取name时调用
                    return person.name
                },
                set(value){
                    //有人修改name时调用
                    console.log('有人修改了name,我去更新页面');
                    person.name = value
                }
            })
            Object.defineProperty(p,'age',{
                get(){
                    //有人读取name时调用
                    return person.age
                },
                set(value){
                    //有人修改name时调用
                    console.log('有人修改了age,我去更新页面');
                    person.age = value
                }
            })
    
    • 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

    实现原理:

    通过Proxy(代理):拦截对象中任意属性变化。包括增删读写

    通过Reflect(反射):对被代理对象的属性进行操作

      let person = {
                name:'张三',
                age:18
            }
    const p = new Proxy(person,{
            get(target,propName){
                console.log(`有人读取了p身上的${propName}属性`);
                return Reflect.get(target,propName)
            },
            set(target,propName,value){
                console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`);
                Reflect.set(target,propName,value)
            },
            deleteProperty(target,propName){
                console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`);
                return Reflect.deleteProperty(target,propName)
            }
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    5、reactive和ref的对比

    • 从定义数据角度对比:

      ​ ref定义基本数据

      ​ reactive定义对象或数组类型

      从原理角度对比:

      ​ ref通过 Object.defineProperty()的get与set来实现响应式。

      ​ reactive通过使用 Proxy来实现响应式(数据劫持)并通过Reflect操作源对象内部数据。

      从使用角度对比:

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

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

    6、Setup的注意点

    setup执行时机:在beforecreate之前执行一次,this是undefined

    Setup的参数:props值为对象,包含组件外传递过来且组件内声明接收了的属性

    context:上下文对象

    ​ attrs:值为对象,包含组件外部传递过来但没有props配置中声明的属性,相当于 this.$attrs

    ​ slots:收到的插槽内容,相当于 this.$slots

    ​ emit:分发自定义事件的函数,相当于 this.$emit

    7、计算属性与监视

    1、computed写法
    import {computed} from 'vue'
    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
    2、watch写法

    监视reactive所定义的一个响应式数据的全部属性,无法正确地获取oldValue,deep失效

    监视的是reactive定义的对象中的某个对象属性,deep配置有效

    import {ref,reactive,watch} from 'vue'
    setup(){
        let sum = ref(0)
        let msg = ref('你好啊')
        let person = reactive({
            name:'张三',
            age:18,
            job:{
                j1:{
                    salary:20
                }
            }
        })
        //情况一:监视ref所定义的一个响应式数据
        watch(sum,(newValue,oldValue)=>{
            console.log('sum变了',newValue,oldValue)
        },{immediate:true})
        //情况二:监视ref所定义的多个响应式数据
        watch([sum,msg],(newValue,oldValue)=>{
            console.log('sum或msg变了',newValue,oldValue)
        },{immediate:true})
    	}
    	//情况三:监视reactive所定义的一个响应式数据的全部属性
    	//此处无法正确地获取oldValue
    	//强制开启深度监视
    	watch(person,(newValue,oldValue)=>{
            console.log('person变化了',newValue,oldValue)
        },{deep:false})//deep配置无效
    	//情况四:监视reactive所定义的一个响应式数据的某个属性
    	watch(()=>person.name,(newValue,oldValue)=>{
            console.log('person的name变化了',newValue,oldValue)
        })
    	//情况五:监视reactive所定义的一个响应式数据中的某些属性
    	watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
            console.log('person的name或age变化了',newValue,oldValue)
        })
    	//特殊情况
    	watch(()=>person.job,(newValue,oldValue)=>{
            console.log('person的job变化了',newValue,oldValue)
        },{deep:true})//此处监视的是reactive定义的对象中的某个对象属性,所以deep配置有效
    	return{
            sum,
            msg,
            person
        }
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    3、watchEffect函数

    watch的套路是:既要指明监视属性,也指明监视回调

    watchEffect:不用指明监视哪个属性,监视回调中用到哪个就监听哪个

    import {ref,reactive,watch,watchEffect} from 'vue'
    setup(){
        let sum = ref(0)
        let msg = ref('你好啊')
        let person = reactive({
            name:'张三',
            age:18,
            job:{
                j1:{
                    salary:20
                }
            }
                })
        watchEffect(()=>{
            const x1 = sum.value
            const x2 = person.job.j1.salary
            console.log('watchEffect所指定的回调执行了')
        })
        	return{
            sum,
            msg,
            person
        }
       
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    8、生命周期

    image-20220902121922938

    Vue3.0中可以继续使用Vue2的生命周期钩子

    beforeDestory改为 beforeUnmount

    destroyed 改为 unmounted

    Composition API形式的生命周期钩子,与Vue2中钩子对应关系如下:

    beforeCreate===> setup()

    created===>setup()

    beforeMount ===> onBeforeMount

    mounted===> onMounted

    beforeUpdate===> onBeforeUpdate

    updated===> onUpdated

    beforeUnmount===> onBeforeUnmount

    unmounted ===> onUnmounted

    通过组合式API的形式去使用生命周期钩子

    
    
    
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    9、自定义hook函数

    • 本质是一个函数,把setup函数中使用的Composition API进行了封装
    • 类似于mixin
    • 复用代码,让setup中的逻辑更清楚

    hooks/usePoint.js

    import {onMounted,reactive,onBeforeUnmount} from 'vue'
    export default function(){
        //实现“打点”的数据
        let point = reactive({
            x:0,
            y:0
        })
        //实现打点的方法
        function savePoint(event){
            point.x = event.pageX
            point.y = event.pageY
            console.log(event.pageX,event.pageY);
        }
        //实现打点的钩子
        onMounted(()=>{
            //函数体
            window.addEventListener('click',savePoint)
        })
        onBeforeUnmount(()=>{
            window.removeEventListener('click',savePoint)
        })
        return point
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    components/DemoOne.vue

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    10、toRef

    作用:创建一个Ref对象,其value值指向另一个对象中的某个属性

    语法 : const name = toRef(person,'name')

    应用:要将响应式对象中的某个属性提供给外部使用时

    扩展: toRefs(person)批量创建ref对象

    
    
    
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36

    三、其他Composition API

    1、shallowReactive与shallowRef

    shallowRecative只考虑对象类型第一层(最外层)的响应式

    shallowRef不去处理对象类型的响应式,只处理基本数据类型响应式

    使用:

    如果一个对象数据结构比较深,但只用外层属性===>shallowRecative

    如果一个对象数据后续不会修改对象中的属性,如要修改则直接替换为新的对象===>shallowRef

    2、readonly与shallowReadonly

    不希望数据被修改时

    readonly:使一个响应式数据变为只读

    shallowReadonly:使一个响应式数据外层只读

    person = readonly(person)
    person = shallowReadonly(person)
    
    • 1
    • 2

    3、toRaw与markRaw

    toRaw

    • 作用:将一个由 reactive生成的响应式对象转为普通对象
    • 使用:读取响应式对象对应的普通对象,对普通对象的所有操作不会引起页面变化
    const p = toRaw(person)
    
    • 1

    markRaw

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

    使用:

    有些值不应该被设为响应式,例如第三方类库axios等

    渲染不可变数据的大列表时,可跳过响应式转换提高性能

    let car = {name:'奔驰',price:40}
    person.car = markRaw(car)
    
    • 1
    • 2

    4、customRef

    创建一个自定义Ref,并对其依赖项跟踪和更新触发进行显式控制

    实现防抖效果:

    
    
    
    
    • 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
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    5、provide与inject

    作用:实现祖孙组件间通信

    使用:父组件有一个 provide来提供数据,后代组件有一个 inject 选项来使用数据

    父组件App.vue

    
    
    
    
    • 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

    后代组件Son.vue

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    6、响应式数据的判断

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

    isReactive():检查一个对象是否为reactive创建的响应式代理

    isReadonly() :检查一个对象是否为readonly创建的只读代理

    isProxy() :检查一个对象是否为代理

    四、新的组件

    1、Fragment

    Vue3组件可以没有根标签,会将多个标签包含在一个Fragment虚拟元素中

    减少层级嵌套

    2、teleport

    将组件html结构移动到指定位置

    
    	

    我是一个弹窗

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3、Suspense

    等待异步组件时渲染一些额外内容,增加用户体验

    异步引入组件

    import {defineAsyncComponent} from 'vue'
    const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
    
    • 1
    • 2

    使用 Suspense包裹组件,配置 defaultfallback

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    五、其他变化

    略写~~~~

    移除 v-on.native修饰符

    父组件中绑定事件

    
    
    
    • 1
    • 2

    子组件中声明自定义事件

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
      
            
        
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    ```

    3、Suspense

    等待异步组件时渲染一些额外内容,增加用户体验

    异步引入组件

    import {defineAsyncComponent} from 'vue'
    const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
    
    • 1
    • 2

    使用 Suspense包裹组件,配置 defaultfallback

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    五、其他变化

    略写~~~~

    移除 v-on.native修饰符

    父组件中绑定事件

    
    
    
    • 1
    • 2

    子组件中声明自定义事件

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    vue3(三)动态路由刷新路由失效问题
    双非一本 大一 就业方向选择
    有含金量的AI证书
    基于 velero 工具迁移 kubesphere 后端存储
    [笔记]JavaScript 实现按钮拖拽效果
    ffmpeg解复用FLV文件
    第五章:方法
    ThreadPoolTaskExecutor不得不说的坑
    快速入门MySQL
    Flutter查漏补缺1
  • 原文地址:https://blog.csdn.net/tianhai12/article/details/126711579