vue源碼分析學(xué)習(xí)--編譯階段匯總

前言
我們從vue-loader
開(kāi)始,一步一步分析到vue/compiler-core
包源碼,那么這編譯階段我們就分析完了(這里忽略compiler-ssr
) 。
這里涉及到了幾個(gè)包,我們來(lái)整理一下。
文章在知乎上,阿B的文章編輯器是真的那啥。。。。。
https://zhuanlan.zhihu.com/p/600569554
入口
https://zhuanlan.zhihu.com/p/572808664
我這里分析的是基于webpack
的,所以入口是vue-loader
。
簡(jiǎn)單的說(shuō)下這里面做了什么。
首先在調(diào)用vue-loader
之前需要先安裝vueLoaderPlugin
vueLoaderPlugin
這里面主要做了幾件事
將
vue-loader
標(biāo)志位置為true
,這樣webpack
編譯器就可以調(diào)用vue-loader
了。復(fù)制一份包含所有
loader
的rules
(不包含vue-loader
),分別給它們加上resouce
和resourceQuery
,這倆是用來(lái)確認(rèn)請(qǐng)求的vue
資源是否可以被該loader
處理的,這里不用擔(dān)心會(huì)影響到其它的loader
。往
copy
的rules
中注入templateLoader
,這樣在資源是vue
的template
的時(shí)候就會(huì)被識(shí)別到并且最終處理成render function
。注意render function
是js
代碼,所以它也需要被其它loader
識(shí)別處理。插入
css-post-loader
用來(lái)處理vue
的style
,這里需要注意順序,得在style loader
和css loader
之前執(zhí)行。改寫(xiě)
request
資源的路徑,這樣才能被vue-loader
識(shí)別到。覆蓋原來(lái)
webpack compiler
的rules
。
然后回到vue-loader
中,根據(jù)請(qǐng)求路徑返回對(duì)應(yīng)的資源。這里說(shuō)下script
部分沒(méi)有loader
,所以在vue-loader
識(shí)別到請(qǐng)求的參數(shù)是js
的才會(huì)調(diào)用@vue/compiler-sfc
的compileScript
來(lái)處理并返回內(nèi)容。
不同的loader
調(diào)用不同的@vue/compiler-sfc
方法處理不同的部分:
compileTemplate
:被templateLoader
調(diào)用,用來(lái)處理template
部分;compileScript
:被vue-loader
自身調(diào)用,用來(lái)處理script
部分;compileStyle
:被css-post-style
調(diào)用,用來(lái)處理style
部分;
處理完后的資源在vue-loader
做runtime
拼接之后再傳遞給下一個(gè)loader
。
compiler-sfc
那么入口分析完了,就輪到被調(diào)用的包了。
https://zhuanlan.zhihu.com/p/591218245
compileScript
https://zhuanlan.zhihu.com/p/589386136
上面說(shuō)了處理script
部分是通過(guò)compileScript
這個(gè)方法來(lái)處理的,在這個(gè)文件中核心是調(diào)用babel
來(lái)處理js
代碼轉(zhuǎn)換為AST
節(jié)點(diǎn),然后基于AST
?節(jié)點(diǎn)做處理。
場(chǎng)景很多,這里簡(jiǎn)單的說(shuō)下:
沒(méi)有
setup
語(yǔ)法糖block
的script
,按optionApi
的方式來(lái)處理有
setup
語(yǔ)法糖block
的script
且有non setup block
的script
,都處理,其中non setup block
處理之后整合到setup block
的頂部。而setup block
處理成非語(yǔ)法糖的寫(xiě)法。
注意:所有的語(yǔ)法糖都只是方便使用,而在編譯階段的都會(huì)轉(zhuǎn)化成原本的寫(xiě)法。
compileTemplate
https://zhuanlan.zhihu.com/p/590240402
這里沒(méi)怎么處理,調(diào)用的是在compiler-dom
包里,但是核心處理是在compiler-core
里。
compileStyle
https://zhuanlan.zhihu.com/p/591215117
核心是調(diào)用postcss
[1]?來(lái)處理。
然后如果有lang
的話,調(diào)用對(duì)應(yīng)的預(yù)處理器先。比如lang="scss"
,那么再開(kāi)始調(diào)用postcss
解析之前會(huì)先調(diào)用sass-loader
來(lái)處理一遍。
其他沒(méi)什么好說(shuō)的了,但是有個(gè)點(diǎn)這里要提一下,就是處理cssVar
。
vue
在調(diào)用之前自定義了一個(gè)postcss
?插件cssVarsPlugin
,這個(gè)插件就是用來(lái)處理vue3.x
中css
變量的方式。比如color: v-bind(color)
會(huì)變成var(--color)
的方式。
vue3.x
中使用css
變量:?SFC CSS Features | Vue.js (vuejs.org)
另外還有一個(gè)點(diǎn),就是scopeId
。
每一個(gè)sfc
文件都會(huì)有一個(gè)對(duì)應(yīng)的scope Id
,如果這個(gè)style block
有要求scoped
,那么就會(huì)給每個(gè)css rule
加上scopeId
。
compiler-dom
https://zhuanlan.zhihu.com/p/594633057
你應(yīng)該注意到了,上面compiler-sfc
處理完了之后其實(shí)只剩下了template
沒(méi)有處理好了,而script、style
都已經(jīng)是可以運(yùn)行在瀏覽器中的最終代碼了。
而接下來(lái)compiler-dom
以及compiler-core
其實(shí)都是在處理template
相關(guān)的,因?yàn)?code>template只是一段長(zhǎng)得像html
的js
代碼。
不過(guò)尷尬的是html
解析引擎不認(rèn)識(shí)它,js
的引擎也不認(rèn)識(shí)它。
所以這里就需要自行去把這template
處理成js
代碼。
而compiler-dom
這個(gè)包是compiler-core
的上游包,也就是基于compiler-core
封裝的,加上了一些插件。
這個(gè)包沒(méi)啥好說(shuō)的,感興趣的可以去看下。
不過(guò)這里有幾個(gè)點(diǎn)需要注意:
v-on/v-model
等的modifiers
也就是修飾符的處理是放到這個(gè)包里的過(guò)濾
style\script
標(biāo)簽也是放在這個(gè)包里,這倆標(biāo)簽不應(yīng)該再出現(xiàn)了(svg
標(biāo)簽里的style
和這里的不同)是否可以
staticStringify
的判斷和處理都是在這個(gè)包中。
compiler-core
https://zhuanlan.zhihu.com/p/600566832
這個(gè)包是處理template
的核心部分
我直接把之前畫(huà)的圖貼出來(lái),應(yīng)該已經(jīng)很清晰了。

可以參考babel
的core
包的架構(gòu)。
簡(jiǎn)單地說(shuō)就是把template
這一段字符串通過(guò)parse
變成AST
,然后通過(guò)transform
遍歷所有節(jié)點(diǎn)通過(guò)不同插件處理節(jié)點(diǎn),接著通過(guò)generate
生成render function
。這個(gè)時(shí)候的render function
已經(jīng)是瀏覽器可以執(zhí)行的runtime js
代碼了。
優(yōu)化
這里面關(guān)于優(yōu)化的東西我都沒(méi)有分析,這里就簡(jiǎn)單說(shuō)下我遇到過(guò)的優(yōu)化:
cache
,這個(gè)cache
有runtime
的,比如指令的value
是一個(gè)表達(dá)式,那么表達(dá)式的結(jié)果其實(shí)是可以緩存的,這樣每次調(diào)用返回緩存即可。而compile
階段的緩存更多的是和hot-reload
搭配的,在下一次編譯的時(shí)候可以過(guò)濾掉。staticHoist
,這個(gè)我們是有分析的,因?yàn)樵谥髁鞒讨小Mㄟ^(guò)判斷不同的點(diǎn),比如屬性是否是靜態(tài)之類(lèi)的來(lái)判斷這個(gè)節(jié)點(diǎn)是否每次都需要create
,如果不需要就把它提升到頂部,這樣就只需要create
一次。注意這個(gè)是runtime
的優(yōu)化。另外這里可以hoist
不只有dom
節(jié)點(diǎn),還有屬性節(jié)點(diǎn)等也是可以的。staticStringify
,這個(gè)是在staticHoist
的基礎(chǔ)上,如果不滿(mǎn)足hoist
的條件那就一定不滿(mǎn)足stringify
的條件。stringify
使用的是innerHTML
的方式插入,所以只有這一塊代碼中節(jié)點(diǎn)數(shù)量/指令數(shù)量達(dá)到了一定的個(gè)數(shù)時(shí)才會(huì)stringify
,因?yàn)槿绻麛?shù)量少,其實(shí)innerHTML
插入的性能沒(méi)啥提升。PatchFlag
,也就是插旗,給不同類(lèi)型的節(jié)點(diǎn)插旗,這樣可以快速定位需要如何處理,在patch
的時(shí)候diff
就能繞過(guò)一些邏輯,減少diff
的時(shí)間和損耗。track
,實(shí)際上和插旗一樣,跟蹤上面的flag
,如果沒(méi)有flag
的節(jié)點(diǎn),那diff
階段就可以skip
了。
總結(jié)
那么編譯階段的源碼我們就都分析完了,這一套分析下來(lái)其實(shí)我個(gè)人學(xué)到的東西非常非常多,整個(gè)vue
的編譯流程也比以前清晰的多。
現(xiàn)在也不再覺(jué)得這是魔法了~
最后如果覺(jué)得對(duì)你有幫助的話請(qǐng)務(wù)必點(diǎn)個(gè)贊~
最后的最后,祝大家新春快樂(lè)!!