Vue源码解析之AST语法树(三)
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。
整个函数调用过程:
相关推荐
yutou0 2020-10-17
codedecode 2020-11-14
summerinsist 2020-08-21
87901735 2020-08-19
benico 2020-08-19
Rain 2020-08-15
RemixGdc 2020-08-15
Jaystrong 2020-08-02
KFLING 2020-08-01
zhangll00 2020-07-29
elitechen 2020-07-28
suosuo 2020-07-28
benico 2020-07-28
xiyoukeke 2020-07-28
小惠 2020-07-27
此处省略三千字 2020-07-20
泥淖 2020-07-19
安得情怀似旧时 2020-07-06
sunzhihaofuture 2020-07-04