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 recursion
return 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 recursion
return ('_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 我们之后再分析。
评论