parse解析完之后,将生成的ast返回到baseCompile,接下来就是调用optimize方法对ast进行优化。
var createCompiler = createCompilerCreator(function baseCompile (
template,
options
) {
//获取ast
var ast = parse(template.trim(), options);
if (options.optimize !== false) {
optimize(ast, options);
}
var code = generate(ast, options);
return {
ast: ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
});
optimize
function optimize (root, options) {
if (!root) { return }
//对静态标签进行缓存
isStaticKey = genStaticKeysCached(options.staticKeys || '');
isPlatformReservedTag = options.isReservedTag || no;
//标记所有非静态节点
markStatic$1(root);
//标记所有静态root节点
markStaticRoots(root, false);
}
optimize方法通过genStaticKeysCached缓存了所有静态标签,调用markStatic$1(root)标记所有非静态节点,调用markStaticRoots(root, false)标记静态root节点。
function markStatic$1 (node) {
//判断是否是静态节点,isStatic代码在下面
node.static = isStatic(node);
if (node.type === 1) {
//过滤掉slot标签和template标签
//原因:组件不能变成slot节点
//静态的slot节点内容不能热加载
if (
!isPlatformReservedTag(node.tag) &&
node.tag !== 'slot' &&
node.attrsMap['inline-template'] == null
) {
return
}
//循环递归标记节点
for (var i = 0, l = node.children.length; i < l; i++) {
var child = node.children[i];
markStatic$1(child);
if (!child.static) {
node.static = false;
}
}
if (node.ifConditions) {
/**/
}
}
}
function isStatic (node) {
if (node.type === 2) { // 判断是不是类似{{message}}这样的表达式
return false
}
if (node.type === 3) { // 判断是不是纯文本
return true
}
return !!(node.pre || (
!node.hasBindings && // 是否动态绑定
!node.if && !node.for && //是否v-if or v-for or v-else
!isBuiltInTag(node.tag) && // not a built-in
isPlatformReservedTag(node.tag) && // not a component
!isDirectChildOfTemplateFor(node) &&
Object.keys(node).every(isStaticKey) //遍历判断属性是否静态
))
}
markStaticRoots
function markStaticRoots (node, isInFor) {
if (node.type === 1) {
if (node.static || node.once) {
node.staticInFor = isInFor;
}
// 作为静态节点 必须有子节点并且不为纯文本 否则更新消耗较大
if (node.static && node.children.length && !(
node.children.length === 1 &&
node.children[0].type === 3
)) {
node.staticRoot = true;
return
} else {
node.staticRoot = false;
}
//进行递归标记
if (node.children) {
for (var i = 0, l = node.children.length; i < l; i++) {
markStaticRoots(node.children[i], isInFor || !!node.for);
}
}
if (node.ifConditions) {
/**/
}
}
}
经过optimize函数,ast对象增加加了两个属性,如图:
接下来调用generate方法,将ast对象转换成Vue自定义的字符串形式。
generate
function generate (
ast,
options
) {
//根据options创建CodegenState对象
var state = new CodegenState(options);
//调用genElement将ast对象转换为字符串
var code = ast ? genElement(ast, state) : '_c("div")';
return {
render: ("with(this){return " + code + "}"),
staticRenderFns: state.staticRenderFns
}
}
genElement
function genElement (el, state) {
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget) {
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
return genSlot(el, state)
} else {
// component or element
var code;
if (el.component) {
code = genComponent(el.component, el, state);
} else {
//本例子进入这里,调用genData$2
var data = el.plain ? undefined : genData$2(el, state);
var children = el.inlineTemplate ? null : genChildren(el, state, true);
code = "_c('" + (el.tag) + "'" + (data ? ("," + data) : '') + (children ? ("," + children) : '') + ")";
}
// module transforms
for (var i = 0; i < state.transforms.length; i++) {
code = state.transforms[i](el, code);
}
return code
}
}
genData$2
function genData$2 (el, state) {
var data = '{';
// 首先对directives进行处理
// directives可能会对el上的其他属性有影响,所以先处理
var dirs = genDirectives(el, state);
if (dirs) { data += dirs + ','; }
//根据本文的例子,没有执行的判断,代码都省略了
/*处理key,ref,refInFor,pre,component*/
// module data generation functions
for (var i = 0; i < state.dataGenFns.length; i++) {
data += state.dataGenFns[i](el);//调用genData对class进行处理
}
if (el.attrs) {
//进入该判断,调用genProps,对el的属性进行处理
data += "attrs:{" + (genProps(el.attrs)) + "},";
}
/*处理props,events,nativeEvents,slotTarget,scopedSlots,model,inlineTemplate*/
data = data.replace(/,$/, '') + '}';
// v-bind data wrap
if (el.wrapData) {
data = el.wrapData(data);
}
// v-on data wrap
if (el.wrapListeners) {
data = el.wrapListeners(data);
}
return data
}
genData$2跳过了大多数的判断,直接进入attrs,调用genProps函数
function genProps (props) {
var res = '';
// 将属性名,属性值拼接成 "属性名":"属性值"形式的字符串
//本文例子"id":"test"
for (var i = 0; i < props.length; i++) {
var prop = props[i];
/* istanbul ignore if */
{
res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ",";
}
}
return res.slice(0, -1)
}
genData$2最终返回的data:
接下来开始对子节点进行处理
genChildren
function genChildren (
el,
state,
checkSkip,
altGenElement,
altGenNode
) {
var children = el.children;
if (children.length) {
var el$1 = children[0];
//对v-for进行简单优化
if (children.length === 1 &&
el$1.for &&
el$1.tag !== 'template' &&
el$1.tag !== 'slot'
) {
return (altGenElement || genElement)(el$1, state)
}
var normalizationType = checkSkip
? getNormalizationType(children, state.maybeComponent)
: 0;
var gen = altGenNode || genNode;
return ("[" + (children.map(function (c) { return gen(c, state); }).join(',')) + "]" + (normalizationType ? ("," + normalizationType) : ''))
}
}
function genNode (node, state) {
if (node.type === 1) {
return genElement(node, state)
} 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)) + ")")
}
最终转换后的字符串的结果为:
正好对应Vue对v-model的解析过程:https://segmentfault.com/a/11...
code:
compileToFunction
接下来compileToFunction将生成的code字符串代码转化为函数
// turn code into functions
var res = {};
var fnGenErrors = [];
//createFunction就返回了new Function(code)
//这里的render就是上文的code字符串
res.render = createFunction(compiled.render, fnGenErrors);
res.staticRenderFns = compiled.staticRenderFns.map(function (code) {
return createFunction(code, fnGenErrors)
});
compileToFunction返回的对象信息:
$amount
var ref = compileToFunctions(template, {
shouldDecodeNewlines: shouldDecodeNewlines,
shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
delimiters: options.delimiters,
comments: options.comments
}, this);
var render = ref.render;
var staticRenderFns = ref.staticRenderFns;
options.render = render;
options.staticRenderFns = staticRenderFns;
$amount调用compileToFunctions后,将返回的对象包含的render函数和staticRenderFns属性,挂载到options参数上,然后再次调用mount。
整个函数调用过程:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。