zoukankan      html  css  js  c++  java
  • vue2.0和vue3.0的响应式原理

    前言

    vue 2.0跟3.0的区别原理,

    • 结构: 2.0用Flow ,3.0用 TypeScript。
    • 性能: 3.0优化了Virtual Dom的算法。
    • 响应式原理:2.0用 Object.defineProperty,3.0用Proxy

    Vue2.0和Vue3.0实现原理

    • Vue 2.0

      Vue2.0实现MVVM(双向数据绑定)的原理是通过 Object.defineProperty 来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

       

    • Vue 3.0 实现响应式基于ES6: Proxy

    Vue2.0和Vue3.0的差异如下:

    Vue2.0

    • 基于Object.defineProperty,不具备监听数组的能力,需要重新定义数组的原型来达到响应式。
    • Object.defineProperty 无法检测到对象属性的添加和删除 。
    • 由于Vue会在初始化实例时对属性执行getter/setter转化,所有属性必须在data对象上存在才能让Vue将它转换为响应式。
    • 深度监听需要一次性递归,对性能影响比较大。

    Vue3.0

    • 基于ProxyReflect,可以原生监听数组,可以监听对象属性的添加和删除。
    • 不需要一次性遍历data的属性,可以显著提高性能。
    • Proxy是ES6新增的属性。

    Vue2.x实现响应式

    下面是基于Object.defineProperty ,一步步实现简单版Vue2.0。

    1. 由于Object.defineProperty 无法监听数组,所以数组类型实现响应式,需要处理。 判断如果是数组类型,就重写数组的原型方法('push','pop','shift',unshift)

       // 重新定义数组原型,Object.defineProperty不具备监听数组的方法
       const oldArrayProperty = Array.prototype;
           const arrProto = Object.create(oldArrayProperty);
           ["push","pop","shift","unshift","splice"].forEach(
               methodName => 
               (arrProto[methodName] = function() {
                   updateView();
                   oldArrayProperty[methodName].call(this, ...arguments);
               })
           )
    2. 将传入的data属性进行深度监听,判断是对象还是数组。

       function observer(target){
           if(typeof target !== 'object' || target === null){
               return target
           }
       
           // 如果是数组类型,重写数组原型的方法("push","pop","shift","unshift","splice")
           if(Array.isArray(target)){
               target.__proto__ == arrProto;
           }
       
           // 如果是对象,遍历对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter
           for(let key in target){
               defineReactive(target,key,target[key])
           }
       }
    3. 核心API Object.defineProperty,将传入属性转为 getter/setter

      function defineReactive(target, key, value){
          // 如果对象有更多的层级,再次调用observer监听方法,实现深层次的监听。
          observer(value);
      
          Object.defineProperty(target, key, {
              get(){
                  return value;
              },
              set(newValue){
                  // 设置值的时候也需要深度监听
                  observer(value);
      
                  if(newValue !== value){
                      value = newValue;
      
                      // 数据驱动视图,如果数据改变,就调用视图更新的方法。对应到Vue中是执行VDOM
                      updateView();
                  }
              }
          })
      }
       
    4. 数据更新会触发视图更新,这是MVVM的绑定原理,这就会涉及到Vue的 template 编译为 render 函数,在执行 Virtual Dom, Diff算法, Vnode等 这些东西了。

       function updateView(){
           console.log('视图更新')
       }

           5.使用

              

    const data = {
      name: "zhangsan",
      age: 20,
      info: {
        address: "杭州" // 需要深度监听
      },
      nums: [10, 20, 30]
    };
    
    observer(data);
     

    Vue3.0实现响应式

    Vue3.0基于Proxy来做数据大劫持代理,可以原生支持到数组的响应式,不需要重写数组的原型,还可以直接支持新增和删除属性, 比Vue2.x的Object.defineProperty更加的清晰明了。

    1. 核心代码

      const proxyData = new Proxy(data, {
         get(target,key,receive){ 
           // 只处理本身(非原型)的属性
           const ownKeys = Reflect.ownKeys(target)
           if(ownKeys.includes(key)){
             console.log('get',key) // 监听
           }
           const result = Reflect.get(target,key,receive)
           return result
         },
         set(target, key, val, reveive){
           // 重复的数据,不处理
           const oldVal = target[key]
           if(val == oldVal){
             return true
           }
           const result = Reflect.set(target, key, val,reveive)
           return result
         },
         // 删除属性
         deleteProperty(target, key){
           const result = Reflect.deleteProperty(target,key)
           return result
         }
       }) 
    2. 使用

       const data = {
         name: "zhangsan",
         age: 20,
         info: {
           address: "杭州" // 需要深度监听
         },
         nums: [10, 20, 30]
       };
       

      直接这样就可以了,也不需要声明,Proxy直接会代理监听data的内容,非常的简单方便,唯一的不足就是部分浏览器无法兼容Proxy,也不能hack

    全部源码

    • Vue2.0

       function defineReactive(target, key, value) {
         //深度监听
         observer(value);
       
         Object.defineProperty(target, key, {
           get() {
             return value;
           },
           set(newValue) {
             //深度监听
             observer(value);
             if (newValue !== value) {
               value = newValue;
       
               updateView();
             }
           }
         });
       }
       
       function observer(target) {
         if (typeof target !== "object" || target === null) {
           return target;
         }
       
         if (Array.isArray(target)) {
           target.__proto__ = arrProto;
         }
       
         for (let key in target) {
           defineReactive(target, key, target[key]);
         }
       }
       
       // 重新定义数组原型
       const oldAddrayProperty = Array.prototype;
       const arrProto = Object.create(oldAddrayProperty);
       ["push", "pop", "shift", "unshift", "spluce"].forEach(
         methodName =>
           (arrProto[methodName] = function() {
             updateView();
             oldAddrayProperty[methodName].call(this, ...arguments);
           })
       );
       
       // 视图更新
        function updateView() {
         console.log("视图更新");
       }
       
       // 声明要响应式的对象
       const data = {
         name: "zhangsan",
         age: 20,
         info: {
           address: "杭州" // 需要深度监听
         },
         nums: [10, 20, 30]
       };
       
       // 执行响应式
       observer(data);
    • Vue3.0

       const proxyData = new Proxy(data, {
         get(target,key,receive){ 
           // 只处理本身(非原型)的属性
           const ownKeys = Reflect.ownKeys(target)
           if(ownKeys.includes(key)){
             console.log('get',key) // 监听
           }
           const result = Reflect.get(target,key,receive)
           return result
         },
         set(target, key, val, reveive){
           // 重复的数据,不处理
           const oldVal = target[key]
           if(val == oldVal){
             return true
           }
           const result = Reflect.set(target, key, val,reveive)
           console.log('set', key, val)
           return result
         },
         deleteProperty(target, key){
           const result = Reflect.deleteProperty(target,key)
           console.log('delete property', key)
           console.log('result',result)
           return result
         }
       })
      
        // 声明要响应式的对象,Proxy会自动代理
       const data = {
         name: "zhangsan",
         age: 20,
         info: {
           address: "杭州" // 需要深度监听
         },
         nums: [10, 20, 30]
       };
  • 相关阅读:
    科技巨头争抢的“超级账本”,到底是个什么组织?
    区块链结合教育,将给教育行业带来哪些变革?
    国家区块链战略开启,教育行业应对几何?
    区块链如何改变教育
    区块链技术在教育领域的应用模式与现实挑战
    知乎-区块链技术运用于教育有多少种可能?
    区块链+教育,让教育行业充满希望
    教育区块链应用案例【2019】
    区块链在教育行业的落地应用现状介绍
    PowerShell 搜索文件编码格式
  • 原文地址:https://www.cnblogs.com/yf-html/p/13917215.html
Copyright © 2011-2022 走看看