Vue2组件更新流程(todo)

status: Idea Created time: March 15, 2024 6:52 PM

Vue2中有三个非常重要的类:Watcher、Observer和Dep。

  • Watcher:Vue2响应式中非常重要的一个类,管理依赖和状态的管理,watcher.update负责依赖的更新。

  • Observer:响应式对象,vm.$options.data会被其转换成响应式。本质上是通过Object.defineProperty(对于对象)和更改原型方法(对于数组)实现。

  • Dep:用于存储Watcher的一个结构,每个状态都有一个对应的dep。

整体流程:

  1. 在初始化时,vue2会对Component、watch和computed等创建Watcher实例(watcher),并通过new Observervm.$options.data转换响应式对象(初始化datainitData函数中完成操作),同时每个状态都有一个对应的dep对象,用于存放该状态的所有依赖(watcher)。

  2. 以组件为例,这里需要了解一个前提知识,vue无论是模板语法还是渲染函数,最后的目的都是为了生成vnode,而模板语法其实会被vue通过模板编译操作转换成渲染函数(render函数),render函数执行的返回值就是vnode,然后通过patch新旧vnode来更新DOM,从而达到更新视图的结果。

  3. 在初始渲染组件时,vue会为组件创建一个watcher对象(执行new Watcher),在Watcher实例化的过程中,会将Dep.target指向watcher自身,之后执行一次组件render函数,而在render函数内可能会使用某些响应式状态,因此在执行render函数中就自动会触发这些状态的getter函数,在getter函数中就会将Dep.target(即当前组件watcher对象)添加到observer对应的dep

  4. 当更新状态时,自然会触发状态的setter函数,此时就会通知该状态dep中所有的watcher,执行watcher.update()进行更新,在这个例子中,会重新执行render函数和patch函数进行DOM更新。

具体代码:

  1. 首先,在组件初始化时,为组件创建一个watcher,每一个vue组件都有一个对应的watcher对象。

function mountComponent (
    vm,
    el,
    hydrating
  ) {
    vm.$el = el;
    if (!vm.$options.render) {
      vm.$options.render = createEmptyVNode;
    }
    callHook(vm, 'beforeMount');

    var updateComponent;
    if (config.performance && mark) {
      updateComponent = function () {
        var vnode = vm._render(); // 执行render函数获得vnode,如果是模板语法,vue内部也会通过模板编译来将其转换成render函数
        vm._update(vnode, hydrating); // vm._update函数内部其实也是通过patch来进行比对新旧vnode,从而进行更新
      };
    } else {
      updateComponent = function () {
        vm._update(vm._render(), hydrating);
      };
    }
    // 每个vue组件都有对应的一个watcher对象
    // 重点在这个实例化Watcher语句,参数传递了vm和updateComponent
    new Watcher(vm, updateComponent, noop, {
      before: function before () {
        if (vm._isMounted && !vm._isDestroyed) {
          callHook(vm, 'beforeUpdate');
        }
      }
    }, true /* isRenderWatcher */);
		// ...
    return vm
  }

之后主要看Watcher实例化的过程

也就是说,在初始化vue组件的watcher时,首先会将Dep.target指向watcher自身,然后执行一次vm.render进行渲染,同时也是让组件render引用的状态来收集watcher自身。

接下来看看vm.$options.data是如何变成