这是我参与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
中可以以多种方式传递渲染需要的模板,可以通过template
、render()
和DOM
(el中的内容)
存在多种方式那么必然会存在一个优先级的问题,否则将会乱成一团,三者的优先级依次降低为:render
、template
、DOM
为什么优先级最高的是render
函数:出于性能考虑。因为无论使用后两则哪种方式传递,最终都会将其编译、转换成render
函数,例如我们在常用的.vue
文件中编写代码,最终也会通过vue-loader
处理为render
函数,因为在最终生成的线上代码中是不包含转换的这部分代码的,编译转换的过程是十分耗时的一项工作
$mount
大致流程:
- 判断是否传递
render
函数,传递则直接使用 - 没有传递
render
函数则获取到template
,将其编译转换为render
函数 - 点用
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() {}
}
复制代码