Taro 源碼解讀 - @tarojs/cli 篇

由於近期使用到 Taro 編寫小程序,出於好奇,準備研讀一下 Taro 的源碼。html

首先從官網拉取最新的 Taro 源碼,版本號爲 3.0.18,源碼目錄以下:node

image

目錄沒什麼特別的,咱們來重點關注一下 packages 目錄中的核心包(以下圖)git

image

這些核心包構成了 Taro,實現了 Taro 的多平臺構建。es6

本次解析的模塊是 tarojs/cli 篇,那咱們開始吧。github

taro 命令

taro 命令是 @taro/cli 的核心指令,使用 taro init 能夠新建一個 taro 項目,這個命令的入口是 packages/taro-cli/bin/taro,代碼實現以下:json

image

從上圖能夠看出,入口文件建立了一個新的 Cli 實例,而後運行了 run 命令。小程序

Kernel 內核

接下來咱們直接看看 Cli 實例(以下圖)微信小程序

image

從上圖能夠看出,Cli 實例的實現仍是比較簡單的,run 命令所作的工做就是解析命令行參數 - parseArgs設計模式

在解析入參後,會新建一個 Kernel(內核) 實例(Kernel@taro/cli 的靈魂,咱們在後面會展開講解),而後使用這個 內核 來建立項目(以下圖)。數組

image

而第 58 行調用的 init 方法,其實是調用了 kernel.run() 方法,因此咱們接下來看看這個方法(以下圖)

image

kernel.init

run 方法的實現比較複雜,咱們須要進行逐行分析,首先是第 266 行的 kernel.init() 方法(以下圖)

image

在上圖的 init 方法中,initConfig 方法初始化了項目配置;而 initPaths 方法初始化了一些基礎的項目路徑,好比項目目錄(appPath),依賴目錄(nodeModulesPath),項目配置文件(configPath)。

kernel.initPresetsAndPlugins 方法比較關鍵,咱們須要仔細看看代碼實現(以下圖)

image

在上圖代碼中:

  • 89~90 行中,收集了全部的預設和插件集合。
  • 91 行中,爲 require 方法註冊了 babel,爲它加上一個鉤子。此後,每當使用 require 加載.js、.jsx、.es 和.es6 後綴名的文件,就會先用 Babel 進行轉碼。 —— Babel 入門教程
  • 97~98 行中,加載了全部的 presetsplugin,最後都以 plugin 的形式註冊到 kernel.plugins 集合中。

plugins 其實就是預設與插件的集合,每個 plugin 都包含了一個 apply 函數,執行該該函數能夠導出對應的 Plugin 模塊。(以下圖)

image

image

初始化 Kernel 插件過程

下面咱們來看一個初始化 Kernel 插件的過程(以下圖)

image

首先,在第 140 行代碼處,初始化插件的 ctx(上下文),initPluginCtx 方法返回的是一個 Proxy 對象(以下圖)

image

從上圖能夠看出,該方法建立了一個新的 Plugin 實例,而後在這個實例的基礎上新建了一個 Proxy,在訪問該實例的屬性時,優先返回 methods 對象中的方法,其次是改寫了 this 的實例方法。

而後咱們回到下圖的方法中繼續解析

image

在上圖的代碼中,第 141 行,將 pluginCtx 做爲入參,執行導出的插件(模塊)函數。

咱們仍是以微信平臺舉例,在微信平臺中,對應的 apply 函數是這樣的(以下圖):

image

咱們上圖能夠看出,在 weapp 插件中導出的方法,反過來調用了 ctx.registerPlatform 方法進行平臺註冊,這也是設計模式中很是有名的 控制反轉 Ioc 模式。

kernel.initPresetsAndPlugins

下面咱們來看看在完成 initPresetsAndPlugins 方法後,Kernel 的各個屬性變成什麼樣了。

kernel.methods

下面展現了 methods,這些方法看起來很眼熟,在擴展 Taro 插件的文檔中,是一些被用來修改編譯過程的 API。

image

咱們來看看官方文檔中是如何編寫一個插件的吧(以下圖)

image

看起來是否是很像咱們上面看到的微信平臺的 plugin 代碼,哈哈~

kernel.hooks

接下來咱們看看 hooks(以下圖)

image

hooks 的命令也很熟悉,是咱們平時使用 taro-cli 時會使用的命令,在對應的執行命令將會調用對應的鉤子。

kernel.platforms

接下來咱們看看 platforms(以下圖)

image

platforms 中包含了各個平臺的編譯代碼,用於將 ReactVue 語法轉換成對應的平臺語法。

kernel.commands

最後咱們來看看 commands(以下圖)

image

從上圖能夠看出,commands 其實對應的就是 Taro 腳手架自帶的各類命令了。

Kernel 生命週期鉤子

至此,Kernel 就基本裝配完成了,進入到 Kernel 的生命週期鉤子。

Kernel 的前兩個生命週期鉤子是 onReadyonStart,並無執行操做,開發者在本身編寫插件時能夠註冊對應的鉤子。

執行完上面兩個鉤子後,Kernel 開始執行 init 鉤子(以下圖)。

image

咱們須要逐行分析一下上述代碼:

  • 233 行:建立了一條 流水線 - AsyncSeriesWaterfallHook 工程,用於順序執行異步任務,AsyncSeriesWaterfallHook 的實現來自於 tapable 庫(以下圖)

image

  • 237 行:循環 hooks 數組,將全部的鉤子註冊到 waterfall 中。

  • 255 行:執行流水線任務。

通過上面的分析,咱們對 taro-cli 的執行流程基本上就能夠了解了。

init 鉤子

那麼下面就進入到 Kernelinit 鉤子(以下圖)

image

init 鉤子最後調用了 project.create(),開始建立新項目。在命令行中有如下提示(以下圖):

image

從上圖看到的歡迎語其實就是代碼中的 Project 實例中的 init 函數(以下圖)

image

從上圖能夠看到,在第 78 行時還調用了 ask 函數,而 ask 函數對應的就是建立新項目時的詢問選項(以下圖)。

image

Taro 以及不少腳手架的命令行交互功能都是經過 inquirer 庫實現的。

在選項確認之後,將經過 git 拉取遠程模板(以下圖)

image

而咱們在 taro 官方也能找到一個對應的模板倉庫(以下圖)

image

咱們隨便打開裏面一個文件看看,例如 package.json(以下圖)

image

從上圖能夠看出,在模板文件倉庫的文件是 tmpl 的後綴名,而後經過相似於 ejs 的模板語法根據不一樣的配置項將其改寫成對應的配置文件(以下圖)。

image

從上圖能夠看出,最後經過 create 中的 createApp 函數,將文件寫入到目錄中,完成項目的建立。

項目建立完成後會自動安裝依賴,而後就完成啦,init 鉤子就執行完啦!

init 執行完成後,taro init 命令也就執行完成了,一個新的 Taro 項目建立成功啦!

最後,咱們畫一個流程圖,來幫助你們理解一下吧。

image

小結

到這裏,@tarojs/cli 就已經解析完成了,這部分源碼的實現仍是比較精闢的,利用 Kernel + 註冊插件 + 生命週期鉤子函數 的實現方式,靈活的實現了各個不一樣的命令組合。

可能有些童鞋已經猜到了,使用 taro 開發時的不一樣平臺構建命令,好比微信小程序的構建命令 taro build --type weapp,也是使用 Kernel + 鉤子 實現的,只不過調用的是 build 鉤子和平臺專屬編譯 Plugin,這部分代碼感興趣的童鞋能夠自行閱讀一下~

最後一件事

若是您已經看到這裏了,但願您仍是點個贊再走吧~

您的點贊是對做者的最大鼓勵,也可讓更多人看到本篇文章!

若是以爲本文對您有幫助,請幫忙在 github 上點亮 star 鼓勵一下吧!

相關文章
相關標籤/搜索