您好,登錄后才能下訂單哦!
這篇文章主要介紹Vue中AST源碼解析的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
從這個(gè)函數(shù)開始的:
// Line-3924 Vue.prototype._init = function(options) { // 大量初始化 // ... // Go! if (vm.$options.el) { vm.$mount(vm.$options.el); } };
弄完data屬性的數(shù)據(jù)綁定后,開始處理el屬性,也就是掛載的DOM節(jié)點(diǎn),這里的vm.$options.el也就是傳進(jìn)去的'#app'字符串。
有一個(gè)值得注意的點(diǎn)是,源碼中有2個(gè)$mount函數(shù)都是Vue$3的原型函數(shù),其中一個(gè)標(biāo)記了注釋public mount method,在7531行,另外一個(gè)在9553行。打斷點(diǎn)進(jìn)入的是后面,因?yàn)槎x的晚,覆蓋了前面的函數(shù)。
// Line-7531 // public mount method Vue$3.prototype.$mount = function(el,hydrating) { el = el && inBrowser ? query(el) : undefined; return mountComponent(this, el, hydrating) }; // Line-9552 var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function( el, hydrating ) { // ...很多代碼 return mount.call(this, el, hydrating) };
現(xiàn)在進(jìn)入后面的$mount函數(shù)看看內(nèi)部結(jié)構(gòu):
// Line-9552 var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function(el,hydrating) { // 將el格式化為DOM節(jié)點(diǎn) el = el && query(el); // 判斷是否掛載到body或者h(yuǎn)tml標(biāo)簽上 if (el === document.body || el === document.documentElement) { "development" !== 'production' && warn( "Do not mount Vue to <html> or <body> - mount to normal elements instead." ); return this } var options = this.$options; // 處理template/el 轉(zhuǎn)換為渲染函數(shù) if (!options.render) { // ...非常多代碼 } return mount.call(this, el, hydrating) };
代碼前半段首先將el轉(zhuǎn)換為DOM節(jié)點(diǎn),并判斷是否掛載到body或者h(yuǎn)tml標(biāo)簽,看看簡(jiǎn)單的query函數(shù):
// Line-4583 function query(el) { // 如果是字符串就調(diào)用querySelector if (typeof el === 'string') { var selected = document.querySelector(el); if (!selected) { "development" !== 'production' && warn( 'Cannot find element: ' + el ); // 找不到就返回一個(gè)div return document.createElement('div') } return selected } // 不是字符串就默認(rèn)傳進(jìn)來的是DOM節(jié)點(diǎn) else { return el } }
函數(shù)比較簡(jiǎn)單,值得注意的幾個(gè)點(diǎn)是,由于調(diào)用的是querySelector方法,所以可以傳標(biāo)簽名、類名、C3新選擇器等,都會(huì)返回查詢到的第一個(gè)。當(dāng)然,總是傳一個(gè)ID或者確定的DOM節(jié)點(diǎn)才是正確用法。
下面看接下來的代碼:
// Line-9552 var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function(el,hydrating) { // ...el轉(zhuǎn)換為DOM節(jié)點(diǎn) // ... // 沒有render屬性 進(jìn)入代碼段 if (!options.render) { var template = options.template; // 沒有template 跳 if (template) { if (typeof template === 'string') { if (template.charAt(0) === '#') { template = idToTemplate(template); /* istanbul ignore if */ if ("development" !== 'production' && !template) { warn( ("Template element not found or is empty: " + (options.template)), this ); } } } else if (template.nodeType) { template = template.innerHTML; } else { { warn('invalid template option:' + template, this); } return this } } // 有el 獲取字符串化的DOM樹 else if (el) { template = getOuterHTML(el); } if (template) { // ...小段代碼 } } return mount.call(this, el, hydrating) };
由于沒有template屬性,會(huì)直接進(jìn)入第二個(gè)判斷條件,調(diào)用getOuterHTML來初始化template變量,函數(shù)比較簡(jiǎn)單, 來看看:
// Line-9623 function getOuterHTML(el) { if (el.outerHTML) { return el.outerHTML } // 兼容IE中的SVG else { var container = document.createElement('div'); container.appendChild(el.cloneNode(true)); return container.innerHTML } }
簡(jiǎn)單來講,就是調(diào)用outerHTML返回DOM樹的字符串形式,看圖就明白了:
下面看最后一段代碼:
// Line-9552 var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function(el,hydrating) { // ...el轉(zhuǎn)換為DOM節(jié)點(diǎn) // ... // 沒有render屬性 進(jìn)入代碼段 if (!options.render) { // ...處理template // ... if (template) { // 編譯開始 if ("development" !== 'production' && config.performance && mark) { mark('compile'); } // 將DOM樹字符串編譯為函數(shù) var ref = compileToFunctions(template, { shouldDecodeNewlines: shouldDecodeNewlines, delimiters: options.delimiters }, this); // options添加屬性 var render = ref.render; var staticRenderFns = ref.staticRenderFns; options.render = render; options.staticRenderFns = staticRenderFns; // 編譯結(jié)束 if ("development" !== 'production' && config.performance && mark) { mark('compile end'); measure(((this._name) + " compile"), 'compile', 'compile end'); } } } return mount.call(this, el, hydrating) };
忽略2段dev模式下的提示代碼,剩下的代碼做了3件事,調(diào)用compileToFunctions函數(shù)肢解DOM樹字符串,將返回的對(duì)象屬性添加到options上,再次調(diào)用mount函數(shù)。
首先看一下compileToFunctions函數(shù),該函數(shù)接受3個(gè)參數(shù),分別為字符串、配置對(duì)象、當(dāng)前vue實(shí)例。
由于函數(shù)比較長(zhǎng),而且部分是錯(cuò)誤判斷,簡(jiǎn)化后如下:
// Line-9326 function compileToFunctions(template,options,vm) { // 獲取配置參數(shù) options = options || {}; // ... var key = options.delimiters ? String(options.delimiters) + template : template; // 檢測(cè)緩存 if (functionCompileCache[key]) { return functionCompileCache[key] } // 1 var compiled = compile(template, options); // ... // 2 var res = {}; var fnGenErrors = []; res.render = makeFunction(compiled.render, fnGenErrors); var l = compiled.staticRenderFns.length; res.staticRenderFns = new Array(l); for (var i = 0; i < l; i++) { res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors); } // ... // 3 return (functionCompileCache[key] = res) }
可以看到,這個(gè)函數(shù)流程可以分為4步,獲取參數(shù) => 調(diào)用compile函數(shù)進(jìn)行編譯 => 將得到的compiled轉(zhuǎn)換為函數(shù) => 返回并緩存。
第一節(jié)現(xiàn)在這樣吧。一張圖總結(jié)下:
以上是“Vue中AST源碼解析的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。