reactive响应式大致思路

717 阅读1分钟

目标

实现响应式数据,类似下面这样,存在两个方法:reactive、watchEffect,reactive用于定义响应式数据,watchEffect用于监听数据变化。

const { reactive, watchEffect } = require('./reactivity')

const obj = reactive({
  a: 10,
})

let b

watchEffect(() => {
  b = obj.a + 10
  console.log('b更新了', b)
})
obj.a = 20

image.png

思路

要监听数据变化,需要利用Object.defineProperty或者Proxy,这里选用Proxy使用代理的方式,在get时添加依赖,在set时触发依赖,来对数据进行监听。

实现步骤

依赖管理

定义Dep类来对依赖进行管理。在get时调用depend方法收集依赖,set时调用notice触发依赖。

let currentEffect
class Dep {
  constructor() {
    this.effects = new Set()
  }
  // 收集依赖
  depend() {
    if (currentEffect) {
      this.effects.add(currentEffect)
    }
  }
  // 触发依赖
  notice() {
    this.effects.forEach((effect) => effect())
  }
}

watchEffect

该方法是一个高阶函数,参数为一个函数,将该参数暂存在currentEffect中,调用effect时将收集到依赖,依赖收集完成后,将currentEffect重置。

function watchEffect(effect) {
  currentEffect = effect
  effect()
  currentEffect = null
}

reactive

reactive方法返回一个代理target对象的proxy,在proxy中代理getset实现对数据的监听。

function reactive(target) {
  return new Proxy(target, {
    get(target, key) {
      // 收集依赖
      const dep = getDep(target, key)
      dep.depend()
      return Reflect.get(target, key)
    },
    set(target, key, value) {
      const dep = getDep(target, key)
      Reflect.set(target, key, value)
      // 触发依赖
      dep.notice()
    },
  })
}
const targetMap = new Map()
function getDep(target, key) {
  let depsMap = targetMap.get(target)
  if (!depsMap) {
    depsMap = new Map()
    targetMap.set(target, depsMap)
  }
  let dep = depsMap.get(key)
  if (!dep) {
    dep = new Dep()
    depsMap.set(key, dep)
  }
  return dep
}