目前工作中大概有 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
对象执行之前保存的函数。
当然还有亿点点细节需要完善,后边的文章会继续。