因为近期应用到 Taro 编写小程序,出于好奇,筹备研读一下 Taro 的源码。
首先从官网拉取最新的 Taro 源码,版本号为 3.0.18
,源码目录如下:
目录没什么特地的,咱们来重点关注一下 packages
目录中的外围包(如下图)
这些外围包形成了 Taro
,实现了 Taro
的多平台构建。
本次解析的模块是 tarojs/cli
篇,那咱们开始吧。
taro 命令
taro
命令是 @taro/cli
的外围指令,应用 taro init
能够新建一个 taro
我的项目,这个命令的入口是 packages/taro-cli/bin/taro
,代码实现如下:
从上图能够看出,入口文件创建了一个新的 Cli
实例,而后运行了 run
命令。
Kernel 内核
接下来咱们间接看看 Cli
实例(如下图)
从上图能够看出,Cli
实例的实现还是比较简单的,run
命令所做的工作就是解析命令行参数 – parseArgs
。
在解析入参后,会新建一个 Kernel(内核)
实例(Kernel
是 @taro/cli
的灵魂,咱们在前面会开展解说),而后应用这个 内核
来创立我的项目(如下图)。
而第 58
行调用的 init
办法,实际上是调用了 kernel.run()
办法,所以咱们接下来看看这个办法(如下图)
kernel.init
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 插件过程
上面咱们来看一个初始化 Kernel
插件的过程(如下图)
首先,在第 140
行代码处,初始化插件的 ctx
(上下文),initPluginCtx
办法返回的是一个 Proxy
对象(如下图)
从上图能够看出,该办法创立了一个新的 Plugin
实例,而后在这个实例的根底上新建了一个 Proxy
,在拜访该实例的属性时,优先返回 methods
对象中的办法,其次是改写了 this
的实例办法。
而后咱们回到下图的办法中持续解析
在上图的代码中,第 141
行,将 pluginCtx
作为入参,执行导出的插件(模块)函数。
咱们还是以微信平台举例,在微信平台中,对应的 apply
函数是这样的(如下图):
咱们上图能够看出,在 weapp
插件中导出的办法,反过来调用了 ctx.registerPlatform
办法进行平台注册,这也是设计模式中十分有名的 管制反转 Ioc
模式。
kernel.initPresetsAndPlugins
上面咱们来看看在实现 initPresetsAndPlugins
办法后,Kernel
的各个属性变成什么样了。
kernel.methods
上面展现了 methods
,这些办法看起来很眼生,在扩大 Taro
插件的文档中,是一些被用来批改编译过程的 API。
咱们来看看官网文档中是如何编写一个插件的吧(如下图)
看起来是不是很像咱们下面看到的微信平台的 plugin
代码,哈哈~
kernel.hooks
接下来咱们看看 hooks
(如下图)
hooks
的命令也很相熟,是咱们平时应用 taro-cli
时会应用的命令,在对应的执行命令将会调用对应的钩子。
kernel.platforms
接下来咱们看看 platforms
(如下图)
platforms
中蕴含了各个平台的编译代码,用于将 React
、Vue
语法转换成对应的平台语法。
kernel.commands
最初咱们来看看 commands
(如下图)
从上图能够看出,commands
其实对应的就是 Taro
脚手架自带的各种命令了。
Kernel 生命周期钩子
至此,Kernel
就根本拆卸实现了,进入到 Kernel
的生命周期钩子。
Kernel
的前两个生命周期钩子是 onReady
和 onStart
,并没有执行操作,开发者在本人编写插件时能够注册对应的钩子。
执行完下面两个钩子后,Kernel
开始执行 init
钩子(如下图)。
咱们须要逐行剖析一下上述代码:
- 第
233
行:创立了一条流水线 - AsyncSeriesWaterfallHook
工程,用于程序执行异步工作,AsyncSeriesWaterfallHook
的实现来自于tapable
库(如下图)
- 第
237
行:循环hooks
数组,将所有的钩子注册到waterfall
中。 - 第
255
行:执行流水线工作。
通过下面的剖析,咱们对 taro-cli
的执行流程基本上就能够理解了。
init 钩子
那么上面就进入到 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
激励一下吧!