第 57 期 - Taro 插件机制原理与源码解析
摘要
文章以 Taro 4.0.2 版本为例,深入解析了其插件机制,包括插件的合并、注册、解析、调用以及相关函数的实现,还提及了一些工具函数的作用,并通过示例展示了插件机制的运行流程。
1. 前言
文章开篇介绍了 Taro 的版本情况,截至 2024 - 07 - 17,taro 正式版是 3.6.34,4.0 版本已经发布。作者计划写一个 taro 源码揭秘系列,本文是其中关于揭开整个架构的插件系统秘密的部分。并且列出了这个系列计划涵盖的文章主题,包括 Taro 架构入口 CLI、项目初始化背后原理、编译打包实现、发布订阅机制实现等。同时提到学习本文可以学到如何合并预设插件集合和插件、插件如何注册和调用等内容。还指出关于克隆项目、环境准备、调试代码等内容可参考系列的第一篇文章。
2. new Kernal 构造函数
- 首先定义了
IKernelOptions
接口,Kernel
类继承自EventEmitter
。 - 在
Kernel
类的构造函数中,根据传入的options
参数初始化了一系列属性,如debugger
、appPath
、optsPresets
等。并且初始化了一些数据结构,如hooks
、methods
、commands
、platforms
等,还调用了一些初始化方法,如initHelper
、initConfig
、initPaths
、initRunnerUtils
。 - 以
init
命令为例,展示了CLI
调用new Kernal
的过程,包括如何注入插件等操作。
3. initPresetsAndPlugins 初始化预设插件集合和插件
- 这个方法主要做了几件事:
- 合并预设插件集合和插件:通过
mergePlugins
函数合并CLI
、用户项目和全局插件中的预设插件集合和插件。 - 转换全局配置里的插件集和插件为对象:使用
convertPluginsToObject
函数进行转换。 - 在非测试环境下,使用
@swc/register
编译ts
等转换成commonjs
,方便require
读取文件。 - 解析预设插件集合和解析插件:分别调用
resolvePresets
和resolvePlugins
函数。
- 合并预设插件集合和插件:通过
3.1 工具函数 mergePlugins、convertPluginsToObject
isNpmPkg
函数用于判断一个名称是否为npm
包名。getPluginPath
函数用于获取插件路径,要求插件和预设配置必须为绝对路径或者包名。convertPluginsToObject
函数将插件项数组转换为对象,遍历数组,根据不同类型的项设置对象的属性。mergePlugins
函数先将源插件和目标插件转换为对象,再进行合并。
4. resolvePresets 解析预设插件集合
- 这个方法主要做了两件事:
- 解析
cli
和项目配置的预设插件集合,调用resolvePresetsOrPlugins
函数得到resolvedCliAndProjectPresets
,然后逐个初始化这些预设插件。 - 解析全局的预设插件集合,调用
resolvePresetsOrPlugins
函数得到resolvedGlobalPresets
,然后逐个初始化这些预设插件。
- 解析
4.1 resolvePresetsOrPlugins 解析插件集或者插件
- 主要功能是获取插件路径并处理插件加载错误。使用
resolve.sync
获取路径,如果找不到插件依赖,根据不同情况处理,如使用备份路径、跳过错误(全局插件引入报错时)或抛出错误(非全局插件引入报错时)。最后组成包含插件信息的数组对象返回。
5. initPreset 初始化预设插件集合
- 主要做了以下几件事:
- 初始化插件
ctx
:调用initPluginCtx
函数。 - 执行插件,获得预设插件集合和插件:调用
apply
方法。 - 注册插件:调用
registerPlugin
函数。 - 如果预设插件集合是数组则继续递归调用
initPreset
。 - 如果插件是数组,是全局插件就合并到全局额外的插件
globalExtraPlugins
中,否则就合并到额外的插件extraPlugins
中。
- 初始化插件
6. initPluginCtx 初始化插件 ctx
- 主要操作如下:
- 生成插件的
pluginCtx
:创建Plugin
实例。 - 注册内部方法:将
onReady
、onStart
等内部方法注册到ctx
。 - 绑定
this
指向:将this.methods
数组中的方法绑定this
指向到Kernal
实例对象上,对kernelApis
中的方法进行代理绑定this
指向到Kernal
实例对象上。
- 生成插件的
6.1 new Plugin({ id, path, ctx })
- 定义了
Plugin
类,包含id
、path
、ctx
等属性,构造函数用于初始化这些属性。 - 还包含以下几个注册方法:
register
注册hook
:判断hook
的name
和fn
类型后存入hooks
。registerCommand
注册方法:将命令存入commands
Map 中,并调用register
方法存入hooks
。registerPlatform
注册平台:将平台存入platforms
中,并调用register
方法存入hooks
。registerMethod
注册方法:处理不同传参形式,将方法存入methods
中。addPluginOptsSchema
添加插件的参数Schema
:设置optsSchema
属性。
7. registerPlugin 注册插件
- 将插件注册到
plugins
Map 中,如果插件已被注册则抛出错误。
8. resolvePlugins 解析插件
- 操作步骤如下:
- 合并预设插件集合中的插件、
CLI
和项目中配置的插件得到resolvedCliAndProjectPlugins
。 - 合并全局预设插件集合中的插件、全局配置的插件得到
resolvedGlobalPlugins
。 - 遍历所有解析后的插件,一次调用
this.initPlugin
初始化插件,最后清空extraPlugins
和globalExtraPlugins
。
- 合并预设插件集合中的插件、
9. initPlugin 初始化插件
- 主要做了以下几件事:
- 初始化插件的
ctx
:调用initPluginCtx
函数。 - 注册插件:调用
registerPlugin
函数。 - 执行插件:调用
apply
方法,传入pluginCtx
和opts
。 - 校验插件的参数是否符合要求:调用
checkPluginOpts
函数。
- 初始化插件的
10. checkPluginOpts 校验插件的参数
- 使用
joi
校验插件参数schema
,如果pluginCtx.optsSchema
不是函数则直接返回,否则根据joi
的schema
验证opts
,不符合要求则抛出错误。
11. applyCliCommandPlugin 暴露 taro cli 内部命令插件
- 主要操作如下:
- 查找存在的
CLI
命令插件文件路径,将其转换为插件对象。 - 使用
helper.createSwcRegister
处理插件。 - 解析插件,然后逐个初始化插件。
- 查找存在的
12. 总结
- 回顾了本文学习的内容,包括如何合并预设插件集合和插件、插件的注册和调用等。
- 以
command/init.ts
插件为例,展示了从插件的注册到最终调用的整个流程,涉及到createSwcRegister
编译、插件初始化、register
方法存入hooks
,最后通过applyPlugins
方法串联hooks
中的函数依次执行。作者还建议读者自己调试源码以发现更多细节。
扩展阅读
Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有