上周末,一直在研究怎么做一个简简单单的 Vue,名字叫 micro-Vue

基础

Vue 的响应式原理是基于 Object.defineProperty() 这个API。

构建

源码中的 vue/src/core 文件是核心代码,Vue 实例在 vue/src/core/instance/index.js 中初始化,然后再初始化它的生命周期、事件、渲染、beforeCreated 钩子函数、状态等。我们接着看初始化状态时,它会依次对 props、methods、data、computed、watch 进行数据绑定。
本次研究的是 data 部分的数据处理。

  1. Complier
    PS:我还没有看完 virtual DOM,所以只能解析真实DOM
    解析渲染时,会创建一个 Watcher 实例。
  2. 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个,没有仔细研究。
    • 我的 Watcher 做了什么事?
      • get
        创造实例后,也是先将 Dep.target 赋值为当前的 Watcher,再去获取 data[key] 的值,此时会触发 getter 方法。
      • update
        data[key] 重新赋值时会触发实例的 update 函数,进而触发回调函数,对 DOM 进行数据更新。
        oh, so easy.我就是没有做清除的功能,以及 computed、watch 函数对应的判断。
  3. Dep
    依赖收集
    • 源码的 Dep 做了什么事?
      • constructor
        拥有 id、subs(依赖收集器)
      • addSub
        添加监听器 Watcher,push 给收集器。
      • removeSub
        删除监听器。
      • depend
        调用 Watcher 的 addDep(这个会比较严格,判断是否重复再添加 Watcher。
      • notify
        依赖响应时,如果是异步的需要给收集器重新排序。然后调用 Watcher 的 update 方法。
    • 我的 Dep 呢?
      • addDep
        添加监听器 Watcher
      • notify
        依赖响应时,调用 Watcher 的 update 方法。
  4. Observer
    对 data 中每个数据创建自己的 Dep 实例,再 Object.defineProperty() 反应。
    • get
      先判断 Dep.target 是否有内容,有内容了代表它被监控了,将 Dep.target 的内容 push 给 Dep 实例的 subs。
    • set
      数据变化时,判断 Dep 实例 的 subs 是否有东西,再依次响应。

我的项目是从 initData 开始,然后对 micro-Vue 的 data 每个数据都绑定 get、set方法。再接着解析文档中的 DOM,对需要响应的数据添加一个 Watcher,此时会触发数据中的 get 方法,从而添加了依赖,在下次数据发生变动时,set 会触发依赖的事件。
功能在我的 github 里有比较简略的介绍,Array 数据类型我也没有进行判断,稍后会再补充。