由於近期使用到 Taro 編寫小程序,出於好奇,準備研讀一下 Taro 的源碼。html
首先從官網拉取最新的 Taro 源碼,版本號爲 3.0.18
,源碼目錄以下:node
目錄沒什麼特別的,咱們來重點關注一下 packages
目錄中的核心包(以下圖)git
這些核心包構成了 Taro
,實現了 Taro
的多平臺構建。es6
本次解析的模塊是 tarojs/cli
篇,那咱們開始吧。github
taro
命令是 @taro/cli
的核心指令,使用 taro init
能夠新建一個 taro
項目,這個命令的入口是 packages/taro-cli/bin/taro
,代碼實現以下:json
從上圖能夠看出,入口文件建立了一個新的 Cli
實例,而後運行了 run
命令。小程序
接下來咱們直接看看 Cli
實例(以下圖)微信小程序
從上圖能夠看出,Cli
實例的實現仍是比較簡單的,run
命令所作的工做就是解析命令行參數 - parseArgs
。設計模式
在解析入參後,會新建一個 Kernel(內核)
實例(Kernel
是 @taro/cli
的靈魂,咱們在後面會展開講解),而後使用這個 內核
來建立項目(以下圖)。數組
而第 58
行調用的 init
方法,其實是調用了 kernel.run()
方法,因此咱們接下來看看這個方法(以下圖)
run
方法的實現比較複雜,咱們須要進行逐行分析,首先是第 266
行的 kernel.init()
方法(以下圖)
在上圖的 init
方法中,initConfig
方法初始化了項目配置;而 initPaths
方法初始化了一些基礎的項目路徑,好比項目目錄(appPath
),依賴目錄(nodeModulesPath
),項目配置文件(configPath
)。
kernel.initPresetsAndPlugins
方法比較關鍵,咱們須要仔細看看代碼實現(以下圖)
在上圖代碼中:
89~90
行中,收集了全部的預設和插件集合。91
行中,爲 require
方法註冊了 babel
,爲它加上一個鉤子。此後,每當使用 require
加載.js、.jsx、.es 和.es6 後綴名的文件,就會先用 Babel 進行轉碼。 —— Babel 入門教程97~98
行中,加載了全部的 presets
和 plugin
,最後都以 plugin
的形式註冊到 kernel.plugins
集合中。plugins 其實就是預設與插件的集合,每個
plugin
都包含了一個apply
函數,執行該該函數能夠導出對應的Plugin
模塊。(以下圖)
下面咱們來看一個初始化 Kernel
插件的過程(以下圖)
首先,在第 140
行代碼處,初始化插件的 ctx
(上下文),initPluginCtx
方法返回的是一個 Proxy
對象(以下圖)
從上圖能夠看出,該方法建立了一個新的 Plugin
實例,而後在這個實例的基礎上新建了一個 Proxy
,在訪問該實例的屬性時,優先返回 methods
對象中的方法,其次是改寫了 this
的實例方法。
而後咱們回到下圖的方法中繼續解析
在上圖的代碼中,第 141
行,將 pluginCtx
做爲入參,執行導出的插件(模塊)函數。
咱們仍是以微信平臺舉例,在微信平臺中,對應的 apply
函數是這樣的(以下圖):
咱們上圖能夠看出,在 weapp
插件中導出的方法,反過來調用了 ctx.registerPlatform
方法進行平臺註冊,這也是設計模式中很是有名的 控制反轉 Ioc
模式。
下面咱們來看看在完成 initPresetsAndPlugins
方法後,Kernel
的各個屬性變成什麼樣了。
下面展現了 methods
,這些方法看起來很眼熟,在擴展 Taro
插件的文檔中,是一些被用來修改編譯過程的 API。
咱們來看看官方文檔中是如何編寫一個插件的吧(以下圖)
看起來是否是很像咱們上面看到的微信平臺的 plugin
代碼,哈哈~
接下來咱們看看 hooks
(以下圖)
hooks
的命令也很熟悉,是咱們平時使用 taro-cli
時會使用的命令,在對應的執行命令將會調用對應的鉤子。
接下來咱們看看 platforms
(以下圖)
platforms
中包含了各個平臺的編譯代碼,用於將 React
、Vue
語法轉換成對應的平臺語法。
最後咱們來看看 commands
(以下圖)
從上圖能夠看出,commands
其實對應的就是 Taro
腳手架自帶的各類命令了。
至此,Kernel
就基本裝配完成了,進入到 Kernel
的生命週期鉤子。
Kernel
的前兩個生命週期鉤子是 onReady
和 onStart
,並無執行操做,開發者在本身編寫插件時能夠註冊對應的鉤子。
執行完上面兩個鉤子後,Kernel
開始執行 init
鉤子(以下圖)。
咱們須要逐行分析一下上述代碼:
233
行:建立了一條 流水線 - AsyncSeriesWaterfallHook
工程,用於順序執行異步任務,AsyncSeriesWaterfallHook
的實現來自於 tapable
庫(以下圖)第 237
行:循環 hooks
數組,將全部的鉤子註冊到 waterfall
中。
第 255
行:執行流水線任務。
通過上面的分析,咱們對 taro-cli
的執行流程基本上就能夠了解了。
那麼下面就進入到 Kernel
的 init
鉤子(以下圖)
init
鉤子最後調用了 project.create()
,開始建立新項目。在命令行中有如下提示(以下圖):
從上圖看到的歡迎語其實就是代碼中的 Project
實例中的 init
函數(以下圖)
從上圖能夠看到,在第 78
行時還調用了 ask
函數,而 ask
函數對應的就是建立新項目時的詢問選項(以下圖)。
Taro
以及不少腳手架的命令行交互功能都是經過inquirer
庫實現的。
在選項確認之後,將經過 git
拉取遠程模板(以下圖)
而咱們在 taro
官方也能找到一個對應的模板倉庫(以下圖)
咱們隨便打開裏面一個文件看看,例如 package.json
(以下圖)
從上圖能夠看出,在模板文件倉庫的文件是 tmpl
的後綴名,而後經過相似於 ejs
的模板語法根據不一樣的配置項將其改寫成對應的配置文件(以下圖)。
從上圖能夠看出,最後經過 create
中的 createApp
函數,將文件寫入到目錄中,完成項目的建立。
項目建立完成後會自動安裝依賴,而後就完成啦,init
鉤子就執行完啦!
在 init
執行完成後,taro init
命令也就執行完成了,一個新的 Taro
項目建立成功啦!
最後,咱們畫一個流程圖,來幫助你們理解一下吧。
到這裏,@tarojs/cli
就已經解析完成了,這部分源碼的實現仍是比較精闢的,利用 Kernel
+ 註冊插件
+ 生命週期鉤子函數
的實現方式,靈活的實現了各個不一樣的命令組合。
可能有些童鞋已經猜到了,使用 taro
開發時的不一樣平臺構建命令,好比微信小程序的構建命令 taro build --type weapp
,也是使用 Kernel
+ 鉤子
實現的,只不過調用的是 build
鉤子和平臺專屬編譯 Plugin
,這部分代碼感興趣的童鞋能夠自行閱讀一下~
若是您已經看到這裏了,但願您仍是點個贊再走吧~
您的點贊是對做者的最大鼓勵,也可讓更多人看到本篇文章!
若是以爲本文對您有幫助,請幫忙在 github 上點亮 star
鼓勵一下吧!