首頁  >  文章  >  web前端  >  Vue2和Vue3在響應式上有什麼不同?簡單對比

Vue2和Vue3在響應式上有什麼不同?簡單對比

青灯夜游
青灯夜游轉載
2022-07-04 21:00:484435瀏覽

Vue2和Vue3在響應式上有什麼差別?以下這篇文章就來跟大家介紹Vue2和Vue3響應式差別,希望對大家有幫助!

Vue2和Vue3在響應式上有什麼不同?簡單對比

第一次得先知道響應式原理過程,才能往下繼續深入:由淺到深,才是王道。 (學習影片分享:vuejs影片教學

Vue2和Vue3在響應式上有什麼不同?簡單對比

#這種圖片很清晰的描述出響應式原理:每個元件都會產生一個render函數(渲染函數),而render函數又會產生一個vnode(虛擬DOM),當執行render函數的時候會觸發data #裡面getter,觸發的時候產生依賴(在data觸發到哪個數據的變量,就會將哪個變量觀察起來)後面需要查看這個變量是否是之前依賴而被觀察起來的,如果是會觸發setter進行資料修改,如果不是,會直接進行監聽操作。如果確定是先前作為依賴被重新觀察起來的,那就執行 re-render 重新渲染操作,並且進行pacth操作。使用響應式原理能達到更好的資料渲染作用。

Vue2.x的響應式

Vue2中透過 Object.defineProperty 實作資料劫持,使得資料實作響應式更新。 Object.defineProperty()方法會直接在一個物件上定義一個新屬性,或修改一個物件的現有屬性,並傳回此物件。

Object.defineProperty(obj, prop, descriptor)

  • #obj:要定義屬性的物件。
  • prop:要定義或修改的屬性的名稱或 Symbol 。
  • descriptor:要定義或修改的屬性描述符。

傳回值:被傳遞給函數的物件。

回應實作

#物件類型

#透過Object.defineProperty()對屬性的讀取、修改進行攔截(資料劫持)。

陣列類型

透過重寫更新陣列的一系列方法來實作攔截(對陣列的變更方法進行了包裹)。

let person = {   // 模拟Vue2实现响应式
	name:'亮哥',
	age:18   
}
 Object.defineProperty(person, "age", {
 // 当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除,解决新增/删除属性,数据无响应问题
 configurable: true, 
 get: () => { 
 // 收集依赖代码...
   return person.age;
 },
 set: (newVal) => { // 一个给属性提供 setter 的方法
   // 当属性值发生变化时我们可以进行额外操作 如调用监听器
   person.age = newVal;
   // 通知更新视图代码...
 },
   });   
 data.age = 25 // 触发set方法
  • 存在問題
    • 遞歸遍歷資料物件屬性,消耗大
    • 新增/刪除屬性,資料無回應;需要額外方法實作(Vue.set/Vue.delete、this.s##et/set/#get/$delete)
    • 陣列修改需要額外方法實作(Vue.set ),或透過重寫的push/pop/shift/unshift/splice/sort/reverse方法實作;
##Vue3.0的響應式

  • Proxy和Reflect實作響應式原理

    #透過Proxy(代理): 攔截物件中任意屬性的變化, 包括:屬性值的讀寫、屬性的新增、屬性的刪除等。
    • 透過Reflect(反射): 對來源物件的屬性進行操作。
    • 下面分別看看Proxy和Reflect是啥,怎麼這麼強能實現響應式的呢。

    捲起來,已經迫不及待想了解了解一下⊙.⊙

#Proxy

         let data = {   // 模拟Vue2实现响应式
                name:'强哥',
                age:20   
            }
          const proxy =   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'
上面的程式碼對

data

陣列架設了一層攔截,重定義了屬性的讀取(get)和設定(set)行為。 作為建構函數,

Proxy

接受兩個參數:<ul> <li>第一个参数是所要代理的目标对象(上例是一个<code>data对象),即如果没有Proxy的介入,操作原来要访问的就是这个data对象。这里的对象指对象类型(数组也是对象类型)。

  • 第二个参数是一个配置对象handler,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。比如,上面代码中,配置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。
  • 注意: 要使Proxy起作用,必须针对Proxy实例(上例是dataProxy对象)进行操作,而不是针对目标对象(上例是data对象)进行操作。

    可以看出Proxy不仅可以实现Object.defineProperties的功能,还有其他的操作也可以拦截。

    Reflect

    说完Proxy就必须要说一说Reflect这个ES6新增的API。Reflect对象和Proxy对象一样也是用来操作对象的,但是Reflect对象的设计目的有重大的意义。

    Reflect是一个内置的对象,它提供拦截 JavaScript 操作的方法。Reflect不是一个函数对象,因此它是不可构造的。Reflect的所有的方法都是静态的就和Math一样,目前它还没有静态属性。

    Reflect的常见方法

    我们可以将之前Proxy案例中对原对象的操作都修改为Reflect来操作

    const objProxy = new Proxy(obj,{
          has:function(target,key){
             return Reflect.has(target,key)
          }
          set:function(target,key,value){
             return Reflect.set(target,key,value)
          }
          get:function(target,key){
             return Reflect.get(target,key)
          }
          deleteProperty:function(target,key){
             return Reflect.deleteProperty(target,key)
          }
    })

    响应式代码

    function reactive(target = {}) {
      if (typeof target !== "object" || target == null) {
        return target
      }
    
      // 代理配置
      const proxyConf = {
        get(target, key, receiver) {
          //只监听对象本身(非原型)属性
          const ownKeys = Reflect.ownKeys(target)
          if (ownKeys.includes(key)) {
            //如果是本身的属性就监听,如果是对象原型的属性就不监听
            console.log("get", key)
          }
            
          const result = Reflect.get(target, key, receiver)
          //(惰性)深度监听-->提升性能
          return reactive(result)
        },
        set(target, key, val, receiver) {
          // 重复的数据不处理
          if (val === target[key]) {
            return true
          }
    
          // 监听是否是新增的key
          const ownKeys = Reflect.ownKeys(target)
          if (ownKeys.includes(key)) {
            console.log("已有的key", key)
          } else {
            console.log("新增的key", key)
          }
    
          const result = Reflect.set(target, key, val, receiver)
          console.log("set", key, val)
          return result //通过return的值可以看出是否设置成功
        },
        deleteProperty(target, key) {
          const result = Reflect.deleteProperty(target, key)
          console.log("delete property", key)
          return result //是否删除成功
        },
      }
    
      // 生成代理对象
      const observed = new Proxy(target, proxyConf)
      return observed
    }

    Vue3的响应式逻辑如何一步一步构造出来的,我放在另一篇博文Vue3响应式实现逻辑

    https://juejin.im/post/6854573217038893070

    【相关视频教程推荐:web前端

    以上是Vue2和Vue3在響應式上有什麼不同?簡單對比的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除