• 【源码解读】VUE2.0和VUE3.0响应式区别?


    数据劫持的目的

    VUE2.0和VUE3.0实现响应式的底层逻辑,是对数据做劫持,为什么要劫持数据呢?是因为,劫持数据后才可以,在更改数据同时对页面进行重新渲染,从而达到响应式。

    VUE3.0响应原理

    VUE3.0使用了ES6 proxy做代理,proxy是用于创建一个对象的代理,可实现基本操作的拦截和自定义。简而言之,proxy可以在数据做操作时,进行拦截,其基础语法是:const p = new Proxy(target,handler) target为要操作的obj,handler是数据的set,get操作方法。
    举个例子:

       let test = reactive({ a: 1, b: 2 });
        function reactive(target) {
          const testProxy = new Proxy(target, {
            get(target, key, receiver) {
              //拦截后可以做很多事情,比如视图渲染
              const res = Reflect.get(target, key, receiver); //等同于res=target[key]
              console.log("响应式获取:" + res);
              return res;
            },
            set(target, key, value, receiver) {
            //拦截后可以做很多事情,比如视图渲染
              const res = Reflect.set(target, key, value, receiver); //等同于设置target[key]=value
              console.log("响应式设置:" + res);
            }
          });
          return testProxy;
        }
    
        test.a;
        test.a = 3;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    运行结果:运行结果
    以上例子是reactive核心思想,其本质也就是对对象进行了代理,并返回代理对象。在get、set方法中使用了Reflect对象,**注意:这是个对象,并不是方法。**官方解释,Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。那为什么在这里使用Reflect对象呢?因为,Reflect与Proxy的get(),set()方法一一对应,一个Proxy的get方法对应一个Reflect.get()方法,如果使用obj.defineProperty方法修改两次同一属性的,就会报错。JS又是单线程的,报错后不会继续执行下面代码,则框架的健壮性就会很差。

        let test = { a: 1, b: 2 };
        Object.defineProperty(test, "c", {
          get() {
            return 7;
          }
        });
        Object.defineProperty(test, "c", {
          get() {
            return 99;
          }
        });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述
    但如果使用Reflect,就不会报错,会将数据反射出去。

        let test = { a: 1, b: 2 };
    
        const t1 = new Proxy(test, {
          get() {
            return 7;
          }
        });
        console.log("t1.c=" + t1.c);
        const t2 = new Proxy(test, {
          get() {
            return 99;
          }
        });
        console.log("t2.c=" + t2.c);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    VUE2.0响应式原理

    vue2.0响应式是通过Object.defineProperty方法,对属性进行劫持。官方文档解释,Object.defineProperty() 静态方法会直接在一个对象上定义一个新属性,或修改其现有属性,并返回此对象。基础语法是:

    Object.defineProperty(obj, prop, descriptor)
    
    • 1

    obj
    要定义属性的对象。

    prop
    一个字符串或 Symbol,指定了要定义或修改的属性键。

    descriptor
    要定义或修改的属性的描述符。

    可以看出,Object.defineProperty是需要对对象里的每个属性,一一进行设置或者监听。在VUE2中,也是使用该核心思想,但此方法只可以对对象进行监听,数组Array监听则采用对数组原型方法重写,进行数据拦截。

    • 对象Object监听
    let test = { a: 1, b: 2 };
        observer(test);
        function observer(obj) {
          let testKeys = Object.keys(test);
          testKeys.map((key) => {
            defineProperty(obj, key, obj[key]);
          });
        }
        function defineProperty(obj, key, val) {
          Object.defineProperty(obj, key, {
            get() {
              console.log("响应式获取:" + val);
              return val;
            },
            set(newval) {
              console.log("响应式设置:" + key + "=" + newval);
              val = newval;
            }
          });
        }
        console.log(test.a);
        //响应式获取:1
        // 1
        test.a = 5;
        //响应式设置:a=5
        console.log(test.a);
         //响应式获取:5
        // 5
    
    • 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

    源码核心思想如上,会建立一个observer监听对象,并在defineProperty中实现对每个属性的数据拦截。
    如果是多层对象嵌套,可通过递归方式,对属性中数据做深入拦截。

    let test = { a: 1, b: { c: 2, d: 3 } };
        observer(test);
        function observer(obj) {
          if (typeof obj !== "object") return;
          let testKeys = Object.keys(obj);
          testKeys.map((key) => {
            defineProperty(obj, key, obj[key]);
          });
        }
        function defineProperty(obj, key, val) {
          if (typeof val === "object" || val !== null) {
            observer(val);
          }
          Object.defineProperty(obj, key, {
            get() {
              console.log("响应式获取:" + val);
              return val;
            },
            set(newval) {
              console.log("响应式设置:" + key + "=" + newval);
              val = newval;
            }
          });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 数组Array监听
      数组的数据劫持,主要是对数组中,会影响数组结构和内容的方法,在原型链上重写。这些方法包括push、pop、shifit等,这是因为数组结构发生变化,需要视图进行同步更新,所以需要对这些方法进行劫持。
     const customMethods = [
          "push",
          "pop",
          "shifit",
          "unshift",
          "splice",
          "sort",
          "reverse"
        ];
        const originArrMethods = Array.prototype, //获取Array上的原型对象,包含所有方法
          arrMethods = Object.create(originArrMethods); //创建一个和原型方法一样的对象
        customMethods.map((m) => {
          //重写方法
          arrMethods[m] = function () {
            const args = Array.prototype.slice.call(arguments), //将入参处理为数组
              rt = originArrMethods[m].apply(this, args); //将this指向改为原始Array
            let newArr;
            switch (m) {
              case "push":
              case "unshift":
                newArr = args;
                break;
              case "splice": //splice(start,end,新值)
                newArr = args.slice(2); //取新值
                break;
              default:
                break;
            }
            return rt;
          };
        });
    
    • 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
  • 相关阅读:
    MyBatis的参数传递
    k8s修改集群IP--重置集群
    fmllr--学习笔记
    IDC服务器商是如何保护服务器的数据安全
    MultiHeadAttention在Tensorflow中的实现原理
    程序员与动漫
    axios 或 fetch 如何实现对发出的请求的终止?
    JSP汽车维修服务管理系统myeclipse开发SqlServer数据库bs框架java编程web网页结构
    【Linux】网络IO(select、poll、epoll)
    安装 hbase(伪分布式)
  • 原文地址:https://blog.csdn.net/csdn_zhuang/article/details/132754388