render 函数是怎么来的(深入浅出 Vue 中的模板编译)
new Vue({
render: h => h(App)
})
这个大家都熟悉,调用 render 就会得到传入的模板(
.vue
文件)对应的虚拟 DOM,那么这个 render 是哪来的呢?它是怎么把 .vue
文件转成浏览器可识别的代码的呢?render 函数是怎么来的有两种方式
- 第一种就是经过模板编译生成 render 函数
- 第二种是我们自己在组件里定义了 render 函数,这种会跳过模板编译的过程
认识模板编译 我们知道
这个是模板,不是真实的 HTML,浏览器是不认识模板的,所以我们需要把它编译成浏览器认识的原生的 HTML这一块的主要流程就是
- 提取出模板中的原生 HTML 和非原生 HTML,比如绑定的属性、事件、指令等等
- 经过一些处理生成 render 函数
- render 函数再将模板内容生成对应的 vnode
- 再经过 patch 过程( Diff )得到要渲染到视图中的 vnode
- 最后根据 vnode 创建真实的 DOM 节点,也就是原生 HTML 插入到视图中,完成渲染
那它是怎么编译,最终生成 render 函数的呢?
模板编译详解——源码 baseCompile()
这就是模板编译的入口函数,它接收两个参数
template
:就是要转换的模板字符串options
:就是转换时需要的参数
- 模板解析:通过正则等方式提取出
模板里的标签元素、属性、变量等信息,并解析成抽象语法树
AST
- 优化:遍历
AST
找出其中的静态节点和静态根节点,并添加标记 - 代码生成:根据
AST
生成渲染函数render
baseCompile
源码中是在哪里调用的源码地址:
src/complier/index.js - 11行
export const createCompiler = createCompilerCreator(function baseCompile (
template: string, // 就是要转换的模板字符串
options: CompilerOptions //就是转换时需要的参数
): CompiledResult {
// 1. 进行模板解析,并将结果保存为 AST
const ast = parse(template.trim(), options)// 没有禁用静态优化的话
if (options.optimize !== false) {
// 2. 就遍历 AST,并找出静态节点并标记
optimize(ast, options)
}
// 3. 生成渲染函数
const code = generate(ast, options)
return {
ast,
render: code.render, // 返回渲染函数 render
staticRenderFns: code.staticRenderFns
}
})
就这么几行代码,三步,调用了三个方法很清晰
我们先看一下最后 return 出去的是个啥,再来深入上面这三步分别调用的方法源码,也好更清楚的知道这三步分别是要做哪些处理
编译结果
比如有这样的模板
{{name}}
打印一下编译后的结果,也就是上面源码 return 出去的结果,看看是啥
{
ast: {
type: 1,
tag: 'div',
attrsList: [ { name: 'id', value: 'app' } ],
attrsMap: { id: 'app' },
rawAttrsMap: {},
parent: undefined,
children: [
{
type: 2,
expression: '_s(name)',
tokens: [ { '@binding': 'name' } ],
text: '{{name}}',
static: false
}
],
plain: false,
attrs: [ { name: 'id', value: '"app"', dynamic: undefined } ],
static: false,
staticRoot: false
},
render: `with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(name))])}`,
staticRenderFns: [],
errors: [],
tips: []
}
看不明白也没有关系,注意看上面提到的三步都干了啥
ast
字段,就是第一步生成的static
字段,就是标记,是在第二步中根据ast
里的type
加上去的render
字段,就是第三步生成的
1. parse()
源码地址:
src/complier/parser/index.js - 79行
就是这个方法就是解析器的主函数,就是它通过正则等方法提取出
模板字符串里所有的 tag
、props
、children
信息,生成一个对应结构的 ast 对象parse
接收两个参数template
:就是要转换的模板字符串options
:就是转换时需要的参数。它包含有四个钩子函数,就是用来把parseHTML
解析出来的字符串提取出来,并生成对应的AST
调用
parseHTML
函数对模板字符串进行解析- 解析到开始标签、结束标签、文本、注释分别进行不同的处理
- 解析过程中遇到文本信息就调用文本解析器
parseText
函数进行文本解析 - 解析过程中遇到包含过滤器,就调用过滤器解析器
parseFilters
函数进行解析
这个地方的源码实在是太长了,有大几百行代码,我就只贴个大概吧,有兴趣的自己去看一下
export function parse (
template: string, // 要转换的模板字符串
options: CompilerOptions // 转换时需要的参数
): ASTElement | void {
parseHTML(template, {
warn,
expectHTML: options.expectHTML,
isUnaryTag: options.isUnaryTag,
canBeLeftOpenTag: options.canBeLeftOpenTag,
shouldDecodeNewlines: options.shouldDecodeNewlines,
shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
shouldKeepComment: options.comments,
outputSourceRange: options.outputSourceRange,
// 解析到开始标签时调用,如
start (tag, attrs, unary, start, end) {
// unary 是否是自闭合标签,如
文章图片
如图可以知道,如果有 template
,就不会管 el
了,所以 template 比 el 的优先级更高,比如
那我们自己写了 render 呢?
{{ name }}
这个代码执行后页面渲染出来只有 好好学习,天天向上
可以得出 render 函数的优先级更高
因为不管是 el
挂载的,还是 emplate
最后都会被编译成 render
函数,而如果已经有了 render
函数了,就跳过前面的编译了
这一点在源码里也有体现
在源码中找到答案:dist/vue.js - 11927行
Vue.prototype.$mount = function ( el, hydrating ) {
el = el && query(el);
var options = this.$options;
// 如果没有 render
if (!options.render) {
var template = options.template;
// 再判断,如果有 template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template);
}
} else if (template.nodeType) {
template = template.innerHTML;
} else {
return this
}
// 再判断,如果有 el
} else if (el) {
template = getOuterHTML(el);
}
}
return mount.call(this, el, hydrating)
};
2. 更灵活的写法
比如说我们需要写很多 if 判断的时候
不知道你有没有写过类似上面这样的代码呢?
我们换一种方式来写出和上面一模一样的代码看看,直接写 render
搞定!就这!就这?
没错,就这!
或者下面这样,多次调用的时候就很方便
补充
如果想知道更多格式的模板编译出来是什么样的,可以这样
Vue2 的模板编译可以安装 vue-template-compiler
Vue3 的模板编译可以点这里
然后自行测试
另外在 Vue3 里模板编译部分有一些修改,可以点击下面链接,看下 深入浅出虚拟 DOM 和 Diff 算法,里面有介绍
往期精彩
- 深入浅出虚拟 DOM 和 Diff 算法,及 Vue2 与 Vue3 中的区别
- Vue3的7种和Vue2的12种组件通信,值得收藏
- 最新的 Vue3.2 都更新了些什么了解一下
- JavaScript进阶知识点
- 前端异常监控和容灾
- 20分钟助你拿下HTTP和HTTPS,巩固你的HTTP知识体系
结语
【render 函数是怎么来的(深入浅出 Vue 中的模板编译)】如果本文对你有一丁点帮助,点个赞支持一下吧,感谢感谢
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 热闹中的孤独
- 我要做大厨
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 爱就是希望你好好活着
- 太平之莲
- 知识
- 叙述作文
- 时间老了
- 清明,是追思、是传承、是感恩。
- 我错了,余生不再打扰