parse解析完以後,將生成的ast返回到baseCompile,接下來就是調用optimize方法對ast進行優化。javascript
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 } });
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節點。java
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) //遍歷判斷屬性是否靜態 )) }
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對象增長加了兩個屬性,如圖:node
接下來調用generate方法,將ast對象轉換成Vue自定義的字符串形式。express
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 } }
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 } }
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函數segmentfault
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:緩存
接下來開始對子節點進行處理app
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:ui
接下來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返回的對象信息:
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。
整個函數調用過程: