vue 3 入门之响应式原理

80 阅读2分钟

1. 如果没有响应式,我们怎么进行更新

const obj = {
  name: 'zhangsan'
}
function fn() {
    document.body.innerHTML = obj.name
}

fn()  // 这里界面上是 zhangsan

obj.name = 'lisi' // 当我们改变名字时,界面上仍然是 zhangsan

fn() // 只有当我们重新执行 fn() 时,才会更新界面

可以看到,如果没有响应式的加持,我们更新完数据,页面是不会发生变化的,当我们重新执行函数,也就是重新获取最新的值来获取更新,所以所谓的响应式就是把这个更新的过程给自动化了,当数据变化时,自动地去更新用到此数据的地方

2. 实现一个简单的响应式

 const data = {
      text: '响应式'
  }

  function effect() {
      document.body.innerHTML = obj.text
  }

  // 定义一个 “桶” 
  const bucket = new Set();

  const obj = new Proxy(data, {
      get(target, key) {
          // 当读数据时,把 effect 收集起来
          bucket.add(effect);
          return target[key]
      },
      set(target, key, newVal) {
          // 值更新了,重新执行一下依赖它的程序。
          target[key] = newVal;
          // 把收集到的 effect 函数重新执行,以达到更新
          bucket.forEach(fn => fn())

          return true
      }
  })

  effect();  // 触发读取, getter 监听到,收集起来
  setTimeout(() => {
      obj.text = '新'  // 触发 setter,重新执行 effect 更新
  }, 1000);

这里的 effect 其实代表着用到数据的地方,可能对应着我们的视图,也就是类比与之前 Vue 2 里面的 watcher,再 get 里手机 effect,当变化时触发 set 通知收集到的 effect 重新执行,这样就实现了简单的响应式

上面的实现只是简单的实现,我们并没有做到 keyeffect 一一对应,并且我们的只能做这个指定的 effect 。我们接下来需要

我们定义一个变量来存储 effect ,
let activeEffect = null;

// 传如用到数据的函数,这样就做到了匿名函数实现收集
function effect(fn) {
    activeEffect = fn; 
    fn()
}

const bucket = new WeakMap()
const obj = new Proxy(data, {
    get(target, key) {
        // 如果没有 effect 也就是没有用到该属性的地方,直接就 return
        if (!activeEffect) return target[key];

        // 从 “桶” 中取得依赖 map,一个 key --> effects 的 Map 结构
        let depsMap = bucket.get(target);

        if (!depsMap) {
            bucket.set(target, (depsMap = new Map()))
        }

        // 到这里,就已经建立(存在)一个关系,类似
        // 一个 weakMap,key:target ,  value: Map, 这个 Map: key:对象的每一个属性 ,value:是一个set里面存有多个 effect

        // 然后根据 key 从 depsMap 中拿到 deps 是一个 set
        let deps = depsMap.get(key);
        if (!deps) {
            depsMap.set(key, (deps = new Set()));
        }
        
        // 向deps 中添加 effect
        deps.add(activeEffect)

        return target[key]
    },
    set(target, key, newVal) {
        // 值更新了,重新执行一下依赖它的程序。
        target[key] = newVal;
    
        // 拿到对象下面存的所有key--> effects map
        const depsMap = bucket.get(target);

        if (!depsMap) return;
        
        // 拿到key 对应的 effects 
        const effects = depsMap.get(key)

        effects && effects.forEach(fn => fn());
        return true
    }
})

这样就实现了一个相对完善的响应式。

实在编不下去了

这个记录,是看 《Vue 设计与实现》做的笔记相当于,这本书真的很好,对深入了解 vue 很有帮助,强烈推荐,看我的这篇记录不如去看原书,配合源码,哈哈哈。