vue2与vue3响应式原理

Posted cjh_code

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue2与vue3响应式原理相关的知识,希望对你有一定的参考价值。

        

目录

1、$set 、$delete

2、静默刷新

3、proxy

4、Reflect 


        vue2的响应式原理是通过object.defineProperty来劫持各个属性的setter和getter在数据发生变化的时候发布消息给订阅者,触发响应的监听回调。这里不再对vue2响应原理做过多解释,可以查看往期博客


        在vue2中通过object.defineProperty实现响应式原理时新增属性、删除属性界面不会更新;直接通过下标修改数组,界面也不会自动更新(vue2也做了相对解决$set,$delete);下面来具体介绍$set、$delete静默刷新

1、$set 、$delete

语法:

  1. vm.$set("被设置的对象" , "属性/索引" , "值")
  2. vm.$delete("被设置的对象" , "属性/索引")

下面看看直接添加、删除对象属性(代码仅供测试,可忽略

<template>
  <div>
    <p>姓名:obj.name</p>
    <p>年龄:obj.age</p>
    <p>性别:obj.sex</p>
    <button @click="addSex">添加性别sex</button>
    <button @click="delAge">删除年龄age</button>
  </div>
</template>
<script>
export default 
  name: "",
  data() 
    return 
      obj:
        name:'zs',
        age:20
      
    ;
  ,
  created() ,
  computed: ,
  methods: 
    addSex()
      this.obj.sex = '男'
      console.log("添加性别sex",this.obj);
    ,
    delAge()
      delete this.obj.age
      console.log("删除年龄age",this.obj);
    
  ,
;
</script>
<style lang="less" scoped></style>

 可发现对象的sex属性被添加了,age属性被删除了,但是数据未被响应,界面没有更新

 

 可以看出数据的变化会影响到界面的更新

2、静默刷新

    addSex() 
      this.flag = false;
      this.obj.sex = "男"; //不是响应式
      //this.$set(this.obj, "sex", "男"); //是响应式
      console.log("添加性别sex", this.obj);
      this.flag = true;
    ,
    delAge() 
      this.flag = false;
      delete this.obj.age; //不是响应式
      //this.$delete(this.obj, "age");  //是响应式
      console.log("删除年龄age", this.obj);
      this.flag = true;
    ,

 

 

         可以看出静默刷新利用v-if重新加载节点实现响应刷新页面数据,这样面对大量添加的属性和未知属性也是比较方便的

3、proxy

语法:const p = new proxy("新对象" , "函数对象")

下面看看vue3的新增和删除属性会不会实现界面刷新

<template>
  <div>
    <p>姓名: obj.name </p>
    <p>年龄: obj.age </p>
    <p>性别: obj.sex </p>
    <button @click="addSex">添加性别sex</button>
    <button @click="delAge">删除年龄age</button>
  </div>
</template>

<script>
import  reactive  from "vue";
export default 
  name: "HelloWorld",
  setup() 
    let obj = reactive(
      name: "zs",
      age: 20,
    );
    function addSex() 
      obj.sex = "男";
      console.log("添加性别sex", this.obj);
    
    function delAge() 
      delete obj.age;
      console.log("删除年龄age", this.obj);
    
    return 
      obj,
      addSex,
      delAge,
    ;
  ,
;
</script>

<style scoped></style>

 可以看出vue3中对象属性的增加和删除,界面会随之更新,这是因为vue2的响应式为object.defineProperty而vue3是proxy搭配Reflect

   //因为proxy就两个参数,所以怎么判断是哪个属性呢
    const p = new Proxy(obj, 
      get(target, prop)     //target:对象    prop:属性
        console.log("get:", target, prop);
        return prop;
      ,
      set(target, prop, value)     //target:对象    prop:属性    value:新的值
        console.log("set:", target, prop, value);
        return true;
      ,
      deleteProperty(target, prop)     //删除
        console.log("delete:", target, prop);
        return delete target[prop];
      ,
    );

接下来将代码进行优化

<template>
  <div>
    <p>姓名: obj.name </p>
    <p>年龄: obj.age </p>
    <p>性别: obj.sex </p>
    <button @click="putAge">修改年龄</button>
    <button @click="getName">读取姓名</button>
    <button @click="delSex">删除性别</button>
    <button @click="addClass">增加班级</button>
  </div>
</template>

<script>
import  reactive  from "vue";
export default 
  name: "HelloWorld",
  setup() 
    let obj = reactive(
      name: "zs",
      age: 20,
      sex: "男",
    );
    const p = new Proxy(obj, 
      get(target, prop) 
        console.log("get:", target, prop);
        return prop;
      ,
      set(target, prop, value) 
        target[prop] = value;
        console.log("set:", target, prop, value);
        console.log("有人修改了", prop);
        return true;
      ,
      deleteProperty(target, prop) 
        console.log("delete:", target, prop);
        console.log("有人删除了", prop);
        return delete target[prop];
      ,
    );
    function putAge() 
      p.age = 18;
    
    function getName() 
      console.log("有人读取了", p.name);
    
    function delSex() 
      delete p.sex;
    
    function addClass() 
      p.class = "www";    //增加对象属性调用的是set
    
    return 
      obj,
      putAge,
      getName,
      delSex,
      addClass,
    ;
  ,
;
</script>

<style scoped></style>

下面做一下总结:

  • 获取属性调用proxy中的get函数
  • 修改新增调用proxy中的set函数
  • 删除调用的 proxy中的deleteProperty函数

而vue3中proxy是和Reflect搭配着使用的,下面来简单介绍下Reflect

4、Reflect 

Reflect直译为映射,官方解释它是一个内置对象,提供拦截js操作的方法,这些方法与proxy handlers的方法相同,Reflect不是一个函数对象,因此它是不可构造的,它的所有属性和方法都是静态的(和Math一样),下面将代码进行改进一下

<template>
  <div>
    <p>姓名: obj.name </p>
    <p>年龄: obj.age </p>
    <p>性别: obj.sex </p>
    <p>班级: obj.class </p>
    <button @click="putAge">修改年龄</button>
    <button @click="getName">读取姓名</button>
    <button @click="delSex">删除性别</button>
    <button @click="addClass">增加班级</button>
  </div>
</template>

<script>
import  reactive  from "vue";
export default 
  name: "HelloWorld",
  setup() 
    let obj = reactive(
      name: "zs",
      age: 20,
      sex: "男",
    );
    const p = new Proxy(obj, 
      get(target, prop) 
        console.log("get:", target, prop);
        return Reflect.get(target, prop);
      ,
      set(target, prop, value) 
        target[prop] = value;
        console.log("set:", target, prop, value);
        console.log("有人修改了", prop);
        return Reflect.set(target, prop, value);
      ,
      deleteProperty(target, prop) 
        console.log("delete:", target, prop);
        console.log("有人删除了", prop);
        return Reflect.deleteProperty(target, prop);
      ,
    );
    function putAge() 
      p.age = 18;
    
    function getName() 
      console.log("有人读取了", p.name);
    
    function delSex() 
      delete p.sex;
    
    function addClass() 
      p.class = "www";
    
    return 
      obj,
      putAge,
      getName,
      delSex,
      addClass,
    ;
  ,
;
</script>

<style scoped></style>

 那Reflect有什么特点和好处呢?

  • 将object对象的一些明显属于语言内部的方法(比如object.defineProperty),放到Reflect对象上,现阶段,某些方法同时在object和Reflect对象上部署,未来的新方法只部署在Reflect对象上,也就是说Reflect可以拿到内部方法
  • 修改object方法的返回结果,比如object.defineProperty在无法定义属性时会抛出错误,可用try...catch...来捕获错误消息,而Reflect直接会返回false
  • 让object操作都变成函数行为,某些object操作是命令式的,Reflect会将其变成函数式
  • Reflect方法跟Proxy对象的方法是对应的,Proxy的方法在Reflect都能找到,尽管修改了Proxy默认行为,Reflect也是能够获取默认行为的

以上总结参考程序思维

总结:

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

以上是关于vue2与vue3响应式原理的主要内容,如果未能解决你的问题,请参考以下文章

简单对比vue2.x与vue3.x响应式及新功能

Vue2与Vue3的响应式原理-区别讲解

vue2的响应式原理学“废”了吗?继续观摩vue3响应式原理Proxy

Vue2和Vue3的响应式原理

Vue2和Vue3的响应式原理

Vue3和Vue2响应式的区别