Vue3.0 響應式原理

Vue3 使用 Proxy 對象重寫響應式系統(tǒng),這個系統(tǒng)主要有以下幾個函數(shù)來組合完成的:

1、reactive:
接收一個參數(shù),判斷這參數(shù)是否是對象。不是對象則直接返回這個參數(shù),不做響應式處理
創(chuàng)建攔截器對象 handler, 設(shè)置 get/set/deleteProperty
get
收集依賴(track)
返回當前 key 的值。
如果當前 key 的值是對象,則為當前 key 的對象創(chuàng)建攔截器 handler, 設(shè)置 get/set/deleteProperty
如果當前的 key 的值不是對象,則返回當前 key 的值
set
設(shè)置的新值和老值不相等時,更新為新值,并觸發(fā)更新(trigger)
deleteProperty
當前對象有這個 key 的時候,刪除這個 key 并觸發(fā)更新(trigger)
返回 Proxy 對象
2、effect: 接收一個函數(shù)作為參數(shù)。作用是:訪問響應式對象屬性時去收集依賴
3、track:
接收兩個參數(shù):target 和 key
如果沒有 activeEffect,則說明沒有創(chuàng)建 effect 依賴
如果有 activeEffect,則去判斷 WeakMap 集合中是否有 target 屬性,
WeakMap 集合中沒有 target 屬性,則 set(target, (depsMap = new Map()))
WeakMap 集合中有 target 屬性,則判斷 target 屬性的 map 值的 depsMap 中是否有 key 屬性
depsMap 中沒有 key 屬性,則 set(key, (dep = new Set()))
depsMap 中有 key 屬性,則添加這個 activeEffect
4、trigger:
判斷 WeakMap 中是否有 target 屬性
WeakMap 中沒有 target 屬性,則沒有 target 相應的依賴
WeakMap 中有 target 屬性,則判斷 target 屬性的 map 值中是否有 key 屬性,有的話循環(huán)觸發(fā)收集的 effect()

//判斷是否是對象
const isObject = (val) => val != null && typeof val === 'object'

//判斷是否是對象,如果是對象繼續(xù)調(diào)用reactive轉(zhuǎn)換成響應式對象
const convert = (target) => (isObject(target) ? reactive(target) : target)

const hasOwnProperty = Object.prototype.hasOwnProperty
//判斷對象中是否有某個屬性
const hasOwn = (target, key) => hasOwnProperty.call(target)

##手寫核心響應式原理

/**
 *  接收一個參數(shù),判斷這個參數(shù)是否是對象
 *  創(chuàng)建攔截器對象handler,設(shè)置get/set/deleteProperty
 *  返回Proxy對象
 */
export function reactive(target) {
  //判斷target是否是對象,不是對象直接返回
  if (!isObject(target)) return
  //創(chuàng)建攔截器對象handler
  const handler = {
    get(target, key, receiver) {
      //收集依賴
      track(target, key)
      const result = Reflect.get(target, key, receiver)
      //如果result是對象繼續(xù)調(diào)用reactive轉(zhuǎn)換成響應式對象
      return convert(result)
    },
    set(target, key, value, receiver) {
      //獲取屬性值
      const oldValue = Reflect.get(target, key, receiver)
      let result = true
      //判斷新值是否等于舊值
      if (oldValue !== value) {
        // 如果相等,調(diào)用set修改屬性值
        result = Reflect.set(target, key, value, receiver)
        //觸發(fā)更新
        trigger(target, key)
      }
      return result
    },
    deleteProperty(target, key) {
      //判斷target中是否有自己的key屬性
      const hasKey = hasOwn(target, key)
      const result = Reflect.deleteProperty(target, key)
      if (hasKey && result) {
        //target中有key屬性且刪除成功
        //觸發(fā)更新
        trigger(target, key)
      }
      return result
    },
  }

  //返回代理對象
  return new Proxy(target, handler)
}

let activeEffect = null
//接收一個函數(shù)作為參數(shù)
export function effect(callback) {
  activeEffect = callback
  callback() //訪問響應式對象屬性,去收集依賴
  activeEffect = null
}

//定義一個map 存儲依賴
let targetMap = new WeakMap()
//接收兩個參數(shù) 目標對象target 要跟蹤的屬性key
export function track(target, key) {
  if (!activeEffect) return
  let depsMap = targetMap.get(target)
  //判斷depsMap 是否有值,沒有的話就創(chuàng)建一個,存儲到targetMap
  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ā)送通知函數(shù)
export function trigger(target, key) {
  //去depsMap中查找target
  const depsMap = targetMap.get(target)
  //如果沒有找到直接返回
  if (!depsMap) return

  //查找dep集合
  const dep = depsMap.get(key)
  if (dep) {
    //找到dep中的每一個函數(shù),調(diào)用effect
    dep.forEach((effect) => {
      effect()
    })
  }
}

//定義一個響應式屬性
export function ref(raw) {
  //判斷raw是否是ref創(chuàng)建的對象, 如果是直接返回
  if (isObject(raw) && raw.__v__isRef) return
  let value = convert(raw)
  //創(chuàng)建ref對象
  const r = {
    __v__isRef: true,
    get value() {
      //收集依賴
      track(r, 'value')
      return value
    },
    set value(newValue) {
      if (newValue !== value) {
        raw = newValue
        value = convert(raw)
        //觸發(fā)更新
        trigger(r, 'value')
      }
    },
  }
  return r
}

export function toRefs(proxy) {
  //判斷proxy 是否是reactive創(chuàng)建的
  //判斷proxy是否是數(shù)組
  const ret = proxy instanceof Array ? new Array(proxy.length) : {}
  for (const key in proxy) {
    //將每一個屬性都轉(zhuǎn)換成ref屬性
    ret[key] = toProxyRef(proxy, key)
  }
  return ret
}

//將屬性轉(zhuǎn)換成ref
function toProxyRef(proxy, key) {
  const r = {
    __v__isRef: true,
    get value() {
      //無需收集依賴, proxy對象內(nèi)部會收集
      return proxy[key]
    },
    set value(newValue) {
      proxy[key] = newValue
    },
  }
  return r
}

//computed 需要接收一個有返回值函數(shù)作為參數(shù)
//這個函數(shù)的返回值就是計算屬性的值
export function computed(getter) {
  const result = ref()
  //將getter的返回值存入result中
  effect(() => {
    result.value = getter()
  })
  return result
}


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

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

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