源码笔记(五):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
模板解析为ast
optimize
标记静态节点,分别递归调用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
阶段。