Vue3.0 —— 常用的 Compoition API 之 reactive 函数 以及 响应式原理 Proxy/Reflect

1. reactive 函数

   - 作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用 ref 函数)

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

   - reactive 定义的响应式数据是 "深层次的"

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

<template>
  <h1>基本信息</h1>
  <h2>姓名:{{person.name}}</h2>
  <h2>年龄:{{person.age}}</h2>
  <h2>工作类型:{{person.job.type}}</h2>
  <h2>薪水:{{person.job.salary}}</h2>
  <h2>测试的数据c:{{person.job.a.b.c}}</h2>
  <h2>爱好:{{person.hobby}}</h2>
  <button @click="changeInfo">改变信息</button>
</template>
<script>
import { reactive } from 'vue'
export default {
  name: 'App',
  // 简单测试 setup,暂时不做响应式
  setup() {
    // 数据
    let person = reactive({
      name: '张三',
      age: 18,
      job: {
        type: '攻城狮',
        salary: '30k',
        a: {
          b: {
            c: 666
          }
        }
      },
      hobby: ["抽烟", "喝酒", "烫头"]
    })

    // 方法
    function changeInfo() {
      person.name = '李四'
      person.age = 88
      // console.log(job)
      person.job.type = '鼓励师'
      person.job.salary = '50k'
      person.job.a.b.c = 999
      person.hobby[0] = "学习"
    }
    // 返回一个对象(常用)
    return {
      person,
      changeInfo,
    }
  }
}
</script>

 

 

 

2. Vue 3.0 中的响应式原理

   vue 2.x 的响应式

     - 实现原理:

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

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

        Object.defineProperty(data, 'count', {

          get() {},

          set() {},

        });

    - 存在问题:

      - 新增属性/删除属性,界面不会更新。

      - 直接通过下标改变数组,界面不会自动更新。

  vue 3.0 的响应式

    - 实现原理:

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

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

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

         - Proxy:https://developer.mozila.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

         - Reflect:https://developer.mozila.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

 

 

 

        const p = new Proxy(person, {
            // 拦截读取属性值
            get(target, prop) {
                return Reflect.get(target, prop);
            },
            // 拦截设置属性值或添加新属性
            set(target, prop, value) {
                Reflect.set(target, prop, value)
            },
            // 拦截删除属性
            deleteProperty(target, prop) {
                return Reflect.delete(target, prop);    
            }
        })
     proxy.name = 'tom'

 

   

  

<script>
        const person = {
            name: '张三',
            age: 18
        };
        // 模拟 vue2 的响应式
        //#region
        // let p = {}
        // Object.defineProperty(p, 'name', {
        //     configurable: true,
        //     get() {
        //         console.log(`有人读取person的name属性`)
        //         return person.name
        //     },
        //     set(value) {
        //         console.log(`有人修改person的name属性`,value)
        //         person.name = value
        //     }
        // });
        // Object.defineProperty(p, 'age', {
        //     configurable: true,
        //     get() {
        //         console.log(`有人读取person的age属性`)
        //         return person.age
        //     },
        //     set(value) {
        //         console.log(`有人修改person的age属性`,value)
        //         person.age = value
        //     }
        // });
        //#endregion</script>

 

 - reactive 对比 ref

   - 从定义数据角度对比:

     - ref 用来定义:基本类型数据

     - reactive 用来定义:对象或数组类型数据

     - 备注:ref 也可以用来定义 对象或数组类型数据,它内部会自动通过 reactive 转为 代理对象

   - 从原理角度对比:

     - ref 通过 Object.defineProperty()getset 来实现响应式(数据劫持)

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

   - 从使用角度对比:

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

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

 

posted @ 2022-01-26 23:34  我就尝一口  阅读(335)  评论(0编辑  收藏  举报