Vue原理:渲染Watcher

2,134 阅读1分钟

这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

Vue初次渲染过程中有一个比较关键的概念:渲染Watcher,其主要是负责视图的更新,是之后响应式中data和视图之间的核心枢纽

Vue中存在三种Watcher,分别是Render Watcher(渲染Watcher)、Computed WatcherComputed)和User Watcher

部分接触Vue不久的小伙伴可能会比较惊讶为何会将ComputedRender Watcheruser Watcher放在一起讨论,因为他们三者在Vue的底层中都是基于Watcher实现的,会通过传递不同的参数来进行区分

首先回顾前文处理初次渲染mountComponent函数做的事情

export function mountComponent(vm, el) {
  const options = vm.$options;
  vm.$el = el; 
  vm._update(vm._render());
}

Vue中每个组件都会创建一个对应的渲染Watcher,当数据变化时视图需要及时响应,将最新的数据渲染至页面上,即响应式

需要对上述代码进行改造

export function mountComponent(vm, el) {
  const options = vm.$options;
  vm.$el = el; // 真实的DOM元素

  /**
   * Watcher 就是用来渲染的
   * vm._render 通过解析的render方法 渲染出虚拟dom
   * vm._update 通过虚拟dom 创建 真实dom
   */

  // 无论渲染还是更新 都会执行
  let updateComponent = () => {
    // vm._render() 返回的是虚拟DOM
    vm._update(vm._render());
  };

  // 渲染 watcher, 每一个组件都有一个watcher
  // true 表示他是一个渲染watcher
  new Watcher(vm, updateComponent, () => {}, true);
}

需要注意:

  1. 为何要将el挂载到vm.$el中,在日常开发中,我们将Vue实例打印出来会发现都有这么一个属性,它的作用是用来记录真实的DOM节点,用于后续存放根据虚拟DOM创建的真实DOM
  2. updateComponent函数在首次出创建渲染Watcher是会调用一次,即初次渲染时。若是当data改变时会触发试图的更新,也需要调用此函数进行处理
  3. 需要注意new Watcher时传递的4个参数,不同的参数可用于区分三种Watcher

mountComponent函数中_render已经实现,_updateWatcher暂未实现,接下来先将Watcher初始化,在此只需要掌握大致的流程和函数的作用即可,具体实现后文再实现

class Watcher {
  constructor(vm, exprOrFn, callback, options) {
    this.vm = vm;
    this.callback = callback;
    this.options = options;
    this.getter = exprOrFn;
  }
	
  // 存储Watcher
  get() {}
	
  // 批量更新
  update() {}
	
  // 执行存储操作
  run() {}
	
  // 添加被观察者
  addDep() {}
}

_update实现,次函数作为入口函数的存在,其中并不会做太多的事情,具体通过虚拟DOM创建真实DOM的流程全封装至patch函数中,热门的diff比较就在其中

Vue.prototype._update = function (vnode) {
  const vm = this;

  // 通过虚拟节点 渲染出来真实dom
  // 需要用虚拟节点创建出来真实节点 替换掉 真实的 $el
  vm.$el = patch(vm.$el, vnode);
};