目前工作中大概有 40% 的需求是在用 Vue2 的技术栈,所谓知其然更要知其所以然,为了更好的使用 Vue 、更快的排查问题,最近学习了源码相关的一些知识,虽然网上总结 Vue 的很多很多了,不少自己一个,但也不多自己一个,欢迎一起讨论学习,发现问题欢迎指出。
响应式系统要干什么
回到最简单的代码:
| 1 | data = { | 
响应式系统要做的事情:某个依赖了 data 数据的函数,当所依赖的 data 数据改变的时候,该函数要重新执行。
我们期望的效果:当上边 data.text 修改的时候, updateComponent 函数再执行一次。
为了实现响应式系统,我们需要做两件事情:
- 知道 - data中的数据被哪些函数依赖
- data中的数据改变的时候去调用依赖它的函数们
为了实现第 1 点,我们需要在执行函数的时候,将当前函数保存起来,然后在读取数据的时候将该函数保存到当前数据中。
第 2 点就迎刃而解了,当修改数据的时候将保存起来的函数执行一次即可。
在读取数据和修改数据的时候需要做额外的事情,我们可以通过 Object.defineProperty()  重写对象属性的 get 和 set 函数。
响应式数据
我们来写一个函数,重写属性的 get 和 set 函数。
| 1 | /** | 
为了调用更方便,我们把第 1 步和第 2 步的操作封装一个 Dep  类。
| 1 | export default class Dep { | 
我们将当前执行的函数保存到 Dep 类的 target 变量上。
保存当前正在执行的函数
为了保存当前的函数,我们还需要写一个 Watcher 类,将需要执行的函数传入,保存到 Watcher 类中的 getter 属性中,然后交由 Watcher 类负责执行。
这样在 Dep 类中, subs 中保存的就不是当前函数了,而是持有当前函数的 Watcher 对象。
| 1 | import Dep from "./dep"; | 
Watcher 的作用就是将正在执行的函数通过 Watcher 包装后保存到 Dep.target 中,然后调用传进来的函数,此时触发对象属性的 get 函数,会收集当前 Watcher 。
如果未来修改对象属性的值,会触发对象属性的 set ,接着就会调用之前收集到的 Watcher 对象,通过 Watcher 对象的 uptate 方法,来调用最初执行的函数。
响应式数据
回到我们之前没写完的 defineReactive 函数,按照上边的思路,我们来补全一下。
| 1 | import Dep from "./dep"; | 
Observer 对象
我们再写一个 Observer 方法,把对象的全部属性都变成响应式的。
| 1 | export class Observer { | 
我们提供一个 observe 方法来负责创建 Observer 对象。
| 1 | export function observe(value) { | 
测试
将上边的方法引入到文章最开头的例子,来执行一下:
| 1 | import { observe } from "./reactive"; | 
此时就会输出两次了~
| 1 | 收到 hello, world | 
说明我们的响应式系统成功了。
总

先从整体理解了响应式系统的整个流程:
每个属性有一个 subs 数组,Watcher 会持有当前执行的函数,当读取属性的时候触发 get ,将当前 Watcher 保存到 subs 数组中,当属性值修改的时候,再通过 subs 数组中的 Watcher 对象执行之前保存的函数。
当然还有亿点点细节需要完善,后边的文章会继续。
