taro源码阅读

背景

在开发"租车项目"的时候使用了taro,生成了微信和支付宝两种小程序,在业务代码中只是个别部分做了逻辑处理,比如"登录"、"支付"等,其他地方的代码相似度达到了95%,非常好奇taro是怎么把一套代码分别转成了微信、支付宝项目的代码。

代码准备

github: github.com/nervjs/taro

项目结构分析和代码分析

打开packages目录,发现有好多种生成方式,如图:

图中包含了生成h5,react, vue,qq等平台的小程序的包,这些包构成了taro的核心功能

taro 命令

初始化一个新的项目,使用 taro init 可以新建一个 taro 项目,这个命令的入口是 packages/taro-cli/bin/taro,代码实现如下:

从上图可以看出,入口文件创建了一个新的 Cli 实例,然后运行了 run 命令。

Kernel 内核

接下来我们直接看看 Cli 实例(如下图)

从上图可以看出,Cli 实例的实现还是比较简单的,run 命令所做的工作就是解析命令行参数 - parseArgs

在解析入参后,会新建一个 Kernel(内核) 实例(Kernel@taro/cli 的灵魂,我们在后面会展开讲解),然后使用这个 内核 来创建项目(如下图)。

命令行调用的的 init 方法,实际上是调用了 kernel.run() 方法,所以我们接下来看看这个方法(如下图)

kernel.init

上图run 方法的实现比较复杂,我们需要进行逐行分析,首先是构造器中的初始化(如下图)

在上图中,initConfig 方法初始化了项目配置;而 initPaths 方法初始化了一些基础的项目路径,比如项目目录(appPath),依赖目录(nodeModulesPath),项目配置文件(configPath)。

kernel.initPresetsAndPlugins 方法比较关键,我们需要仔细看看代码实现(如下图)

在上图代码中:

  • 加载了所有的 presetsplugin,最后都以 plugin 的形式注册到 kernel.plugins 集合中。

plugins 其实就是预设与插件的集合,每一个 plugin 都包含了一个 apply 函数,执行该该函数可以导出对应的 Plugin 模块。(如下图)

初始化 Kernel 插件过程

下面我们来看一个初始化 Kernel 插件的过程(如下图)

初始化插件的 ctx(上下文),initPluginCtx 方法返回的是一个 Proxy 对象(如下图)

该方法创建了一个新的 Plugin 实例,然后在这个实例的基础上新建了一个 Proxy,在访问该实例的属性时,优先返回 methods 对象中的方法,其次是改写了 this 的实例方法。

然后我们回到下图的方法中继续解析

在上图的代码中,将 pluginCtx 作为入参,执行导出的插件(模块)函数。

我们还是以微信平台举例,在微信平台中,对应的 apply 函数是这样的(如下图):

我们上图可以看出,在 weapp 插件中导出的方法,反过来调用了 ctx.registerPlatform 方法进行平台注册,这也是设计模式中非常有名的 控制反转 Ioc 模式。

kernel.hooks

接下来我们看看 hooks(如下图)

hooks 的命令也很熟悉,是我们平时使用 taro-cli 时会使用的命令,在对应的执行命令将会调用对应的钩子。

kernel.platforms

接下来我们看看 platforms(如下图)

platforms 中包含了各个平台的编译代码,用于将 ReactVue 语法转换成对应的平台语法。

kernel.commands

最后我们来看看 commands(如下图)

从上图可以看出,commands 其实对应的就是 Taro 脚手架自带的各种命令了。

Kernel 生命周期钩子

至此,Kernel 就基本装配完成了,进入到 Kernel 的生命周期钩子。

Kernel 的前两个生命周期钩子是 onReadyonStart,并没有执行操作,开发者在自己编写插件时可以注册对应的钩子。

执行完上面两个钩子后,Kernel 开始执行 init 钩子(如下图)。

init 钩子

那么下面就进入到 Kernelinit 钩子(如下图)

init 钩子最后调用了 project.create(),开始创建新项目。在命令行中有以下提示(如下图):

从上图看到的欢迎语其实就是代码中的 Project 实例中的 init 函数(如下图)

从上图可以看到,在第 78 行时还调用了 ask 函数,而 ask 函数对应的就是创建新项目时的询问选项(如下图)。

Taro 以及很多脚手架的命令行交互功能都是通过 inquirer 库实现的。

在选项确认以后,将通过 git 拉取远程模板(如下图)

而我们在 taro 官方也能找到一个对应的模板仓库(如下图)

我们随便打开里面一个文件看看,例如 package.json(如下图)

项目创建完成后会自动安装依赖,然后就完成啦,init 钩子就执行完啦!

init 执行完成后,taro init 命令也就执行完成了,一个新的 Taro 项目创建成功啦!

最后,我们画一个流程图,来帮助大家理解一下吧。

小结

到这里,@tarojs/cli 就已经解析完成了,利用 Kernel + 注册插件 + 生命周期钩子函数 的实现方式,灵活的实现了各个不同的命令组合。

使用 taro 开发时的不同平台构建命令,比如微信小程序的构建命令 taro build --type weapp,也是使用 Kernel + 钩子 实现的,只不过调用的是 build 钩子和平台专属编译 Plugin

相关推荐
小镇程序员35 分钟前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
疯狂的沙粒1 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪1 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背1 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript
程楠楠&M1 小时前
node.js第三方Express 框架
前端·javascript·node.js·express
weiabc1 小时前
学习electron
javascript·学习·electron
想自律的露西西★2 小时前
用el-scrollbar实现滚动条,拖动滚动条可以滚动,但是通过鼠标滑轮却无效
前端·javascript·css·vue.js·elementui·前端框架·html5
白墨阳2 小时前
vue3:瀑布流
前端·javascript·vue.js
霍先生的虚拟宇宙网络2 小时前
webp 网页如何录屏?
开发语言·前端·javascript
温吞-ing2 小时前
第十章JavaScript的应用
开发语言·javascript·ecmascript