Vue源码解析之AST语法树(三)
生活随笔
收集整理的這篇文章主要介紹了
Vue源码解析之AST语法树(三)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
parse解析完之后,將生成的ast返回到baseCompile,接下來就是調用optimize方法對ast進行優化。
var createCompiler = createCompilerCreator(function baseCompile (template,options ) {//獲取astvar 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-inisPlatformReservedTag(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 elementvar code;if (el.component) {code = genComponent(el.component, el, state);} else {//本例子進入這里,調用genData$2var 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 transformsfor (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 functionsfor (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 wrapif (el.wrapData) {data = el.wrapData(data);}// v-on data wrapif (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 functionsvar 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。
整個函數調用過程:
總結
以上是生活随笔為你收集整理的Vue源码解析之AST语法树(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于matplotlib的数据可视化 -
- 下一篇: 2018.7月Vue优质开源项目清单