阅读 387

reactivity 诞生记

由来

看完 vue3 的 reactivity 模块后,脑子里面有无数的念头冒出来。

不得不写一篇文章来记录一下脑子里面的想法

我怕想法太多脑子会爆炸

首先现在各种论坛上讨论的最多的都是直接分析 vue3 的源码

但是我认为直接分析源码,不如分析过程

它是怎么设计的,这个思考的过程才是最迷人的

结果只是这个思考的产物

当脑子里面有这个想法后

着就驱使我去了解去探索 reactivity 到底是怎么设计出来的

所以我就想着基于我自己的理解把整个思考设计的过程来描述出来

思考的开端

下面我们就开始从零思考这个问题

首先每一种技术都是因为需求或者是问题才驱动出来的

所以让我们以一个最简单的问题入手

先看代码


const data = {
    name:"cxr"
}

const myTitle = ""
function update () {
    myTitle = data.name + "Title"
}

update()
console.log(myTitle) // cxrTitle
data.name = "kkb"
update()
console.log(myTitle) // kkbTitle

复制代码

首先我们有一个 myTitle 变量

然后它依赖 data.name

当我们的 data.name 变更的时候

如果我们想让 myTitle 变量的值也变

我们必须要手动的调用 update

不然我们的 myTitle 是无法更新的

当然了,这里只是一个简单的举例,更多的情况下是我们更新了数据,然后需要去更新 UI

jquery 的时代大家就是这个样子来编程的

那我们就需要考虑一下了。

有没有可能,当我们改变了 data.name 的时候,让所以依赖它的视图或者函数都自动执行呢?就像我们手动调用 update 那样。

好,其实思考到这里就有了响应式编程的雏形了。

但是管它叫什么呢? 叫哈哈哈叫嘿嘿嘿都有什么关系呢?

最后我们理一下我们的需求

当改变一个数据的时候,自动执行所有依赖这个数据的函数

当改变一个数据的时候,自动执行所有依赖这个数据的函数

那怎么实现这个需求呢?

我们先推理一下

首先我们需要知道都有哪些函数依赖了这个数据

然后我们需要把这些函数都收集起来

等到我们更改数据的时候好手动调用这些收集好的函数

在稍微整理一下我们的问题点

  1. 需要收集对当前数据的依赖函数
    1. 怎么收集,收集到哪里?
    2. 这个依赖函数要怎么获取到呢?
  2. 更改数据的时候调用收集好的依赖函数
    1. 如何知道数据的更改呢?
    2. 如何获取到之前收集好的依赖函数呢?

我发现第一个要解决的问题是 1.2 这个问题

只有我们先获取到依赖函数之后才能相继解决后续的问题

这个问题是个开端

const data = {
    name:"cxr"
}

let myTitle = ""

function update () {
    myTitle = data.name + "Title"
}

// 依赖收集
const dependCollection = {}
dependCollection.data = {};
dependCollection.data.name = [];
dependCollection.data.name.push(update)
复制代码

哈哈哈,我用最 low 的方式来做到了收集依赖函数

low 在哪里呢? low 在现在都是硬编码的

但是无所谓 现在来看不重要 已经可以解决我们的问题了

ps: 我可太机灵了 完成这个依赖收集仅仅用了几秒钟哈哈哈哈

那接下来思考另外一个问题: 更改数据的时候如何触发我们收集好的依赖函数呢?

这个问题其实有答案

在 vue3 时代之前我们用的是 object.defineProperty

现在我们用 proxy

// 触发依赖
const handler = {
  get: function (obj, prop) {
    return Reflect.get(obj, prop);
  },
  set: function (obj, prop, value) {
    const result = Reflect.set(obj, prop, value);
    // 触发依赖当前数据的函数
    dependCollection.data.name.forEach((fn) => fn());
    return result
  },
};

const observed = new Proxy(data, handler);
observed.name = "kkb"
console.log(myTitle)
复制代码

用了 proxy 之后我们就能知道啥时候更新数据了

当我们知道了这个节点后,那么在数据更新好之后触发依赖不就好了吗

简单的试了试

果然当我们更新了 observed.name 的时候

myTitle 自己就更新了哈哈哈哈

老铁没毛病~~~

效果是达到了

但是我们现在的代码太 low 了,都是硬编码

那我们接下来就该思考如何写一个更通用的依赖收集和触发依赖的逻辑

ps: proxy 这段我直接 copy 的 MDN 哈哈哈哈

编写更通用的依赖收集和触发依赖函数

TODO

累了累了,明天再继续思考