您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關(guān)Vue3編譯流程的源碼是怎樣的,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
前言:
Vue3 發(fā)布已經(jīng)很長(zhǎng)一段時(shí)間了,最近也有機(jī)會(huì)在公司項(xiàng)目中用上了 Vue3 + TypeScript + Vite
的技術(shù)棧,所以閑暇之余抽空也在抽空閱讀 Vue3 的源碼。本著好記性不如爛筆頭的想法,在閱讀源碼時(shí)順便記錄了一些筆記,也希望能爭(zhēng)取寫一些源碼閱讀筆記,幫助每個(gè)想看源碼但可能存在困難的同學(xué)減少理解成本。
Vue2.x 的源碼我也有過一些簡(jiǎn)單的閱讀,自 Vue3 重構(gòu)后,Vue 項(xiàng)目的目錄結(jié)構(gòu)也發(fā)生了很大的變化,各個(gè)功能模塊被分別放入了 packages
目錄下,職責(zé)更加清晰,通過目錄名就可以一目了然。今天將從 Vue 的入口文件開始,看看聲明了一個(gè) Vue 的單文件之后是如何被 compile-core
編譯核心模塊編譯成渲染函數(shù)的。
為了大家的閱讀方便,以及控制文章篇幅,我會(huì)把閱讀源碼時(shí)不太需要在意的邏輯進(jìn)行折疊,或者通過注釋 /* 忽略邏輯 */ 這樣的標(biāo)識(shí)進(jìn)行忽略處理。
我個(gè)人是不太喜歡在看源碼分析文章時(shí)一上來就懟出一大段代碼,這容易讓沒閱讀的同學(xué)有點(diǎn)懵逼。所以這個(gè)系列的文章我會(huì)盡量對(duì)關(guān)鍵的代碼畫出一張流程圖。目的還是一個(gè),幫助大家降低理解成本,同時(shí)也讓各位同學(xué)在下次自主閱讀時(shí)有張流程圖能參考。
我們會(huì)先從一個(gè) Vue 對(duì)象的入口來開始我們的源碼閱讀, packages/vue/index.ts
。這個(gè)入口文件的代碼比較簡(jiǎn)單,只有一個(gè) compileToFunction
函數(shù),但函數(shù)體內(nèi)的內(nèi)容卻又比較關(guān)鍵,所以先看一張圖,來理解這個(gè)函數(shù)體究竟完成了哪些事情。
在看完流程圖之后,我們來對(duì)照代碼一起看,我相信大部分同學(xué)在此時(shí)可能對(duì)下發(fā)圖片中的代碼一目了然了。
直接跳過所有代碼,看文件的末尾 35 行,調(diào)用了 registerRuntiomCompiler
函數(shù),將 compileToFunction
函數(shù)作為參數(shù)傳入,這行代碼即對(duì)應(yīng)流程圖的起始,通過依賴注入的方式,將 compile
函數(shù)注入至 runtime
運(yùn)行時(shí)中,依賴注入是一種比較巧妙的解耦方式,此時(shí)運(yùn)行時(shí)再調(diào)用 compile
編譯函數(shù),就是在調(diào)用當(dāng)前的 compileToFunction
函數(shù)了。
再看代碼中的第 17 行,調(diào)用了 compile-dom
庫提供的 compile
函數(shù),從返回值中解構(gòu)出了 code
變量。這個(gè)就是編譯器執(zhí)行之后生成的編譯結(jié)果,code 是編譯結(jié)果的其中一個(gè)參數(shù),是一個(gè)代碼字符串。比如
<template> <div> Hello World </div> </template>
這個(gè)簡(jiǎn)單的模板,在經(jīng)過編譯后,code
返回的字符串為
const _Vue = Vue return function render(_ctx, _cache) { with (_ctx) { const { openBlock: _openBlock, createBlock: _createBlock } = _Vue return (_openBlock(), _createBlock("div", null, "Hello World")) } }
這個(gè)神奇的 compile
函數(shù)內(nèi)部的奧妙在之后我會(huì)詳細(xì)講解。
在拿到這個(gè)這個(gè)代碼字符串的結(jié)果后,我們?cè)夙樦a往下看,第 25 行聲明了一個(gè) render
變量,并且將生成的代碼字符串 code 作為參數(shù)傳入了 new Function
構(gòu)造函數(shù)。這就是流程圖中的倒數(shù)第二步,生成了 render
函數(shù)??梢詫⑽曳旁谏厦娴?code
字符串格式化,能夠發(fā)現(xiàn) render
函數(shù)是一個(gè)柯里化的函數(shù),返回了一個(gè)函數(shù),函數(shù)內(nèi)部通過 with 來擴(kuò)展作用域鏈。
而最后入口文件返回了 render
變量,并且順手緩存了 render 函數(shù)。
上方源碼的第 1 行,我們看到入口文件創(chuàng)建了一個(gè) compileCache
對(duì)象,用以緩存 compileToFunction
函數(shù)生成的 render
函數(shù),將 template
參數(shù)作為緩存的 key, 并在 11 行的位置有一個(gè) if 分支做緩存的判斷,如果該模板之前被緩存過,則不再進(jìn)行編譯,直接返回緩存中的 render
函數(shù),以此提高性能。
至此 package/vue/index.ts
的入口文件就解讀完了。相信大家也都看出來了,最有意思的部分就是調(diào)用 compile 函數(shù)編譯出了代碼字符串,所以接下來我將圍繞 compile
函數(shù)來接著嘮。compile 函數(shù)牽扯到 compile-dom
和 compile-core
兩個(gè)模塊,本篇文章我只會(huì)解讀關(guān)鍵流程。細(xì)節(jié)分析的話會(huì)放在后續(xù)文章中。一起來看一下 compile 的運(yùn)行流程:
compile
函數(shù)內(nèi)部直接返回 baseCompile
函數(shù)的結(jié)果,而 baseCompile
函數(shù)在執(zhí)行過程中會(huì)生成 AST 抽象語法樹,并調(diào)用 transform
對(duì) 每個(gè) AST 節(jié)點(diǎn)進(jìn)行處理,例如轉(zhuǎn)換vOn、v-if、v-for 等指令,最后將處理后的 AST 抽象語法樹通過 generate 函數(shù)生成之前提及的代碼字符串,并返回編譯結(jié)果,至此 compile
函數(shù)執(zhí)行完畢。明白了大體的流程后,接著來看源碼。
compile
函數(shù)的源碼路徑是 packages/compiler-dom/src/index.ts
, 我們看到在 compile
的函數(shù)體內(nèi),直接 return 了 baseCompile
的處理結(jié)果。而 baseCompile
的源碼路徑是 packages/compiler-core/src/compile.ts
。為什么會(huì)有 baseCompile
這樣的命名呢?因?yàn)?code> compile-core 是編譯的核心模塊,接受外部的參數(shù)來按照規(guī)則完成編譯,而 compile-dom 是專門處理瀏覽器場(chǎng)景下的編譯,在這個(gè)模塊下導(dǎo)出的 compile 函數(shù)是入口文件真正接收的編譯函數(shù)。而 compile-dom
中的 compile 函數(shù)相對(duì) baseCompile
也是更高階的一個(gè)編譯器。例如當(dāng) Vue 在 weex 在 iOS 或者 Android 這些 Native App 中工作時(shí),compile-dom
可能會(huì)被相關(guān)的移動(dòng)端編譯庫來取代。
順著往下一起看一下 baseCompile 函數(shù):
先從函數(shù)聲明中來看,baseCompile
接收 template
模板以及上層高階編譯器中處理過 options
編譯選項(xiàng),最終返回一個(gè) CodegenResult
類型的編譯結(jié)果。
export interface CodegenResult { code: string preamble: string ast: RootNode map?: RawSourceMap }
通過 CodegenResult
的接口聲明能清晰的看到返回結(jié)果中存在 code
代碼字符串、處理后的 AST 抽象語法樹,以及 sourceMap。
看上方源碼的第 12 行,判斷 template
模板是否為字符串,如果是的話則會(huì)對(duì)字符串進(jìn)行解析,否則直接將 template
作為 AST 。其實(shí)我們平時(shí)在寫的單文件 vue 代碼,都是以字符串的形式傳遞進(jìn)去的。
接下來源碼是 16 行調(diào)用了 transform
函數(shù),以及傳入了指令轉(zhuǎn)換、節(jié)點(diǎn)轉(zhuǎn)換等工具函數(shù),對(duì)由模板生成的 AST 進(jìn)行轉(zhuǎn)換。
最終的 32 行位置,我們將轉(zhuǎn)換好的 AST 傳入 generate,生成 CodegenResult
類型的返回結(jié)果。
在 compile-core 模塊中,AST 解析、transform
、codegen
、compile
、parse
這些函數(shù)都是一個(gè)單獨(dú)的小模塊,內(nèi)部的實(shí)現(xiàn)都非常精妙,在編譯器的后續(xù)文章中,會(huì)逐個(gè)進(jìn)行介紹。
本文通過從入口文件開始,對(duì)編譯的大體流程進(jìn)行解釋,希望可以幫助大家在閱讀編譯器這個(gè)模塊的代碼時(shí)能有一個(gè)清晰的流程概念,配合流程圖食用更香喲。
以上就是Vue3編譯流程的源碼是怎樣的,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(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)容。