vue中指令的编译过程

首先生成的 ast 会加上一些属性,每个 element 元素可以看作是一个 ast 对象,整颗 DOM 树可以看作是包含依赖关系的 ast 对象。
v-if 指令
源码在 processIf(element) 函数里面处理
<span v-if="msg">6</span>(msg)?_c('span',[_v("6")]):_e()let ast = [{type: 1,tag: 'span',attrsList: [],attrsMap: { 'v-if': 'msg' },rawAttrsMap: {},parent: {type: 1,tag: 'div',attrsList: [],attrsMap: {},rawAttrsMap: {},parent: undefined,children: [],plain: true,static: false,staticRoot: false},children: [],if: 'msg',ifConditions: [ { exp: 'msg', block: el } ],plain: true,static: false,staticRoot: false,pre: undefined,ifProcessed: true}]
先 processIf 再 genIf,生成 render:
function genIf (el,state,altGen,altEmpty) {el.ifProcessed = true; // avoid recursionreturn genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)}function genIfConditions (conditions, // [ { exp: 'msg', block: el } ]state,altGen, // 无altEmpty // 无) {if (!conditions.length) {return altEmpty || '_e()'}var condition = conditions.shift();if (condition.exp) {// 主要看这里,通过三元运算符?和:拼接字符串return ("(" + (condition.exp) + ")?" + (genElement(el, state)) + ":" + (genIfConditions(conditions, state, altGen, altEmpty)))} else {return ("" + (genTernaryExp(condition.block)))}}
v-for 指令
源码在 processFor(element) 函数里面处理
_l((list), function(item){return _c('span',[_v("6")])})<span v-for="item in list">6</span>let ast = [{type: 1,tag: 'span',attrsList: [],attrsMap: { 'v-for': 'item in list' },rawAttrsMap: {},parent: {type: 1,tag: 'div',attrsList: [],attrsMap: {},rawAttrsMap: {},parent: undefined,children: [],plain: true,static: false,staticRoot: false},children: [],for: 'list',alias: 'item',plain: true,static: false,staticRoot: false,pre: undefined,forProcessed: true}]
我们简单想象一下 for 指令需要包装成一个函数,方便之后的循环遍历,比如下面这样:
function _l(list, callback) { list.forEach(item => callback(item))  }具体的生成过程:
function genFor (el,state,altGen, // 无altHelper // 无) {var exp = el.for;var alias = el.alias;var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : '';var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : '';// 主要看这里,包装成一个function函数el.forProcessed = true; // avoid recursionreturn ('_l') + "((" + exp + ")," +"function(" + alias + iterator1 + iterator2 + "){" +"return " + ((genElement)(el, state)) +'})'}
v-once 指令
源码在 processOnce(element) 函数里面处理
<div>11<span v-once="msg">6</span></div>render: `with(this){return _c('div',[_v("11"),_m(0)])}`,staticRenderFns: [ `with(this){return _c('span',[_v("6")])}` ],let ast = [{type: 1,tag: 'span',attrsList: [],attrsMap: { 'v-once': '' },rawAttrsMap: {},parent: {type: 1,tag: 'div',attrsList: [],attrsMap: {},rawAttrsMap: {},parent: undefined,children: [],plain: true,static: false,staticRoot: false},children: [],once: true,plain: true,static: false,staticInFor: false,staticRoot: false,pre: undefined,onceProcessed: true,staticProcessed: true}]
v-once 只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。
function genOnce (el, state) {el.onceProcessed = true;return genStatic(el, state)}function genStatic (el, state) {el.staticProcessed = true;var originalPreState = state.pre;state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}"));state.pre = originalPreState;return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")")}
生成 render 函数的目的就是为了当绑定的变量值发生改变的时候,可以重新执行 render 函数,最后生成真实的 DOM,其中_c 和 _v 等等这些辅助函数就是处理真实 DOM 的函数,关于 render 函数生成 DOM 我们之后再分析。
评论
