上周末,一直在研究怎么做一个简简单单的 Vue,名字叫 micro-Vue。
基础
Vue 的响应式原理是基于 Object.defineProperty() 这个API。
构建
源码中的 vue/src/core 文件是核心代码,Vue 实例在 vue/src/core/instance/index.js 中初始化,然后再初始化它的生命周期、事件、渲染、beforeCreated 钩子函数、状态等。我们接着看初始化状态时,它会依次对 props、methods、data、computed、watch 进行数据绑定。
本次研究的是 data 部分的数据处理。
- Complier
PS:我还没有看完 virtual DOM,所以只能解析真实DOM
解析渲染时,会创建一个 Watcher 实例。 - Watcher
Watcher 是要和 Dep 打配合的。- Watcher 构造函数接受5个参数:
- vm:Vue 对象;
- expOrFn:对数据对象的访问触发数据的 getter 方法的函数;
- cb:回调函数,就是数据更新时会发生的事情;
- options:deep、user、lazy、sync、before等,主要给 computed、watch 方法使用的;
- isRenderWatcher:(没看懂做啥子)
- 然后我的 Watcher 根据 Complier 的需要设置为4个参数:
- vm:Vue对象
- data[key]
- cb:回调函数
- arg:回调函数的参数
- 源码的 Watcher 做了什么事?
- constructor
获取 options 的各种值,判断是否 lazy(个人盲猜是 Vue 中 computed 函数的专属 key)再决定是否给 value 赋值。
然后 get() - get
先将 Dep.target 赋值为当前的 Watcher,然后通过 expOrFn 获取 value 值(这个是时时变化的) - addDep
添加新的依赖(Dep) - cleanupDeps
清除依赖 - update
数据变化后,区分 computed、watch、还是 Node,再进行对应的数据响应。 - run
在 update 之后,发现是同步的,开始干活了。 - evaluate
- depend
判断依赖是否重复,给 Dep 实例的收集器添加新的 Watcher。 - teardown
后面这3个,没有仔细研究。
- constructor
- 我的 Watcher 做了什么事?
- get
创造实例后,也是先将 Dep.target 赋值为当前的 Watcher,再去获取 data[key] 的值,此时会触发 getter 方法。 - update
data[key] 重新赋值时会触发实例的 update 函数,进而触发回调函数,对 DOM 进行数据更新。
oh, so easy.我就是没有做清除的功能,以及 computed、watch 函数对应的判断。
- get
- Watcher 构造函数接受5个参数:
- Dep
依赖收集- 源码的 Dep 做了什么事?
- constructor
拥有 id、subs(依赖收集器) - addSub
添加监听器 Watcher,push 给收集器。 - removeSub
删除监听器。 - depend
调用 Watcher 的 addDep(这个会比较严格,判断是否重复再添加 Watcher。 - notify
依赖响应时,如果是异步的需要给收集器重新排序。然后调用 Watcher 的 update 方法。
- constructor
- 我的 Dep 呢?
- addDep
添加监听器 Watcher - notify
依赖响应时,调用 Watcher 的 update 方法。
- addDep
- 源码的 Dep 做了什么事?
- Observer
对 data 中每个数据创建自己的 Dep 实例,再 Object.defineProperty() 反应。- get
先判断 Dep.target 是否有内容,有内容了代表它被监控了,将 Dep.target 的内容 push 给 Dep 实例的 subs。 - set
数据变化时,判断 Dep 实例 的 subs 是否有东西,再依次响应。
- get
我的项目是从 initData 开始,然后对 micro-Vue 的 data 每个数据都绑定 get、set方法。再接着解析文档中的 DOM,对需要响应的数据添加一个 Watcher,此时会触发数据中的 get 方法,从而添加了依赖,在下次数据发生变动时,set 会触发依赖的事件。
功能在我的 github 里有比较简略的介绍,Array 数据类型我也没有进行判断,稍后会再补充。