多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
模板到真实DOM渲染的过程,**中间有一个环节是把模板编译成render函数**,这个过程我们把它称作编译。 虽然可以直接为组件编写render函数,但是编写template模板更加直观,也符合开发习惯。 Vue提供了2个版本,一个是Runtime+Compiler的,一个是Runtime only的,前者包含编译代表的,可以把编译过程放在运行时做,后者是不包含编译代码的,需要借助webpack的vue-loader实现把模板编译成render函数。 对编译的过程的了解,会让我们对Vue的指令、内置组件等有更好的理解。 ## 编译入口 编译入口找到了,执行如下几个逻辑 1. 解析模板字符串生成AST ~~~ const ast = parse(template.trim(), options) ~~~ 2. 优化语法树 ~~~ optimize(ast, options) ~~~ 3. 生成代码 ~~~ const code = generate(ast, options) ~~~ ### 总结 baseOptions:配置,不同平台下都会有编译过程,因此依赖的baseOptions会有所不同。 Vue.js利用了函数柯里化的技巧很好的实现了baseOptions参数的保留。 同样,vue也是利用函数柯里化把基础的编译过程函数抽出来,把真正编译的过程和其他逻辑如编译配置处理,缓存处理等剥离开。 ## parse 编译过程首先就是对模板做解析,生成AST,它是一种抽象语法树,是对源代码的抽象语法结构的树状表现形式。如babel编译ES6的代码都会生成AST。 这个过程是比较复杂的,它会用到**大量的正则表达式对字符串解析**。 ~~~ ast = { 'type': 1, 'tag': 'ul', 'attrsList': [], 'attrsMap': { ':class': 'bindCls', 'class': 'list', 'v-if': 'isShow' }, 'if': 'isShow', 'ifConditions': [{ 'exp': 'isShow', 'block': // ul ast element }], 'parent': undefined, 'plain': false, 'staticClass': 'list', 'classBinding': 'bindCls', 'children': [{ 'type': 1, 'tag': 'li', 'attrsList': [{ 'name': '@click', 'value': 'clickItem(index)' }], 'attrsMap': { '@click': 'clickItem(index)', 'v-for': '(item,index) in data' }, 'parent': // ul ast element 'plain': false, 'events': { 'click': { 'value': 'clickItem(index)' } }, 'hasBindings': true, 'for': 'data', 'alias': 'item', 'iterator1': 'index', 'children': [ 'type': 2, 'expression': '_s(item)+":"+_s(index)' 'text': '{{item}}:{{index}}', 'tokens': [ {'@binding':'item'}, ':', {'@binding':'index'} ] ] }] } ~~~ 生成的AST是一个树状结构,每一个节点都是一个AST element。自身属性,还维护了父子关系。 ### 总结 parse的目标是把template模板字符串转换成AST树,它是一种用JavaScript对象的形式来描述整个模板。 整个parse的过程是利用正则表达式顺序解析模板,当解析到开始标签,闭合标签,文本的时候,都会分别执行对应的回调函数,来达到构造AST树的目的。 ## optimize 模板template经过parse过程后,会输出生成的AST树。接下来需要优化。 为什么优化:Vue是数据驱动,是响应式的,**但模板中并不是所有的数据都是响应式的**,也有很大数据是首次**渲染后就永远不会 变化的,那么这部分数据生成的DOM也不会变化**。我们可以在patch的过程跳过对它们的对比。 我们在编译阶段,可以把一些**AST节点优化成静态节点**。 optimize:**标记静态节点,标记静态根**。 ### 标记静态节点 isStatic:是否是静态的判断,表达式则非静态,纯文本则静态。 * v-pre指令是静态。 或同时满足一些条件 1. 没有使用v-if,v-for,没有使用其他指令(不包括v-once),非2. 非内置组件 3. 是平台保留的标签 4. 非带有v-for的template标签的直接子节点 5. 节点的所有属性的key都满足静态key ### 标记静态根 * 本身是静态节点 * 用于children * children不能只是一个文本节点 ### 总结 optimize的过程,就是**深度遍历这个AST树,去检测它的每一颗子树是不是静态节点,如果是静态节点,则他们生成的DOM永远不需要改变,这对运行时对模板的更新起到极大的优化作用。** static staticRoot ## codegen 编译的最后一步就是把**优化后的AST树转换成可执行的代码**。 ,执行`const code = generate(ast, options)`,**生成的`render`**代码 `_c`就是执行createElement去创建VNode,`_l`对应renderList,`_v`创建文本VNode **实际就是把render代码串通过new Function的方式转换成可执行的函数,赋值给vm.options.render,这样当组件通过vm._render的时候,就会执行这个render函数**。 ### 总结 `ast->code`,编译后生成的代码就是在运行时执行的代码。