Vue原理:渲染流程入口

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

渲染流程是Vue的核心之一,掌握渲染渲染流程更有助于我们更好的理解和使用Vue

渲染的入口在_init函数中(省略无关代码)

Vue.prototype._init = function (options) {
    /**
     * 渲染流程
     *  如果用户传递了el属性,代表el将会作为容器渲染页面
     */
    if (vm.$options.el) {
      vm.$mount(el);
    }
  };
复制代码

通过调用可以看到$mount方法也是一个挂载在vue.prototype上的函数,可以将函数初始化

/**
 * 渲染流程
 */
Vue.prototype.$mount = function (_el) {
  // todo ...
};
复制代码

Vue中可以以多种方式传递渲染需要的模板,可以通过templaterender()DOM(el中的内容)

存在多种方式那么必然会存在一个优先级的问题,否则将会乱成一团,三者的优先级依次降低为:rendertemplateDOM

为什么优先级最高的是render函数:出于性能考虑。因为无论使用后两则哪种方式传递,最终都会将其编译、转换成render函数,例如我们在常用的.vue文件中编写代码,最终也会通过vue-loader处理为render函数,因为在最终生成的线上代码中是不包含转换的这部分代码的,编译转换的过程是十分耗时的一项工作

$mount大致流程:

  1. 判断是否传递render函数,传递则直接使用
  2. 没有传递render函数则获取到template,将其编译转换为render函数
  3. 点用render函数进行渲染

将上述的逻辑用代码实现

Vue.prototype.$mount = function (_el) {
  const vm = this;
  const ops = this.$options;
  const el = document.querySelector(_el);

  /**
   * Vue可以有多种方式传递渲染模版:
   * 在Vue进入渲染时,
   */
  if (!ops.render) {
    // 不存在render函数, 需要将其进行编译
    let template = ops.template;

    if (!template && el) {
      // 没有传递模版 且传递了el
      template = el.outerHTML; // 将el的内容作为模版
    }
    
    // 将template 转换为 render
    const render = compileToFunction(template);
    ops.render = render;
  }
  
  // 传递了render则直接使用,若是没有传递则通过compileToFunction生成render函数
  options.render()
};
复制代码

大致的流程如上述代码,接下来的重心便是如何将其进行编译,核心在于实现compileToFunction函数。在开始之前先看看期望是怎样的

现在我们有这么一段HTML

<div id="app">
  <p>{{name}}</p>
  <p>{{age}}</p>
</div>
复制代码

将其编译成render函数

function render() {
  with(this) {
    return _c('div', {
      attrs: {
        "id": "app"
      }
    }, [_c('p', [_v(_s(name))]), _c('p', [_v(_s(age))])])
  }
}
复制代码

render函数返回的就是我们常说的虚拟DOM

compileToFunction函数初始化

function compileToFunction(template) {
  return function render() {}
}
复制代码

猜你喜欢

转载自juejin.im/post/7030983218522259486