摘要:頁面這個(gè)實(shí)例,按理就需要解析兩次,但是有緩存之后就不會(huì)理清思路也就是說,其實(shí)內(nèi)核就是不過是經(jīng)過了兩波包裝的第一波包裝在中的內(nèi)部函數(shù)中內(nèi)部函數(shù)的作用是合并公共和自定義,但是相關(guān)代碼已經(jīng)省略,另一個(gè)就是執(zhí)行第二波包裝在中,目的是進(jìn)行緩存
寫文章不容易,點(diǎn)個(gè)贊唄兄弟
專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧
研究基于 Vue版本 【2.5.17】
如果你覺得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧
【Vue原理】Compile - 源碼版 之 從新建實(shí)例到 compile結(jié)束的主要流程
Compile 的內(nèi)容十分之多,今天先來個(gè)熱身,先不研究 compile 內(nèi)部編譯細(xì)節(jié),而是記錄一下
從新建實(shí)例開始,到結(jié)束 compile ,其中的大致外部流程,不涉及 compile 的內(nèi)部流程
或者說,我們要研究 compile 這個(gè)函數(shù)是怎么生成的
注意,如果你沒有準(zhǔn)備好,請(qǐng)不要閱讀這篇文章
注意哦,會(huì)很繞,別暈了
好的,正文開始
首先,當(dāng)我們通過 Vue 新建一個(gè)實(shí)例的時(shí)候會(huì)調(diào)用Vue
所以從 Vue 函數(shù)入手
function Vue(){ // ..... vm.$mount(vm.$options.el); }
然后內(nèi)部的其他處理都可以忽視,直接定位到 vm.$mount,就是從這里開始去編譯的
繼續(xù)去查找這個(gè)函數(shù)
Vue.prototype.$mount = function(el) { var options = this.$options; if (!options.render) { var tpl= options.template; // 獲取模板字符串 if (tpl) { // 根據(jù)傳入的選擇器找到元素,然后拿到該元素內(nèi)的模板 // 本來有很多種獲取方式,但是為了簡單,我們簡化為一種,知道意思就可以了 tpl = document.querySelector(tpl).innerHTML; } if (tpl) { // 生成 render 函數(shù)保存 var ref = compileToFunctions(tpl, {},this); // 每一個(gè)組件,都有自己的 render options.render = ref.render options.staticRenderFns =ref.staticRenderFns; } } // 執(zhí)行上面生成的 render,生成DOM,掛載DOM,這里忽略不討論 return mount.call(this, el) };
compile 的主要作用就是,根據(jù) template 模板,生成 render 函數(shù)
那么到這里,整個(gè)流程就走完了,因?yàn)?render 已經(jīng)在這里生成了
我們觀察到
在上面這個(gè)函數(shù)中,主要就做了三件事
1 獲取 template 模板根據(jù)你傳入的參數(shù),來各種獲取 template 模板
這里應(yīng)該都看得懂了,根據(jù)DOM,或者根據(jù)選擇器
2 生成 render通過 compileToFunctions ,傳入 template
就可以生成 render 和 staticRenderFns
看著是挺簡單哦,就一個(gè) compileToFunctions,但是我告訴你,這個(gè)函數(shù)的誕生可不是這么容易的,兜兜轉(zhuǎn)轉(zhuǎn),十分曲折,相當(dāng)?shù)们蹚?fù)雜,沒錯(cuò),這就是我們下面研究的重點(diǎn)
但是這流程其實(shí)好像也沒有什么幫助?但是如果你閱讀源碼的話,或許可以對(duì)你理清源碼有些許幫助吧
再一次佩服 尤大的腦回路
3 保存 render保存在 vm.$options 上,用于在后面調(diào)用
下面就來說 compileToFunctions 的誕生史
注意,很繞很繞,做好心理準(zhǔn)備
首先我定位到 compileToFunctions,看到下面這段代碼
var ref$1 = createCompiler(); // compileToFunctions 會(huì)返回 render 函數(shù) 以及 staticRenderFns var compileToFunctions = ref$1.compileToFunctions;
于是我知道
compileToFunctions 是 createCompiler 執(zhí)行返回的!!
那么繼續(xù)定位 createCompiler
createCompilervar createCompiler = createCompilerCreator( function baseCompile(template, options) { 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 } } );
臥槽,又來一個(gè)函數(shù),別暈啊兄弟
不過,注意注意,這里是重點(diǎn),非常重
首先明確兩點(diǎn)
1、createCompiler 是 createCompilerCreator 生成的
2、給 createCompilerCreator 傳了一個(gè)函數(shù) baseCompile
baseCompile這個(gè) baseCompile 就是 生成 render 的大佬
看到里面包含了 渲染三巨頭,【parse,optimize,generate】
但是今天不是講這個(gè)的,這三個(gè)東西,每個(gè)內(nèi)容都十分巨大
這里先跳過,反正 baseCompile 很重要,會(huì)在后面被調(diào)用到,得先記著
然后,沒錯(cuò),我們又遇到了一個(gè) 函數(shù) createCompilerCreator ,定位它!
createCompilerCreatorfunction createCompilerCreator(baseCompile) { return function () { // 作用是合并選項(xiàng),并且調(diào)用 baseCompile function compile(template) { // baseCompile 就是 上一步傳入的,這里執(zhí)行得到 {ast,render,statickRenderFn} var compiled = baseCompile(template); return compiled } return { // compile 執(zhí)行會(huì)返回 baseCompile 返回的 字符串 render compile: compile, // 為了創(chuàng)建一層 緩存閉包,并且閉包保存 compile // 得到一個(gè)函數(shù),這個(gè)函數(shù)是 把 render 字符串包在 函數(shù) 中 compileToFunctions: createCompileToFunctionFn(compile) } } }
這個(gè)函數(shù)執(zhí)行過后,會(huì)返回一個(gè)函數(shù)
很明顯,返回的函數(shù)就 直接賦值 給了上面講的的 createCompiler
我們看下這個(gè)返回給 createCompiler 的函數(shù)里面都干了什么?
生成一個(gè)函數(shù) compile內(nèi)部存在一個(gè)函數(shù) compile,這個(gè)函數(shù)主要作用是
調(diào)用 baseCompile,把 baseCompile 執(zhí)行結(jié)果 return 出去
baseCompile 之前我們強(qiáng)調(diào)過的,就是那個(gè)生成 render 的大佬
忘記的,可以回頭看看,執(zhí)行完畢會(huì)返回
{ render,staticRenderFns }
返回 compileToFunctions 和 compile其實(shí) 返回的這兩個(gè)函數(shù)的作用大致都是一樣的
都是為了執(zhí)行上面那個(gè) 內(nèi)部 compile
但是為什么分出一個(gè) compileToFunctions 呢?還記得開篇我們的 compileToFunctions 嗎
就是那個(gè)在 vm.$mount 里我們要探索的東西啊
就是他這個(gè)吊毛,生成的 render 和 staticRenderFns
再看看那個(gè) 內(nèi)部 compile,可以看到他執(zhí)行完就是返回
{ render, staticRenderFns }
你看,內(nèi)部 compile 就是 【vm.$mount 執(zhí)行的 compileToFunctions】 啊
為什么 compileToFunctions 沒有直接賦值為 compile 呢!!
因?yàn)橐瞿0寰彺妫。?/b>可以看到,沒有直接讓 compileToFunctions = 內(nèi)部compile
而是把 內(nèi)部 compile 傳給了 createCompileToFunctionFn
沒錯(cuò) createCompileToFunctionFn 就是做緩存的
為了避免每個(gè)實(shí)例都被編譯很多次,所以做緩存,編譯一次之后就直接取緩存
createCompileToFunctionFn來看看內(nèi)部的源碼,緩存的代碼已經(jīng)標(biāo)紅
function createCompileToFunctionFn(compile) { // 作為緩存,防止每次都重新編譯 // template 字符串 為 key , 值是 render 和 staticRenderFns var cache = Object.create(null); return function compileToFunctions(template, options, vm) { var key = template; // 有緩存的時(shí)候直接取出緩存中的結(jié)果即可 if (cache[key]) return cache[key] // compile 是 createCompileCreator 傳入的compile var compiled = compile(template, options); var res = { // compiled.render 是字符串,需要轉(zhuǎn)成函數(shù) render : new Function(compiled.render) staticRenderFns : compiled.staticRenderFns.map(function(code) { return new Function(code, fnGenErrors) }); }; return (cache[key] = res) } }
額外:render 字符串變成可執(zhí)行函數(shù)
var res = { render: new Function(compiled.render) , staticRenderFns: compiled.staticRenderFns.map(function(code) { return new Function(code, fnGenErrors) }); };
這段代碼把 render 字符串可執(zhí)行函數(shù),因?yàn)閞ender生成的形態(tài)是一個(gè)字符串,如果后期要調(diào)用運(yùn)行,比如轉(zhuǎn)成函數(shù)
所以這里使用了 new Function() 轉(zhuǎn)化成函數(shù)
同理,staticRenderFns 也一樣,只不過他是數(shù)組,需要遍歷,逐個(gè)轉(zhuǎn)化成函數(shù)
他的緩存是怎么做的使用一個(gè) cache 閉包變量
template 為 key
生成的 render 作為 value
當(dāng)實(shí)例第一次渲染解析,就會(huì)被存到 cache 中
當(dāng)實(shí)例第二次渲染解析,那么就會(huì)從 cache 中直接獲取
什么時(shí)候?qū)嵗龝?huì)解析第二次?
比如 頁面A到頁面B,頁面B又轉(zhuǎn)到頁面A。
頁面A 這個(gè)實(shí)例,按理就需要解析兩次,但是有緩存之后就不會(huì)
理清思路也就是說,compileToFunctions 其實(shí)內(nèi)核就是 baseCompile!
不過 compileToFunctions 是經(jīng)過了 兩波包裝的 baseCompile
第一波包裝在 createCompilerCreator 中的 內(nèi)部 compile 函數(shù)中
內(nèi)部函數(shù)的作用是合并公共options和 自定義options ,但是相關(guān)代碼已經(jīng)省略,
另一個(gè)就是執(zhí)行 baseCompile
第二波包裝在 createCompileToFunctions 中,目的是進(jìn)行 緩存
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/106565.html
摘要:當(dāng)字符串開頭是時(shí),可以匹配匹配尾標(biāo)簽。從結(jié)尾,找到所在位置批量閉合。 寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧 【Vue原理】Compile - 源碼版 之 標(biāo)簽解析...
寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧 【Vue原理】Compile - 源碼版 之 Parse 主要流程 本文難度較繁瑣,需要耐心觀看,如果你對(duì) compile 源碼暫時(shí)...
摘要:一旦我們檢測到這些子樹,我們可以把它們變成常數(shù),這樣我們就不需要了在每次重新渲染時(shí)為它們創(chuàng)建新的節(jié)點(diǎn)在修補(bǔ)過程中完全跳過它們。否則,吊裝費(fèi)用將會(huì)增加好處大于好處,最好總是保持新鮮。 寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,...
摘要:還原的難度就在于變成模板了,因?yàn)槠渌氖裁吹仁窃獠粍?dòng)的哈哈,可是直接照抄最后鑒于本人能力有限,難免會(huì)有疏漏錯(cuò)誤的地方,請(qǐng)大家多多包涵,如果有任何描述不當(dāng)?shù)牡胤剑瑲g迎后臺(tái)聯(lián)系本人,有重謝 寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版...
摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理白話版終于到了要講白話的時(shí)候了 寫文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究...
閱讀 897·2019-08-30 15:54
閱讀 1466·2019-08-30 15:54
閱讀 2399·2019-08-29 16:25
閱讀 1292·2019-08-29 15:24
閱讀 749·2019-08-29 12:11
閱讀 2505·2019-08-26 10:43
閱讀 1227·2019-08-26 10:40
閱讀 465·2019-08-23 16:24