vue源码版本为2.6.11(cdn地址为: https://lib.baomitu.com/vue/2.6.11/vue.js)
代码生成器,将 AST 对象生成渲染函数中的内容,这个内容可以被称为代码字符串。
<div id="app">
<h1>{{message}}</h1>
</div>
该模板转换成AST后的结构如下:
{
tag: "div",
type: 1,
plain: false,
start: 0,
end: 44,
parent: undefined,
attrs: [{
dynamic: undefined,
end: 13,
name: "id",
start: 5,
value: "app",
}],
attrsList: [{
end: 13,
name: "id",
start: 5,
value: "app"
}],
attrsMap: {
id: "app"
},
rawAttrsMap: {
id: {
name: "id",
value: "app",
start: 5,
end: 13
}
},
children: [{
tag: "h1",
type: 1,
type: 1,
plain: true,
start: 17,
end: 37,
parent: {...},
attrsList: [],
attrsMap: {},
rawAttrsMap: {},
children: [{
end: 32,
expression: "_s(message)",
start: 21,
text: "{{message}}",
type: 2,
tokens: [{
"@binding": "message"
}]
}]
}]
}
根据该AST来生成render后是这样的:
{
render: 'with(this){return _c("div",{attrs:{"id":"app"}},[_c("h1",[_v(_s(message))])])}'
}
生成后的代码字符串中有几个函数调用_c, _v, _s
其中_s是返回参数中的字符串:
var _toString = Object.prototype.toString;
function toString(val) {
return val == null
? ""
: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
? JSON.stringify(val, null, 2)
: String(val);
}
类型 | 创建方法 | 别名 |
元素节点 | createElement | _c |
文本节点 | createTextVNode | _v |
注释节点 | createEmptyVNode | _e |
生成代码字符串代码入口在:
var createCompiler = createCompilerCreator(function baseCompile(
template,
options
) {
// ...
// 将ast生成渲染函数
// code.staticRenderFns为静态渲染函数,code.render为动态渲染函数
// code = {
// render: `with(this){return ${_c(tag, data, children, normalizationType)}}`,
// staticRenderFns: [_c(tag, data, children, normalizationType), ...]
// }
var code = generate(ast, options);
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns,
};
});
generate
var CodegenState = function CodegenState(options) {
this.options = options;
this.warn = options.warn || baseWarn;
this.transforms = pluckModuleFunction(options.modules, "transformCode");
this.dataGenFns = pluckModuleFunction(options.modules, "genData");
this.directives = extend(extend({}, baseDirectives), options.directives);
var isReservedTag = options.isReservedTag || no;
this.maybeComponent = function (el) {
return !!el.component || !isReservedTag(el.tag);
};
this.onceId = 0;
this.staticRenderFns = [];
this.pre = false;
};
// 将AST生成渲染函数
function generate(ast, options) {
var state = new CodegenState(options);
var code = ast ? genElement(ast, state) : '_c("div")';
return {
// 最外层包一个 with(this) 之后返回
render: "with(this){return " + code + "}",
staticRenderFns: state.staticRenderFns,
};
}
其中 genElement
函数是会根据 AST 的属性调用不同的方法生成字符串返回。也就是拼接字符串了:
genElement
function genElement(el, state) {
if (el.parent) {
el.pre = el.pre || el.parent.pre;
}
if (el.staticRoot && !el.staticProcessed) {
/**
* 处理静态根节点,生成节点的渲染函数
* 1、将当前静态节点的渲染函数放到 staticRenderFns 数组中
* 2、返回一个可执行函数 _m(index, true or '')
*/
return genStatic(el, state);
} else if (el.once && !el.onceProcessed) {
/**
* 处理带有 v-once 指令的节点,结果会有三种:
* 1、当前节点存在 v-if 指令,得到一个三元表达式,condition ? render1 : render2
* 2、当前节点是一个包含在 v-for 指令内部的静态节点,得到 `_o(_c(tag, data, children), number, key)`
* 3、当前节点就是一个单纯的 v-once 节点,得到 `_m(index, true of '')`
*/
return genOnce(el, state);
} else if (el.for && !el.forProcessed) {
/**
* 处理节点上的 v-for 指令
* 得到 `_l(exp, function(alias, iterator1, iterator2){return _c(tag, data, children)})`
*/
return genFor(el, state);
} else if (el.if && !el.ifProcessed) {
/**
* 处理带有 v-if 指令的节点,最终得到一个三元表达式:condition ? render1 : render2
*/
return genIf(el, state);
} else if (el.tag === "template" && !el.slotTarget && !state.pre) {
/**
* 当前节点不是 template 标签也不是插槽和带有 v-pre 指令的节点时走这里
* 生成所有子节点的渲染函数,返回一个数组,格式如:
* [_c(tag, data, children, normalizationType), ...]
*/
return genChildren(el, state) || "void 0";
} else if (el.tag === "slot") {
/**
* 生成插槽的渲染函数,得到
* _t(slotName, children, attrs, bind)
*/
return genSlot(el, state);
} else {
// 处理动态组件和普通元素(自定义组件、原生标签)
var code;
if (el.component) {
/**
* 处理动态组件,生成动态组件的渲染函数
* 得到 `_c(compName, data, children)`
*/
code = genComponent(el.component, el, state);
} else {
var data;
if (!el.plain || (el.pre && state.maybeComponent(el))) {
// 非普通元素或者带有 v-pre 指令的组件走这里,处理节点的所有属性,返回一个 JSON 字符串,
// 比如 '{ key: xx, ref: xx, ... }'
data = genData$2(el, state);
}
// 处理子节点,得到所有子节点字符串格式的代码组成的数组,格式:
// `['_c(tag, data, children)', ...],normalizationType`,
var children = el.inlineTemplate ? null : genChildren(el, state, true);
code =
"_c('" +
el.tag +
"'" +
(data ? "," + data : "") +
(children ? "," + children : "") +
")";
}
// 如果提供了 transformCode 方法,
// 则最终的 code 会经过各个模块(module)的该方法处理,
// 不过框架没提供这个方法,不过即使处理了,最终的格式也是 _c(tag, data, children)
for (var i = 0; i < state.transforms.length; i++) {
code = state.transforms[i](el, code);
}
return code;
}
}
先来说说staticRenderFns,这个数组中的函数与VDOM中的diff算法优化相关,我们会在模板编译优化过程中给每个节点加上staticRoot属性,其中标记为true的VNode就会生成静态节点渲染函数并放到到staticRenderFns数组中, 对应的方法是genStatic
genStatic
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state);
}
/**
* 生成静态节点的渲染函数
* 1、将当前静态节点的渲染函数放到 staticRenderFns 数组中
* 2、返回一个可执行函数 _m(idx, true or '') 字符串
*/
function genStatic(el, state) {
// 标记当前静态节点已经被处理过了
el.staticProcessed = true;
var originalPreState = state.pre;
if (el.pre) {
state.pre = el.pre;
}
state.staticRenderFns.push(
"with(this){return " + genElement(el, state) + "}"
);
state.pre = originalPreState;
return (
"_m(" +
(state.staticRenderFns.length - 1) +
(el.staticInFor ? ",true" : "") +
")"
);
}
v-once这个指令不需要任何表达式,它的作用就是定义它的元素或组件只会渲染一次,包括元素或者组件的所有字节点。首次渲染后,不再随着数据的改变而重新渲染。也就是说使用v-once,那么该块都将被视为静态内容。
genOnce
/**
* 处理带有 v-once 指令的节点,结果会有三种:
* 1、当前节点存在 v-if 指令,得到一个三元表达式,condition ? render1 : render2
* 2、当前节点是一个包含在 v-for 指令内部的静态节点,得到 `_o(_c(tag, data, children), number, key)`
* 3、当前节点就是一个单纯的 v-once 节点,得到 `_m(idx, true of '')`
*/
function genOnce(el, state) {
// 标记当前节点的 v-once 指令已经被处理过了
el.onceProcessed = true;
if (el.if && !el.ifProcessed) {
// 处理v-if 指令的节点
return genIf(el, state);
} else if (el.staticInFor) {
// 有 v-for 指令节点内部的静态节点
// 获取 v-for 指令的 key
var key = "";
var parent = el.parent;
while (parent) {
if (parent.for) {
key = parent.key;
break;
}
parent = parent.parent;
}
// key 不存在则给出提示,v-once 节点只能用于带有 key 的 v-for 节点内部
if (!key) {
state.warn(
"v-once can only be used inside v-for that is keyed. ",
el.rawAttrsMap["v-once"]
);
return genElement(el, state);
}
// 生成 `_o(_c(tag, data, children), number, key)`
return (
"_o(" + genElement(el, state) + "," + state.onceId++ + "," + key + ")"
);
} else {
// 上面几种情况都不符合,说明就是一个简单的静态节点,和处理静态根节点时的操作一样
return genStatic(el, state);
}
}
genFor
// 处理有v-for指令的节点
function genFor(el, state, altGen, altHelper) {
// 遍历的对象
var exp = el.for;
// 迭代时的别名
var alias = el.alias;
// iterator 为 v-for = "(item ,index ) in arr" 时会有,比如 iterator1 = index
var iterator1 = el.iterator1 ? "," + el.iterator1 : "";
var iterator2 = el.iterator2 ? "," + el.iterator2 : "";
// 提示,v-for 指令在组件上时必须使用 key
if (
state.maybeComponent(el) &&
el.tag !== "slot" &&
el.tag !== "template" &&
!el.key
) {
state.warn(
"<" +
el.tag +
' v-for="' +
alias +
" in " +
exp +
'">: component lists rendered with ' +
"v-for should have explicit keys. " +
"See https://vuejs.org/guide/list.html#key for more info.",
el.rawAttrsMap["v-for"],
true /* tip */
);
}
// 标记当前节点上的 v-for 指令已经被处理过了
el.forProcessed = true; // avoid recursion
// 得到 `_l(exp, function(alias, iterator1, iterator2){return _c(tag, data, children)})`
return (altHelper || '_l') + "((" + exp + ")," +
"function(" + alias + iterator1 + iterator2 + "){" +
"return " + ((altGen || genElement)(el, state)) +
'})'
}
genIf
// 处理带有 v-if 指令的节点
function genIf(el, state, altGen, altEmpty) {
// 标记当前节点的 v-if 指令已经被处理过了
el.ifProcessed = true; // avoid recursion
return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty);
}
function genIfConditions(conditions, state, altGen, altEmpty) {
if (!conditions.length) {
// 长度若为空,则直接返回一个空节点渲染函数
return altEmpty || "_e()";
}
var condition = conditions.shift();
if (condition.exp) {
// 如果 condition.exp 条件成立,则得到一个三元表达式,
// 如果条件不成立,则通过递归的方式找 conditions 数组中下一个元素,
// 直到找到条件成立的元素,然后返回一个三元表达式
return ("(" + (condition.exp) + ")?" + (genTernaryExp(condition.block)) + ":" + (genIfConditions(conditions, state, altGen, altEmpty)))
} else {
return "" + genTernaryExp(condition.block);
}
// v-if with v-once should generate code like (a)?_m(0):_m(1)
function genTernaryExp(el) {
return altGen
? altGen(el, state)
: el.once
? genOnce(el, state)
: genElement(el, state);
}
}
当前节点不是 template 标签也不是插槽和带有 v-pre 指令的节点时调用genChildren方法 生成所有子节点的渲染函数
genChildren
// 生成所有子节点的渲染函数
function genChildren(el, state, checkSkip, altGenElement, altGenNode) {
var children = el.children;
if (children.length) {
var el$1 = children[0];
// optimize single v-for
// 只有一个子节点且子节点的上有 v-for 指令且子节点的标签不为 template 或者 slot
// 直接调用 genElement 生成该节点的渲染函数
if (
children.length === 1 &&
el$1.for &&
el$1.tag !== "template" &&
el$1.tag !== "slot"
) {
var normalizationType = checkSkip
? state.maybeComponent(el$1) ? ",1" : ",0"
: "";
return ("" + ((altGenElement || genElement)(el$1, state)) + normalizationType)
}
var normalizationType$1 = checkSkip
? getNormalizationType(children, state.maybeComponent)
: 0;
var gen = altGenNode || genNode;
// 返回一个数组,数组的每个元素都是一个子节点的渲染函数
return ("[" + (children.map(function (c) { return gen(c, state); }).join(',')) + "]" + (normalizationType$1 ? ("," + normalizationType$1) : ''))
}
}
genNode、genText、genComment
function genNode(node, state) {
if (node.type === 1) {
return genElement(node, state);
} else if (node.type === 3 && node.isComment) {
return genComment(node);
} else {
return genText(node);
}
}
function genText(text) {
return ("_v(" + (text.type === 2
? text.expression // no need for () because already wrapped in _s()
: transformSpecialNewlines(JSON.stringify(text.text))) + ")")
}
function genComment(comment) {
return "_e(" + JSON.stringify(comment.text) + ")";
}
genSlot
// 处理插槽
function genSlot(el, state) {
// 插槽名称
var slotName = el.slotName || '"default"';
// 生成所有的子节点
var children = genChildren(el, state);
var res = "_t(" + slotName + (children ? "," + children : "");
var attrs = el.attrs || el.dynamicAttrs
? genProps((el.attrs || []).concat(el.dynamicAttrs || []).map(function (attr) { return ({
// slot props are camelized
name: camelize(attr.name),
value: attr.value,
dynamic: attr.dynamic
}); }))
: null;
var bind$$1 = el.attrsMap["v-bind"];
if ((attrs || bind$$1) && !children) {
res += ",null";
}
if (attrs) {
res += "," + attrs;
}
if (bind$$1) {
res += (attrs ? "" : ",null") + "," + bind$$1;
}
return res + ")";
}
genComponent
// 生成动态组件的渲染函数
function genComponent(componentName, el, state) {
var children = el.inlineTemplate ? null : genChildren(el, state, true);
return (
"_c(" +
componentName +
"," +
genData$2(el, state) +
(children ? "," + children : "") +
")"
);
}
genData$2
// 处理节点上的属性
function genData$2(el, state) {
var data = "{";
// 首先先处理指令,因为指令可能在生成其它属性之前改变这些属性
var dirs = genDirectives(el, state);
if (dirs) {
data += dirs + ",";
}
// key
if (el.key) {
data += "key:" + el.key + ",";
}
// ref
if (el.ref) {
data += "ref:" + el.ref + ",";
}
if (el.refInFor) {
data += "refInFor:true,";
}
// pre
if (el.pre) {
data += "pre:true,";
}
// 动态组件
if (el.component) {
data += 'tag:"' + el.tag + '",';
}
// 为节点执行模块(class、style)的 genData 方法,
// 得到 data = { staticClass: xx, class: xx, staticStyle: xx, style: xx }
for (var i = 0; i < state.dataGenFns.length; i++) {
data += state.dataGenFns[i](el);
}
// attributes
if (el.attrs) {
data += "attrs:" + genProps(el.attrs) + ",";
}
// DOM props
if (el.props) {
data += "domProps:" + genProps(el.props) + ",";
}
// 自定义事件
if (el.events) {
data += genHandlers(el.events, false) + ",";
}
// 带 native 修饰符的事件
if (el.nativeEvents) {
data += genHandlers(el.nativeEvents, true) + ",";
}
// 非作用域插槽
if (el.slotTarget && !el.slotScope) {
data += "slot:" + el.slotTarget + ",";
}
// 作用域插槽
if (el.scopedSlots) {
data += genScopedSlots(el, el.scopedSlots, state) + ",";
}
// component v-model
if (el.model) {
data += "model:{value:" + (el.model.value) + ",callback:" + (el.model.callback) + ",expression:" + (el.model.expression) + "},";
}
// 内联模版
if (el.inlineTemplate) {
var inlineTemplate = genInlineTemplate(el, state);
if (inlineTemplate) {
data += inlineTemplate + ",";
}
}
data = data.replace(/,$/, "") + "}";
// v-bind dynamic argument wrap
// v-bind with dynamic arguments must be applied using the same v-bind object
// merge helper so that class/style/mustUseProp attrs are handled correctly.
// 动态属性
if (el.dynamicAttrs) {
data =
"_b(" + data + ',"' + el.tag + '",' + genProps(el.dynamicAttrs) + ")";
}
// v-bind data wrap
if (el.wrapData) {
data = el.wrapData(data);
}
// v-on data wrap
if (el.wrapListeners) {
data = el.wrapListeners(data);
}
return data;
}
genDirectives
/**
* 运行指令的编译方法,如果指令存在运行时任务,则返回 directives: [{ name, rawName, value, arg, modifiers }, ...}]
*/
function genDirectives(el, state) {
var dirs = el.directives;
if (!dirs) {
return;
}
// 指令的处理结果
var res = "directives:[";
// 标记,用于标记指令是否需要在运行时完成的任务,比如 v-model 的 input 事件
var hasRuntime = false;
var i, l, dir, needRuntime;
for (i = 0, l = dirs.length; i < l; i++) {
dir = dirs[i];
needRuntime = true;
var gen = state.directives[dir.name];
if (gen) {
// 执行指令的编译方法,如果指令还需要运行时完成一部分任务,则返回 true,比如 v-model
needRuntime = !!gen(el, dir, state.warn);
}
if (needRuntime) {
// 表示该指令在运行时还有任务
hasRuntime = true;
res +=
'{name:"' +
dir.name +
'",rawName:"' +
dir.rawName +
'"' +
(dir.value
? ",value:(" + dir.value + "),expression:" + JSON.stringify(dir.value)
: "") +
(dir.arg
? ",arg:" + (dir.isDynamicArg ? dir.arg : '"' + dir.arg + '"')
: "") +
(dir.modifiers ? ",modifiers:" + JSON.stringify(dir.modifiers) : "") +
"},";
}
}
if (hasRuntime) {
// 有指令存在运行时任务时,才会返回 res
return res.slice(0, -1) + "]";
}
}
genProps
// 遍历属性数组 props,得到所有属性组成的字符串
function genProps (props) {
// 静态属性
var staticProps = "";
// 动态属性
var dynamicProps = "";
for (var i = 0; i < props.length; i++) {
var prop = props[i];
var value = transformSpecialNewlines(prop.value);
if (prop.dynamic) {
dynamicProps += (prop.name) + "," + value + ",";
} else {
staticProps += "\"" + (prop.name) + "\":" + value + ",";
}
}
// 去掉静态属性最后的逗号
staticProps = "{" + (staticProps.slice(0, -1)) + "}";
if (dynamicProps) {
return ("_d(" + staticProps + ",[" + (dynamicProps.slice(0, -1)) + "])")
} else {
return staticProps
}
}
genHandlers
// 生成自定义事件的代码
function genHandlers(events, isNative) {
var prefix = isNative ? "nativeOn:" : "on:";
// 静态
var staticHandlers = "";
// 动态
var dynamicHandlers = "";
for (var name in events) {
// 获取指定事件的回调函数名
var handlerCode = genHandler(events[name]);
if (events[name] && events[name].dynamic) {
dynamicHandlers += name + "," + handlerCode + ",";
} else {
staticHandlers += '"' + name + '":' + handlerCode + ",";
}
}
// 去掉末尾的逗号
staticHandlers = "{" + staticHandlers.slice(0, -1) + "}";
if (dynamicHandlers) {
return (
prefix +
"_d(" +
staticHandlers +
",[" +
dynamicHandlers.slice(0, -1) +
"])"
);
} else {
return prefix + staticHandlers;
}
}
参考资料: