Vue3.0 響應(yīng)式系統(tǒng)原理

Vue3 響應(yīng)式

  • Proxy 對(duì)象實(shí)現(xiàn)屬性監(jiān)聽
  • 多層屬性嵌套,在訪問(wèn)屬性過(guò)程中處理下一級(jí)屬性
  • 默認(rèn)監(jiān)聽動(dòng)態(tài)添加的屬性
  • 默認(rèn)監(jiān)聽屬性的刪除操作
  • 默認(rèn)監(jiān)聽數(shù)組索引和length屬性
  • 可以作為單獨(dú)的模塊使用

核心方法

  • reactive/ref/toRefs/computed
  • effect
  • track
  • trigger

Proxy

// 問(wèn)題1: set 和 deleteProperty 中需要返回布爾類型的值
    //        在嚴(yán)格模式下,如果返回 false 的話會(huì)出現(xiàn) Type Error 的異常
    const target = {
      foo: 'xxx',
      bar: 'yyy'
    }
    // Reflect.getPrototypeOf()
    // Object.getPrototypeOf()
    const proxy = new Proxy(target, {
      get (target, key, receiver) {
        // return target[key]
        return Reflect.get(target, key, receiver)
      },
      set (target, key, value, receiver) {
        // target[key] = value
        return Reflect.set(target, key, value, receiver)
      },
      deleteProperty (target, key) {
        // delete target[key]
        return Reflect.deleteProperty(target, key)
      }
    })

    proxy.foo = 'zzz'
    // delete proxy.foo


    // 問(wèn)題2:Proxy 和 Reflect 中使用的 receiver

    // Proxy 中 receiver:Proxy 或者繼承 Proxy 的對(duì)象
    // Reflect 中 receiver:如果 target 對(duì)象中設(shè)置了 getter,getter 中的 this 指向 receiver

    const obj = {
      get foo() {
        console.log(this)
        return this.bar
      }
    }

    const proxy = new Proxy(obj, {
      get (target, key, receiver) {
        if (key === 'bar') { 
          return 'value - bar' 
        }
        return Reflect.get(target, key, receiver)
      }
    })

reactive

  • 接收一個(gè)參數(shù),判斷參數(shù)是否是對(duì)象
  • 創(chuàng)建攔截器對(duì)象 handler,設(shè)置 get/set/deleteProperty
  • 返回 Proxy 對(duì)象
const isObject = val => val !== null && typeof val === 'object'
const convert = target => isObject(target) ? reactive(target) : target
const hasOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (target.key) => hasOwnProperty.call(target, key) //判斷對(duì)象中是否有對(duì)應(yīng)屬性

export function reactive(target) {
  //1.判斷是否是對(duì)象
  if (!isObject(target)) return target

  const handler = {
    get(target, key, receiver) {
      //收集依賴
      const res = Reflect.get(target, key, receiver)
      return convert(res)
    },
    set(target, key, value, receiver) {
      const oldval = Reflect.get(target, key, receiver)
      let result = true
      if (oldval !== value) {
        result = Reflect.set(target, key, value, receiver)
        //觸發(fā)更新

      }
      return result //返回布爾值結(jié)果
    },
    deleteProperty(target, key) {
      const haskey = hasOwn(target, key)
      const result = Reflect.deleteProperty(target, key)
      if (haskey && result) {
        //觸發(fā)更新

      }
      return result //返回布爾值結(jié)果
    }
  }

  return new Proxy(target, handler)
}

收集依賴 effect && track

export function effect(callback) {
  activeEffect = callback
  callback() //訪問(wèn)響應(yīng)式對(duì)象屬性,去收集依賴
  activeEffect = null
}

let targetMap = new WeakMap()
//收集依賴
export function track(target,key){
  if(!activeEffect) return
  let depsMap = targetMap.get(target)
  if(!depsMap){
    targetMap.set(target,(depsMap = new Map()))
  }
  let dep = depsMap.get(key)
  if(!dep){
    depsMap.set(key,(dep = new Set()))
  }
  dep.add(activeEffect)
}

trigger

//觸發(fā)更新
export function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  const dep = depsMap.get(key)
  if (dep) {
    dep.forEach(effect => {
      effect()
    });
  }
}

創(chuàng)建響應(yīng)式對(duì)象 ref

export function ref(raw) {
  //判斷 raw是否是ref創(chuàng)建的對(duì)象  是直接返回
  if (isObject(raw) && raw.__v_isRef) return

  let value = convert(raw) //判斷是否是對(duì)象 是的話 創(chuàng)建reactive 響應(yīng)式對(duì)象

  const r = {
    __v_isRef: true,
    get value() {
      track(r, 'value') //收集依賴
      return value
    },
    set value(newValue) {
      if (newValue !== value) {
        raw = newValue
        value = convert(raw)
        trigger(r, 'value')
      }
    }
  }
}

reactive ref

  • ref 可以把基本數(shù)據(jù)類型數(shù)據(jù),轉(zhuǎn)成響應(yīng)式對(duì)象
  • ref 返回的對(duì)象,重新賦值成對(duì)象也是響應(yīng)式的
  • reactive 返回的對(duì)象,重新賦值丟失響應(yīng)式
  • reactive 返回的對(duì)象不可以解構(gòu)

toRefs函數(shù)

接收一個(gè)reactive返回的響應(yīng)式對(duì)象,也就是一個(gè)proxy對(duì)象,傳入?yún)?shù)不是reactive創(chuàng)建的響應(yīng)式對(duì)象直接返回,再把傳入對(duì)象的所有屬性轉(zhuǎn)換成一個(gè)類似ref返回的對(duì)象,把轉(zhuǎn)換后的屬性掛載在一個(gè)新的對(duì)象上返回。

function toProxyRef(proxy, key) {
  const r = {
    __v_isRef: true,
    get value() {
      return proxy[key]
    },
    set value(newValue) {
      proxy[key] = newValue
    }
  }
  return r
}

export function toRefs(proxy) {
  const ret = proxy instanceof Array ? new Array(proxy.length) : {}
  for (const key in proxy) {
    ret[key] = toProxyRef(proxy, key)
  }
  return ret
}

computed

需要接收一個(gè)有返回值的函數(shù)作為參數(shù),這個(gè)函數(shù)的返回值就是計(jì)算屬性的值,并且需要監(jiān)聽這個(gè)函數(shù)內(nèi)部使用響應(yīng)數(shù)據(jù)的變化,最后將這個(gè)函數(shù)執(zhí)行的結(jié)果返回。

export function computed(getter){
  const result = ref()

  effect( () => ( result.value = getter() ) )
  
  return result
}

所有方法


const isObject = val => val !== null && typeof val === 'object'
const convert = target => isObject(target) ? reactive(target) : target
const hasOwnProperty = Object.prototype.hasOwnProperty
const hasOwn = (target.key) => hasOwnProperty.call(target, key) //判斷對(duì)象中是否有對(duì)應(yīng)屬性

export function reactive(target) {
  //1.判斷是否是對(duì)象
  if (!isObject(target)) return target

  const handler = {
    get(target, key, receiver) {
      //收集依賴
      track(target, key)
      const res = Reflect.get(target, key, receiver)
      return convert(res)
    },
    set(target, key, value, receiver) {
      const oldval = Reflect.get(target, key, receiver)
      let result = true
      if (oldval !== value) {
        result = Reflect.set(target, key, value, receiver)
        //觸發(fā)更新
        trigger(target, key)
      }
      return result //返回布爾值結(jié)果
    },
    deleteProperty(target, key) {
      const haskey = hasOwn(target, key)
      const result = Reflect.deleteProperty(target, key)
      if (haskey && result) {
        //觸發(fā)更新
        trigger(target, key)
      }
      return result //返回布爾值結(jié)果
    }
  }

  return new Proxy(target, handler)
}

let activeEffect = null

export function effect(callback) {
  activeEffect = callback
  callback() //訪問(wèn)響應(yīng)式對(duì)象屬性,去收集依賴
  activeEffect = null
}

let targetMap = new WeakMap()
//收集依賴
export function track(target, key) {
  if (!activeEffect) return
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    targetMap.set(target, (depsMap = new Map()))
  }
  let dep = depsMap.get(key)
  if (!dep) {
    depsMap.set(key, (dep = new Set()))
  }
  dep.add(activeEffect)
}

//觸發(fā)更新
export function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if (!depsMap) return
  const dep = depsMap.get(key)
  if (dep) {
    dep.forEach(effect => {
      effect()
    });
  }
}

export function ref(raw) {
  //判斷 raw是否是ref創(chuàng)建的對(duì)象  是直接返回
  if (isObject(raw) && raw.__v_isRef) return

  let value = convert(raw) //判斷是否是對(duì)象 是的話 創(chuàng)建reactive 響應(yīng)式對(duì)象

  const r = {
    __v_isRef: true,
    get value() {
      track(r, 'value') //收集依賴
      return value
    },
    set value(newValue) {
      if (newValue !== value) {
        raw = newValue
        value = convert(raw)
        trigger(r, 'value')
      }
    }
  }
}

function toProxyRef(proxy, key) {
  const r = {
    __v_isRef: true,
    get value() {
      return proxy[key]
    },
    set value(newValue) {
      proxy[key] = newValue
    }
  }
  return r
}

export function toRefs(proxy) {
  const ret = proxy instanceof Array ? new Array(proxy.length) : {}
  for (const key in proxy) {
    ret[key] = toProxyRef(proxy, key)
  }
  return ret
}

export function computed(getter) {
  const result = ref()

  effect(() => (result.value = getter()))

  return result
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容