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 鼓励一下吧!

相关文章
相关标签/搜索