這篇文章主要講述compileToFunctions的作用。
我們現(xiàn)在就compileToFunctions 的真弄明白為什么要弄的這么復(fù)雜?現(xiàn)在我們看看下面完整代碼。
compileToFunctions是如何把模板字符串template編譯成渲染函數(shù)render的。
Vue.prototype.$mount函數(shù)體
回歸到Vue.prototype.$mount函數(shù)體內(nèi)。
var ref = compileToFunctions(template, { shouldDecodeNewlines: shouldDecodeNewlines, shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref, delimiters: options.delimiters, comments: options.comments }, th
is);
在上面可以看出,在此傳遞給compileToFunctions的第一個參數(shù)就是模板字符串template,而第二個參數(shù)則是一個配置選項options。
先說說這些配置選項中的屬性!
shouldDecodeNewlines
源碼出處
// check whether current browser encodes a char inside attribute values var div; function getShouldDecode(href) { div = div || document.createElement('div'); div.innerHTML = href ? "<a href=\"\n\"/>" : "<div a=\"\n\"/>"; return div.innerHTML.indexOf(' ') > 0 } // #3663: IE encodes newlines inside attribute values while other browsers don't var shouldDecodeNewlines = inBrowser ? getShouldDecode(false) : false; // #6828: chrome encodes content in a[href] var shouldDecodeNewlinesForHref = inBrowser ? getShouldDecode(true) : false;
上面代碼想表達什么?
其實大致表達是在我們innerHTML獲取內(nèi)容時,換行符和制表符則轉(zhuǎn)化成
和	。在IE瀏覽器中,這個將不會成為問題。
但這會對Vue的編譯器在對模板進行編譯后的結(jié)果有影響,如何不出現(xiàn)這就要Vue需要知道在什么時候要做兼容工作,如果 shouldDecodeNewlines 為 true,意味著 Vue 在編譯模板的時候,要對屬性值中的換行符或制表符做兼容處理。而shouldDecodeNewlinesForHref為true 意味著Vue在編譯模板的時候,要對a標簽的 href 屬性值中的換行符或制表符做兼容處理。
options.delimiters & options.comments
兩者都是當(dāng)前Vue實例的$options屬性,并且delimiters和comments都是 Vue 提供的選項。
現(xiàn)在我們已經(jīng)搞清楚了這些配置選項是什么意思,那接下來我們把目光放在《Vue編譯器源碼分析(二)》針對compileToFunctions函數(shù)逐行分析。
compileToFunctions函數(shù)逐行分析
function createCompileToFunctionFn(compile) { var cache = Object.create(null); return function compileToFunctions( template, options, vm ) { options = extend({}, options); var warn$$1 = options.warn || warn; delete options.warn; /* istanbul ignore if */ { // detect possible CSP restriction try { new Function('return 1'); } catch (e) { if (e.toString().match(/unsafe-eval|CSP/)) { warn$$1( 'It seems you are using the standalone build of Vue.js in an ' + 'environment with Content Security Policy that prohibits unsafe-eval. ' + 'The template compiler cannot work in this environment. Consider ' + 'relaxing the policy to allow unsafe-eval or pre-compiling your ' + 'templates into render functions.' ); } } } // check cache var key = options.delimiters ? String(options.delimiters) + template : template; if (cache[key]) { return cache[key] } // compile var compiled = compile(template, options); // check compilation errors/tips { if (compiled.errors && compiled.errors.length) { warn$$1( "Error compiling template:\n\n" + template + "\n\n" + compiled.errors.map(function(e) { return ("- " + e); }).join('\n') + '\n', vm ); } if (compiled.tips && compiled.tips.length) { compiled.tips.forEach(function(msg) { return tip(msg, vm); }); } } // turn code into functions var res = {}; var fnGenErrors = []; res.render = createFunction(compiled.render, fnGenErrors); res.staticRenderFns = compiled.staticRenderFns.map(function(code) { return createFunction(code, fnGenErrors) }); // check function generation errors. // this should only happen if there is a bug in the compiler itself. // mostly for codegen development use /* istanbul ignore if */ { if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { warn$$1( "Failed to generate render function:\n\n" + fnGenErrors.map(function(ref) { var err = ref.err; var code = ref.code; return ((err.toString()) + " in\n\n" + code + "\n"); }).join('\n'), vm ); } } return (cache[key] = res) } }
注意compileToFunctions函數(shù)是接收三個參數(shù)的,第三個參數(shù)是當(dāng)前Vue實例。
首先:
options = extend({}, options); var warn$$1 = options.warn || warn; delete options.warn;
通過extend 把 options 配置對象上的屬性擴展一份到新對象上,定義warn$$1變量。warn是一個錯誤信息提示的函數(shù)。
接下來:
// detect possible CSP restriction try { new Function('return 1'); } catch (e) { if (e.toString().match(/unsafe-eval|CSP/)) { warn$$1( 'It seems you are using the standalone build of Vue.js in an ' + 'environment with Content Security Policy that prohibits unsafe-eval. ' + 'The template compiler cannot work in this environment. Consider ' + 'relaxing the policy to allow unsafe-eval or pre-compiling your ' + 'templates into render functions.' ); } }
上面代碼體現(xiàn)出語句問題就是使用 try catch 語句塊對 new Function('return 1') 這句代碼進行錯誤捕獲,如果有錯誤發(fā)生且錯誤的內(nèi)容中包含如 'unsafe-eval' 或者 'CSP' 這些字樣的信息時就會給出一個警告。
CSP全稱Content Security Policy ,內(nèi)容安全策略,為了頁面內(nèi)容安全而制定的一系列防護策略. 通過CSP所約束的的規(guī)責(zé)指定可信的內(nèi)容來源(這里的內(nèi)容可以指腳本、圖片、iframe、fton、style等等可能的遠程的資源)。通過CSP協(xié)定,讓W(xué)EB處于一個安全的運行環(huán)境中。
由于new Function() 被影響到,因此不可以使用。但是將模板字符串編譯成渲染函數(shù)又依賴new Function(),所以解決方案有兩個:
1、放寬你的CSP策略
2、預(yù)編譯
這段代碼的作用就是檢測 new Function() 是否可用,并在某些極端情況下給你一個有用的提示。
接下來是:
var key = options.delimiters ? String(options.delimiters) + template : template; if (cache[key]) { return cache[key] }
options.delimiters這個選項是改變純文本插入分隔符,如果options.delimiters存在,則使用String 方法將其轉(zhuǎn)換成字符串并與 template 拼接作為 key 的值,否則直接使用 template 字符串作為 key 的值,然后判斷 cache[key] 是否存在,如果存在直接返回cache[key]。
這么做的目的是緩存字符串模板的編譯結(jié)果,防止重復(fù)編譯,提升性能,我們再看一下compileToFunctions函數(shù)的最后一句代碼:
return (cache[key] = res)
這句代碼在返回編譯結(jié)果的同時,將結(jié)果緩存,這樣下一次發(fā)現(xiàn)如果 cache 中存在相同的 key則不需要再次編譯,直接使用緩存的結(jié)果就可以了。
接下來:
// compile var compiled = compile(template, options); // check compilation errors/tips if (compiled.errors && compiled.errors.length) { warn$$1( "Error compiling template:\n\n" + template + "\n\n" + compiled.errors.map(function(e) { return ("- " + e); }).join('\n') + '\n', vm ); } if (compiled.tips && compiled.tips.length) { compiled.tips.forEach(function(msg) { return tip(msg, vm); }); } }
compile 是引用了來自 createCompileToFunctionFn 函數(shù)的形參稍后會重點來介紹它。
在使用 compile 函數(shù)對模板進行編譯后會返回一個結(jié)果 compiled,返回結(jié)果 compiled 是一個對象且這個對象可能包含兩個屬性 errors 和 tips 。這兩個屬性分別包含了編譯過程中的錯誤和提示信息。所以上面那段代碼的作用就是用來檢查使用 compile 對模板進行編譯的過程中是否存在錯誤和提示的,如果存在那么需要將其打印出來。
接下來:
// turn code into functions var res = {}; var fnGenErrors = []; res.render = createFunction(compiled.render, fnGenErrors); res.staticRenderFns = compiled.staticRenderFns.map(function(code) { return createFunction(code, fnGenErrors) });
res 是一個空對象且它是最終的返回值,fnGenErrors 是一個空數(shù)組。
在 res 對象上添加一個 render 屬性,這個 render 屬性,就是最終生成的渲染函數(shù),它的值是通過 createFunction 創(chuàng)建出來的。
createFunction 函數(shù)源碼
function createFunction(code, errors) { try { return new Function(code) } catch (err) { errors.push({ err: err, code: code }); return noop } }
createFunction 函數(shù)接收兩個參數(shù),第一個參數(shù) code 為函數(shù)體字符串,該字符串將通過new Function(code) 的方式創(chuàng)建為函數(shù)。
第二個參數(shù) errors 是一個數(shù)組,作用是當(dāng)采用 new Function(code) 創(chuàng)建函數(shù)發(fā)生錯誤時用來收集錯誤的。
已知,傳遞給 createFunction 函數(shù)的第一個參數(shù)是 compiled.render,所以 compiled.render 應(yīng)該是一個函數(shù)體字符串,且我們知道 compiled 是 compile 函數(shù)的返回值,這說明:compile函數(shù)編譯模板字符串后所得到的是字符串形式的函數(shù)體。
傳遞給 createFunction 函數(shù)的第二個參數(shù)是之前聲明的 fnGenErrors 常量,也就是說當(dāng)創(chuàng)建函數(shù)出錯時的錯誤信息被 push 到這個數(shù)組里了。
在這句代碼之后,又在 res 對象上添加了 staticRenderFns 屬性:
res.staticRenderFns = compiled.staticRenderFns.map(function(code) { return createFunction(code, fnGenErrors) });
由這段代碼可知 res.staticRenderFns 是一個函數(shù)數(shù)組,是通過對compiled.staticRenderFns遍歷生成的,這說明:compiled 除了包含 render 字符串外,還包含一個字符串?dāng)?shù)組staticRenderFns ,且這個字符串?dāng)?shù)組最終也通過 createFunction 轉(zhuǎn)為函數(shù)。staticRenderFns 的主要作用是渲染優(yōu)化,我們后面詳細講解。
最后的代碼:
// check function generation errors. // this should only happen if there is a bug in the compiler itself. // mostly for codegen development use /* istanbul ignore if */ if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) { warn$$1( "Failed to generate render function:\n\n" + fnGenErrors.map(function(ref) { var err = ref.err; var code = ref.code; return ((err.toString()) + " in\n\n" + code + "\n"); }).join('\n'), vm ); } return (cache[key] = res)
上面代碼主要是渲染函數(shù)過程在打印中的錯誤,且同時將結(jié)果村存儲下來,接下來我們講講compile 的作用。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/127790.html
摘要:頁面這個實例,按理就需要解析兩次,但是有緩存之后就不會理清思路也就是說,其實內(nèi)核就是不過是經(jīng)過了兩波包裝的第一波包裝在中的內(nèi)部函數(shù)中內(nèi)部函數(shù)的作用是合并公共和自定義,但是相關(guān)代碼已經(jīng)省略,另一個就是執(zhí)行第二波包裝在中,目的是進行緩存 寫文章不容易,點個贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 ...
摘要:具體可以查看抽象語法樹。而則是帶緩存的編譯器,同時以及函數(shù)會被轉(zhuǎn)換成對象。會用正則等方式解析模板中的指令等數(shù)據(jù),形成語法樹。是將語法樹轉(zhuǎn)化成字符串的過程,得到結(jié)果是的字符串以及字符串。里面的節(jié)點與父節(jié)點的結(jié)構(gòu)類似,層層往下形成一棵語法樹。 寫在前面 因為對Vue.js很感興趣,而且平時工作的技術(shù)棧也是Vue.js,這幾個月花了些時間研究學(xué)習(xí)了一下Vue.js源碼,并做了總結(jié)與輸出。 文...
摘要:圖在中應(yīng)用三數(shù)據(jù)渲染過程數(shù)據(jù)綁定實現(xiàn)邏輯本節(jié)正式分析從到數(shù)據(jù)渲染到頁面的過程,在中定義了一個的構(gòu)造函數(shù)。一、概述 vue已是目前國內(nèi)前端web端三分天下之一,也是工作中主要技術(shù)棧之一。在日常使用中知其然也好奇著所以然,因此嘗試閱讀vue源碼并進行總結(jié)。本文旨在梳理初始化頁面時data中的數(shù)據(jù)是如何渲染到頁面上的。本文將帶著這個疑問一點點追究vue的思路。總體來說vue模版渲染大致流程如圖1所...
知道嗎?Vue.js 有 2 個版本,一個是Runtime + Compiler版本,另一個是Runtime only版本。Runtime + Compiler版本是包含編譯代碼的,簡單來說就是Runtime only版本不包含編譯代碼的,在運行時候,需要借助 webpack 的 vue-loader 事先把模板編譯成 render 函數(shù)。 假如在你需要在客戶端編譯模板 (比如傳入一個字符串...
摘要:中引入了中的中引入了中的中,定義了的構(gòu)造函數(shù)中的原型上掛載了方法,用來做初始化原型上掛載的屬性描述符,返回原型上掛載的屬性描述符返回原型上掛載與方法,用來為對象新增刪除響應(yīng)式屬性原型上掛載方法原型上掛載事件相關(guān)的方法。 入口尋找 入口platforms/web/entry-runtime-with-compiler中import了./runtime/index導(dǎo)出的vue。 ./r...
閱讀 547·2023-03-27 18:33
閱讀 732·2023-03-26 17:27
閱讀 630·2023-03-26 17:14
閱讀 591·2023-03-17 21:13
閱讀 521·2023-03-17 08:28
閱讀 1801·2023-02-27 22:32
閱讀 1292·2023-02-27 22:27
閱讀 2178·2023-01-20 08:28