1. 程式人生 > 程式設計 >詳解Vue3的響應式原理解析

詳解Vue3的響應式原理解析

目錄
  • 2響應式原理回顧
  • Vue3響應式原理剖析
    • 巢狀物件響應式
    • 避免重複代理
  • 總結

    Vue2響應式原理回顧

    // 1.物件響應化:遍歷每個key,定義getter、setter
    // 2.陣列響應化:覆蓋陣列原型方法,額外增加通知邏輯
    const originalProto = Array.prototype
    const arrayProtowww.cppcns.com = Object.create(originalProto)
      ;['push','pop','shift','unshift','splice','reverse','sort'].forEach(
        method => {
          arrayProto[method] = function () {
            originalProto[method].apply(this,arguments)
            notifyUpdate()
          }
        }
      )
    function observe (obj) {
      if (typeof obj !== 'object' || obj == null) {
        return
      }
      // 增加陣列型別判斷,若是陣列則覆蓋其原型
      if (Array.isArray(obj)) {
        Object.setPrototypeOf(obj,arrayProto)
      } else {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
          const key = keys[i]
          defineReactive(obj,key,obj[key])
        }
      }
    }
    function defineReactive (obj,val) {
      observe(val) // 解決巢狀物件問題
      Object.defineProperty(obj,{
        get () {
          return val
        },set (newVal) {
          if (newVal !== val) {
            observe(newVal) // 新值是物件的情況
            val = newVal
            notifyUpdate()
          }
        }
      })
    }
    function notifyUpdate () {
      console.log('頁面更新!')
    }
    

    vue2響應式弊端:
    響應化過程需要遞迴遍歷,消耗較大
    新加或刪除屬性無法監聽
    陣列響應化需要額外實現
    Map、Set、Class等無法響應式
    修改語法有限制

    Vue3響應式原理剖析

    vue3使用ES6的Proxy特性來解決這些問題。

    function reactive (obj) {
      if (typeof obj !== 'object' && obj != null) {
        return obj
      }
      // Proxy相當於在物件外層加攔截
      // http://es6.ruanyifeng.com/#docs/proxy
      consMjyNhwCgBKt observed = new Proxy(obj,{
        get (target,receiver) {
          // Reflect用於執行物件預設操作,更規範、更友好
          // Proxy和Object的方法Reflect都有對應
          // http://es6.ruanyifeng.com/#docs/reflect
          const res = Reflect.get(target,receiver)
          console.log(`獲取${key}:${res}`)
          return res
        },set (target,value,receiver) {
          const res = Reflect.set(target,receiver)
          console.log(`設定${key}:${value}`)
          return res
        },deleteProperty (target,key) {
          const res = Reflect.deleteProperty(target,key)
          console.log(`刪除${key}:${res}`)
          return res
        }
      })
      return observed
    }
    //程式碼測試
    const state = reactive({
      foo: 'foo',bar: { a: 1 }
    })
    // 1.獲取
    state.foo // ok
    // 2.設定已存在屬性
    state.foo = 'fooooooo' // ok
    // 3.設定不存在屬性
    state.dong = 'dong' // ok
    // 4.刪除屬性
    delete state.dong // ok
    

    巢狀物件響應式

    測試:巢狀物件不能響應

    // 設定巢狀物件屬性
    react.bar.a = 10 // no ok
    

    新增物件型別遞迴

          // 提取幫助方法
          const isObject = val => val !== null && typeof val === 'object'
          function reactive (obj) {
            //判斷是否物件
            if (!isObject(obj)) {
              return obj
            }
            const observed = new Proxy(obj,{
              ge
    t (target,receiver) { // ... // 如果是物件需要遞迴 MjyNhwCgBK return isObject(res) ? reactive(res) : res },//... }

    避免重複代理

    重複代理,比如

    reactive(data) // 已代理過的純物件
    reactive(react) // 代理物件

    解決方式:將之前代理結果快取,get時直接使用

    const toProxy = new WeakMap() // 形如obj:observed
          const toRaw = new WeakMap() // 形如observed:obj
          function reactive (obj) {
            //...
            // 查詢快取,避免重複代理
            if (toProxy.has(obj)) {
              return toProxy.get(obj)
            }
            if (toRaw.has(obj)) {
              return obj
            }
            const observed = new Proxy(...)
            // 快取代理結果
            toProxy.set(obj,observed)
            toRaw.set(observed,obj)
            return observed
          }
          // 測試效果
          console.log(reawww.cppcns.comctive(data) === state)
          console.log(reactive(state) === state)
    

    總結

    本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注我們的更多內容!