源码笔记(五):beforeMount 阶段
接上文,在触发生命周期钩子 created 后,执行:
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}该句执行完后,_init 方法(位置在 src/core/instance/init.js)执行结束。
$mount 简述
判断是否有 $options.el,如果有就直接执行:vm.$mount(vm.$options.el)。在前面已经提到 $mount 方法与平台相关,所以在本例会执行 entry-runtime-with-compiler.js 中的 $mount。
$mount 的主要功能是根据 el 或者 options 里的 template,通过编译器编译成 render 和 staticRenderFns。
构建 render
$mount 里先取得 el 对应的 dom 节点,然后判断 options(即 this.$options) 里有没有 render 方法,有 render 就可以直接跳过取 template 的过程了;没有 render 则判断 options 有没有 template,没有提供 template 就用 getOuterHTML(el) 得一个 template。
compileToFunctions
得到 template 后,执行:
var ref = compileToFunctions(
template,
{
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines: shouldDecodeNewlines,
shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments,
},
this,
);compileToFunctions 是柯里化函数 createCompileToFunctionFn(compile) 的返回值,主要作用是将 template 转化为 render 函数。
内部先执行 new Function('return 1') 来判断 csp 的配置,然后根据(options.delimiters)是否存在设置 key, 判断缓存后执行:
compile
var compiled = compile(template, options);compile 在 createCompiler 里定义,方法里先处理合并处理了 compileToFunctions 传入的 options 和 baseOptions(来自平台相关) 相关配置(定义了 warn,合并了 modules,directives)得到 finalOptions,然后执行:
baseCompile
var compiled = baseCompile(template.trim(), finalOptions);baseCompile 方法里执行 3 个步骤:
parse模板解析为astoptimize标记静态节点,分别递归调用markStatic$1和markStaticRoots方法得到静态节点标志挂载在ast对象下的的static和staticRoot属性。其中static代表该节点是普通静态节点,staticRoot代表是可以优化的静态节点,他来自于:jsif (node.static && node.children.length && !(node.children.length === 1 && node.children[0].type === 3)) { node.staticRoot = true; return; } else { node.staticRoot = false; }表明如果
node.static为真(即静态节点)且他不仅仅只有一个文本子元素为的时候node.staticRoot就为真,即用来优化。如果只有一个子文本元素就无需优化,优化反而增加成本。generate将AST转换成渲染函数,其中如果有标记node.staticRoot为真,则将staticRenderFns数组增加一个静态的render。
最终 baseCompile 返回:
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns,
};继而 compile 返回:
return {
ast: {type: 1, tag: "div", attrsList: Array(1), attrsMap: {…}, rawAttrsMap: {…}, …}
render: "with(this){return _c('div',{attrs:{"id":"main"}..."
staticRenderFns: []
errors: []
tips: []
};处理编译错误后,对 render 和 staticRenderFns 进行函数包装( createFunction )并缓存结果,这样在后续如果再次解析到相同的模板可以直接读缓存。
继而 compileToFunctions 返回:
return {
render: () => {
with (this) {
return 'xxx';
}
},
staticRenderFns: [],
};到此 template->render 的编译过程结束。
执行 runtime 里的原 $mount 及 mountComponent
回到 vm.$mount 方法里继续执行,将 render 和 staticRenderFns 赋到 vm.$options 上后,执行:
return mount.call(this, el, hydrating);即执行被覆盖的之前的 $mount (/runtime/index 中定义),内部执行:
mountComponent(this, el, hydrating);在 mountComponent (core/instance/lifecycle 中定义)里先将原始的真实 el 赋给 vm.$el,判断 $options.render 是否存在进行相关报错处理。
触发 beforeMount 钩子
callHook(vm, 'beforeMount');执行生命周期钩子 beforeMount,打印 vue beforeMount。
本章小结
- 本章介绍了
vue执行的beforeMount阶段; - 该阶段主要执行与平台相关的
$mount,主要生成render函数; $mount经过compileToFunctions -> compile -> baseCompile得到render,其中baseCompile的执行会经过 3 个阶段;- 在
$mount最后执行原$mount(平台相关),方法里执行mountComponent开始进入构建vnode阶段。