当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。 比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。

模块文件

  • index.js
    • 主要用于将对象转换为响应式对象(Object.defineProperty)
    • Observer、defineReactive
  • watcher.js
    • 观察者
  • dep.js
    • 依赖,用来收集观察者
  • array.js
    • 劫持数组方法,当修改数组时会触发更新
  • traverse.js
    • 递归对象,进行对象的深度监听,进行依赖收集,用于watch的deep选项为true时,还有createElement中
  • scheduler.js
    • 这是个负责调度的方法,更合理的执行watcher更新

流程顺序

new Vue()

initRender(vm)

  • Vue.prototype._render

    initState(vm)

  • new Observe

    • this.dep = new Dep()
    • 数组:劫持数组内置方法
    • 对象:defineReactive方法:修改defineProperty的get和set
      • get: dep.depend() 收集watcher
      • set: dep.notify() 通知watcher更新

        vm.$mount()

        根组件挂载
  • new Watcher(vm, expOrFn)

    • construction
      • 将expOrFn转换为function。expOrFn有可能是表达式,也可能是function
        • this.getter = expOrFn;
        • this.getter = parsePath(expOrFn);
      • Watch.prototype.get()
        • pushTarget(this)
          • targetStack.push(target);
          • Dep.target = target;
        • let value = this.getter.call(vm, vm); 触发Object.defineProperty的get方法
          • dep.depend(); 收集watcher
  • 3种watcher

    • 当是渲染watcher时,expOrFn是updateComponent,即重新渲染执行render(_update)
    • 当是计算watcher时,expOrFn是计算属性的计算方法
    • 当是侦听器watcher时,expOrFn是watch属性的名字,this.cb就是watch的handler属性

      vm._update(vm._render(), hydrating)

  • 这里的expOrFn传的是updateComponent,包着vm._update

  • render 是返回vnode的函数,即产生虚拟dom

    vm.patch

  • createElm()

  • createComponent(vnode)
  • vnode.data.hook.init()

    child.$mount(vm.$mount)

图例

image.png
image.png

  1. export default {
  2. name: 'app',
  3. components: {
  4. HelloWorld,
  5. HelloWorld1
  6. },
  7. mounted () {
  8. console.log(this)
  9. console.log(this.$data)
  10. // 第一个读取组件内data触发依赖收集的,就是组件的视图template中的渲染
  11. // 一个组件内只存在一个render函数,所以如果在template中使用到的变量,都会收集到render-watcher
  12. console.log(this.test.__ob__.dep.subs[0] === this.a.__ob__.dep.subs[0])
  13. // 这个render-watcher 是同一个wather对象
  14. },
  15. computed: {
  16. testComputed () {
  17. return this.test.msg + 1
  18. },
  19. testComputed2 () {
  20. return this.test.msg + 2
  21. }
  22. },
  23. data () {
  24. return {
  25. test: {
  26. msg: 'Welcome to Your Vue.js App'
  27. },
  28. a: [
  29. 1,
  30. 2,
  31. 3
  32. ]
  33. }
  34. }
  35. }

observer模块浅析 - 图3reactive.png
vue observer模块浅析.ppt

重点关注

  1. 响应式数据和依赖收集的过程
  2. 三种watcher的异同
  3. scheuler调度方法对watcher队列的执行

疑惑点
observer/index.js
// #7981: for accessor properties without setter
image.png
image.png