Vue3.0中的响应式 | 青训营笔记

87 阅读3分钟

这是我参与「第四届青训营」笔记创作活动的第15天。

前言

本文初步从vue响应式介绍,然后说明一下在什么情况下使用 ref、reactive 函数。

Vue3.0中的响应式原理

回顾vue2.x的响应式

  • 实现原理:

    • 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。

    • 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。

       Object.defineProperty(data, 'count', {
           get () {}, 
           set () {}
       })
      
  • 存在问题:

    • 新增属性、删除属性, 界面不会更新。
    • 直接通过下标修改数组, 界面不会自动更新。

Vue3.0的响应式

  • 实现原理:

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

    • 通过Reflect(反射): 对源对象的属性进行操作。

    • MDN文档中描述的Proxy与Reflect:

      • Proxy:developer.mozilla.org/zh-CN/docs/…

      • Reflect:developer.mozilla.org/zh-CN/docs/…

         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)
             }
         })
         ​
         proxy.name = 'tom'   
        

Reflect vs Object

Reflect 可以返回一个bool值来判断操作是否成功,在封装时减少 try...catch 使用

 let obj = {a:1,b:2}
 //通过Object.defineProperty去操作
 try {
     Object.defineProperty(obj,'c',{
         get(){
             return 3
         }
     })
     Object.defineProperty(obj,'c',{
         get(){
             return 4
         }
     })
 } catch (error) {
     console.log(error)
 } 
 ​
 //通过Reflect.defineProperty去操作
 const x1 = Reflect.defineProperty(obj,'c',{
     get(){
         return 3
     }
 })
 console.log(x1)
 ​
 const x2 = Reflect.defineProperty(obj,'c',{
     get(){
         return 4
     }
 }) 
 if(x2){
     console.log('某某某操作成功了!')
 }else{
     console.log('某某某操作失败了!')
 } 
 ​
 //----------------------------------
 ​
 // Reflect 静态方法
 ​
 Reflect.apply(target, thisArgument, argumentsList)
 //对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。
 ​
 Reflect.construct(target, argumentsList[, newTarget])
 //对构造函数进行 new 操作,相当于执行 new target(...args)。
 ​
 Reflect.defineProperty(target, propertyKey, attributes)
 //和 Object.defineProperty() 类似。如果设置成功就会返回 true
 ​
 Reflect.deleteProperty(target, propertyKey)
 //作为函数的delete操作符,相当于执行 delete target[name]。
 ​
 Reflect.get(target, propertyKey[, receiver])
 //获取对象身上某个属性的值,类似于 target[name]。
 ​
 Reflect.getOwnPropertyDescriptor(target, propertyKey)
 //类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined。
 ​
 Reflect.getPrototypeOf(target)
 //类似于 Object.getPrototypeOf()。
 ​
 Reflect.has(target, propertyKey)
 //判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
 ​
 Reflect.isExtensible(target)
 //类似于 Object.isExtensible().
 ​
 Reflect.ownKeys(target)
 //返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响).
 ​
 Reflect.preventExtensions(target)
 //类似于 Object.preventExtensions()。返回一个Boolean。
 ​
 Reflect.set(target, propertyKey, value[, receiver])
 //将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
 ​
 Reflect.setPrototypeOf(target, prototype)
 //设置对象原型的函数。返回一个 Boolean, 如果更新成功,则返回true。

使用ref函数

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

  • 语法: const xxx = ref(initValue)

    • 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
    • JS中操作数据: xxx.value
    • 模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
  • 备注:

    • 接收的数据可以是:基本类型、也可以是对象类型。
    • 基本类型的数据:响应式依然是靠Object.defineProperty()getset完成的。
    • 对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive函数。

使用reactive函数

  • 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
  • 语法:const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的响应式数据是*“深层次的”*。
  • 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。

【reactive的参数必须是一个对象,包括json数据和数组都可以,否则不具有响应式】

 <template>
     <h1>一个人的信息</h1>
     <h2>姓名:{{person.name}}</h2>
     <h2>年龄:{{person.age}}</h2>
     <h3>工作种类:{{person.job.type}}</h3>
     <h3>工作薪水:{{person.job.salary}}</h3>
     <h3>爱好:{{person.hobby}}</h3>
     <h3>测试的数据c:{{person.job.a.b.c}}</h3>
     <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',
                     a:{
                         b:{
                             c:666
                         }
                     }
                 },
                 hobby:['抽烟','喝酒','烫头']
             })
 ​
             //方法
             function changeInfo(){
                 person.name = '李四'
                 person.age = 48
                 person.job.type = 'UI设计师'
                 person.job.salary = '60K'
                 person.job.a.b.c = 999
                 person.hobby[0] = '学习'
             }
 ​
             //返回一个对象(常用)
             return {
                 person,
                 changeInfo
             }
         }
     }
 </script>

总结

本文初步从vue响应式介绍,然后说明一下在什么情况下使用 ref、reactive 函数。