VUE3响应式原理(手撕)

211 阅读2分钟

vue3!

image.png 先创建一个类,收集每个对象改变时候要进行响应的函数,上图的叶子节点。

let activeReactive = null
class Depend {
    constructor() {
        this.reactiveFns= new Set()//创建这个是为了收集依赖,防止重复收集依赖
    },
    depend() {//给set中添加相关响应函数
        if(activeReactive) {
            this.reactiveFns.add(activeReactive)
        }
    },
    notify() {//当对象进行修改时候,遍历相关响应函数
        this.reactiveFns.forEach(Fn=>Fn())
    }
}

封装一个响应函数,将与对象有关的函数放在响应函数里面

function wathchFn (fn) {
    activeReactive = fn
    fn() //执行传入函数,判断是否与对象属性有关,若有关则加入depend
    activeReactive = null 
}

封装一个获取depend的函数,如上图

const targetMap = new WeakMap()
function getDepend (target,key) {
   let map = targetMap.target
   if (!map) {
       map = new Map() //新建Map
       targetMap.set(target,map) //WeakMap的键必须为对象
   }
   let depend = map.key
   if (!depend) {
       depend = new Depend()//新建depend
       map.set(key,depend)
   }
    return depend //创建了一下当前tatget和key下的depend
}

proxy的响应式函数

function reactive(obj) {
  return new Proxy(obj, {
    get: function(target, key, receiver) {
      // 根据target.key获取对应的depend
      const depend = getDepend(target, key)
      // 给depend对象中添加响应函数
      depend.depend()
  
      return Reflect.get(target, key, receiver)
    },
    set: function(target, key, newValue, receiver) {
      Reflect.set(target, key, newValue, receiver)
      const depend = getDepend(target, key)
      depend.notify()//若进行修则遍历响应函数
    }
  })
}

结果测试

const infoProxy = reactive({
  address: "广州市",
  height: 1.88
})
​
watchFn(() => {
  console.log(infoProxy.address)
})
​
infoProxy.address = "北京市"const foo = reactive({
  name: "foo"
})
​
watchFn(() => {
  console.log(foo.name)
})
​
foo.name = "bar"

image.png

可以理解,响应式原理为:当一个变量有一个初值,另一段代码使用了这个值,那么当我们修改这个值时候,这段代码可以自动重新执行。

需要重新执行的代码可能不止一行,我们可以将这些代码放入一个函数当中,这样我们的问题变成了,当数据发生变化时,自动去执行某一个函数。

当我们将一个函数放入响应函数中,怎样判断这个函数与哪些对象属性的修改有关呢?

我们可以执行传入的函数,当这个函数对对象的某个属性进行获取或修改时候,就会进入proxy的get或者set当中。我们可以在set或者get当中将这个函数加入depend,当对象发生修改时,遍历这个depend。

那我们要如何获取每个对象中每个属性所对应的depend呢?

我们将get或者set中的target和key传入获取depend的函数(getDepend)。从weakmap中获取target对应的map,若没有map的话则新建一个map;再从map中获取key所对应的depend,若没有depend的话则新建一个depend,然后返回这个depend。这样我们就给有响应函数的属性都创建了一个depend