vue原理Compile從新建實(shí)例到結(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)的模板
// 本來有很多種獲取方式,但是為了簡(jiǎn)單,我們簡(jiǎn)化為一種,知道意思就可以了
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
看著是挺簡(jiǎn)單哦,就一個(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
createCompiler
var 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 ,定位它!
createCompilerCreator
function 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寰彺妫。?/h3>
可以看到,沒有直接讓 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)行 緩存
以上就是vue原理Compile從新建實(shí)例到結(jié)束流程源碼的詳細(xì)內(nèi)容,更多關(guān)于vue原理Compile新建實(shí)例的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue實(shí)現(xiàn)固定定位圖標(biāo)滑動(dòng)隱藏效果
移動(dòng)端頁面,有時(shí)候會(huì)出現(xiàn)一些固定定位在底部圖標(biāo),比如購(gòu)物車等。這篇文章主要介紹了Vue制作固定定位圖標(biāo)滑動(dòng)隱藏效果,需要的朋友可以參考下2019-05-05
vue數(shù)組動(dòng)態(tài)刷新失敗問題及解決
這篇文章主要介紹了vue數(shù)組動(dòng)態(tài)刷新失敗問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
Element?el-date-picker?日期選擇器的使用
本文主要介紹了Element?el-date-picker?日期選擇器的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Vue-cli中post請(qǐng)求發(fā)送Json格式數(shù)據(jù)方式
這篇文章主要介紹了Vue-cli中post請(qǐng)求發(fā)送Json格式數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
快速解決vue動(dòng)態(tài)綁定多個(gè)class的官方實(shí)例語法無效的問題
今天小編就為大家分享一篇快速解決vue動(dòng)態(tài)綁定多個(gè)class的官方實(shí)例語法無效的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09
解決Vue 移動(dòng)端點(diǎn)擊出現(xiàn)300毫秒延遲的問題
這篇文章主要介紹了解決Vue 移動(dòng)端點(diǎn)擊出現(xiàn)300毫秒延遲的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-07-07
Vue實(shí)現(xiàn)刷新當(dāng)前頁面的三種方式總結(jié)
項(xiàng)目當(dāng)中如果做新增/修改/刪除等等操作通常情況下都需要刷新數(shù)據(jù)或者刷新當(dāng)前頁面。本文為大家整理了三種不同的實(shí)現(xiàn)方法,需要的可以參考一下2023-01-01
關(guān)于echarts?清空上一次加載的數(shù)據(jù)問題
這篇文章主要介紹了關(guān)于echarts?清空上一次加載的數(shù)據(jù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
微信小程序?qū)崙?zhàn)基于vue2實(shí)現(xiàn)瀑布流的代碼實(shí)例
瀑布流,又稱瀑布流式布局,是比較流行的一種網(wǎng)站頁面布局,視覺表現(xiàn)為參差不齊的多欄布局,隨著頁面滾動(dòng)條向下滾動(dòng),這種布局還會(huì)不斷加載數(shù)據(jù)塊并附加至當(dāng)前尾部,這篇文章主要介紹了微信小程序?qū)崙?zhàn),基于vue2實(shí)現(xiàn)瀑布流,需要的朋友可以參考下2022-12-12

