团队分享之VUE模板编译原理
写在前面的话
只是团队的一种分享模式,不在于技术的高低,只在于这次分享的意义,也是大家坐在一起创聊的一种方式,在于活跃气氛,不外乎工作之余的一种娱乐方式
VUE模板构建版本
Vue提供了几个构建版本
- vue.js :完整版本,包含了模板编译的能力
- vue.runtime.js: 运行时版本,不提供模板编译能力,需要通过vue-loader 进行提前编译。
简单来说,就是如果你用了 vue-loader ,就可以使用 vue.runtime.min.js,将模板编译的过程交过 vue-loader,如果你是在浏览器中直接通过 script 标签引入 Vue,需要使用 vue.min.js,运行的时候编译模板。
编译入口
在挂载阶段,判断没有render的时候会进行模板编译转成render函数
compileToFunctions
parse方法:解析html,转化为 ast
Vue 模板:
<div>
<h2 v-if="message">{{message}}</h2>
<button @click="showName">showName</button>
</div>
经过parse得到下面的
parse方法
parseHtml方法分析
- 第一部分解析注释
- 继续解析
parseStartTag方法分析
下述代码为简化后的 parseHTML,while 循环中每次截取一段 html 文本,然后通过正则判断文本的类型进行处理,这就类似于编译原理中常用的有限状态机。每次拿到 "<" 字符前后的文本,"<" 字符前的就当做文本处理,"<" 字符后的通过正则判断,可推算出有限的几种状态
startTagMatch变成这样
optimize方法
优化ast,标记静态节点,过程并不是很复杂,主要是对AST标记一些静态节点以及静态根节点,这样这些静态根节点就不需要参与第二次的页面渲染了,大大提升了渲染效率。
- markStatic
- 分析isStatic方法
Node.Type 节点分析
如果此时的node是一个表达式的话,直接就标记成非静态节点;如果是一个普通文本的话就直接标记为静态节点;如果既不是表达式也不是文本节点,就说明这是一个标签,有子节点,就根据这个标签上的一些一些属性或者标签名等判断是不是一个静态节点。回到markStatic,进一步判断是不是一个静态节点。
接下来就是递归了,遍历children属性,为每一个子节点都调用markStatic方法,如果子节点不是static,那父节点也肯定不是static。下面一个if是v-if v-else的特殊处理。
markStaticRoots方法
注释的英文大概意思是让文本节点标记为静态的成本高于此举动带来的性能提升,为false又将该节点设置为非静态节点
generate方法
将 ast 转化为可执行代码
最终的code变成这样
这个_c就是虚拟 DOM 中的 createElement 方法
genElement方法
genChildren方法
getNormalizationType方法
确定子数组需要的规范化。
- 不需要标准化
- 需要简单的规范化(可能是1级深嵌套数组)
- 需要完全标准化
genNode方法
根据不同的type类型,模板中子元素深入到最后肯定是剩下文本。这种文本有3种情况:注释文本、插值语法、纯文本节点。所以这边会根据文本的不同情况生成不同的代码块
之后将生成的子元素代码块插入到事先定义好的模板中,最后再将这个完整的代码块(render函数)返回出去