Vue3.0 响应式原理

2021-10-19  本文已影响0人  A_走在冷风中

Vue3 使用 Proxy 对象重写响应式系统,这个系统主要有以下几个函数来组合完成的:

1、reactive:
接收一个参数,判断这参数是否是对象。不是对象则直接返回这个参数,不做响应式处理
创建拦截器对象 handler, 设置 get/set/deleteProperty
get
收集依赖(track)
返回当前 key 的值。
如果当前 key 的值是对象,则为当前 key 的对象创建拦截器 handler, 设置 get/set/deleteProperty
如果当前的 key 的值不是对象,则返回当前 key 的值
set
设置的新值和老值不相等时,更新为新值,并触发更新(trigger)
deleteProperty
当前对象有这个 key 的时候,删除这个 key 并触发更新(trigger)
返回 Proxy 对象
2、effect: 接收一个函数作为参数。作用是:访问响应式对象属性时去收集依赖
3、track:
接收两个参数:target 和 key
如果没有 activeEffect,则说明没有创建 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 属性,有的话循环触发收集的 effect()

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

//判断是否是对象,如果是对象继续调用reactive转换成响应式对象
const convert = (target) => (isObject(target) ? reactive(target) : target)

const hasOwnProperty = Object.prototype.hasOwnProperty
//判断对象中是否有某个属性
const hasOwn = (target, key) => hasOwnProperty.call(target)

##手写核心响应式原理

/**
 *  接收一个参数,判断这个参数是否是对象
 *  创建拦截器对象handler,设置get/set/deleteProperty
 *  返回Proxy对象
 */
export function reactive(target) {
  //判断target是否是对象,不是对象直接返回
  if (!isObject(target)) return
  //创建拦截器对象handler
  const handler = {
    get(target, key, receiver) {
      //收集依赖
      track(target, key)
      const result = Reflect.get(target, key, receiver)
      //如果result是对象继续调用reactive转换成响应式对象
      return convert(result)
    },
    set(target, key, value, receiver) {
      //获取属性值
      const oldValue = Reflect.get(target, key, receiver)
      let result = true
      //判断新值是否等于旧值
      if (oldValue !== value) {
        // 如果相等,调用set修改属性值
        result = Reflect.set(target, key, value, receiver)
        //触发更新
        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属性且删除成功
        //触发更新
        trigger(target, key)
      }
      return result
    },
  }

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

let activeEffect = null
//接收一个函数作为参数
export function effect(callback) {
  activeEffect = callback
  callback() //访问响应式对象属性,去收集依赖
  activeEffect = null
}

//定义一个map 存储依赖
let targetMap = new WeakMap()
//接收两个参数 目标对象target 要跟踪的属性key
export function track(target, key) {
  if (!activeEffect) return
  let depsMap = targetMap.get(target)
  //判断depsMap 是否有值,没有的话就创建一个,存储到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)
}

//发送通知函数
export function trigger(target, key) {
  //去depsMap中查找target
  const depsMap = targetMap.get(target)
  //如果没有找到直接返回
  if (!depsMap) return

  //查找dep集合
  const dep = depsMap.get(key)
  if (dep) {
    //找到dep中的每一个函数,调用effect
    dep.forEach((effect) => {
      effect()
    })
  }
}

//定义一个响应式属性
export function ref(raw) {
  //判断raw是否是ref创建的对象, 如果是直接返回
  if (isObject(raw) && raw.__v__isRef) return
  let value = convert(raw)
  //创建ref对象
  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')
      }
    },
  }
  return r
}

export function toRefs(proxy) {
  //判断proxy 是否是reactive创建的
  //判断proxy是否是数组
  const ret = proxy instanceof Array ? new Array(proxy.length) : {}
  for (const key in proxy) {
    //将每一个属性都转换成ref属性
    ret[key] = toProxyRef(proxy, key)
  }
  return ret
}

//将属性转换成ref
function toProxyRef(proxy, key) {
  const r = {
    __v__isRef: true,
    get value() {
      //无需收集依赖, proxy对象内部会收集
      return proxy[key]
    },
    set value(newValue) {
      proxy[key] = newValue
    },
  }
  return r
}

//computed 需要接收一个有返回值函数作为参数
//这个函数的返回值就是计算属性的值
export function computed(getter) {
  const result = ref()
  //将getter的返回值存入result中
  effect(() => {
    result.value = getter()
  })
  return result
}


上一篇下一篇

猜你喜欢

热点阅读