Taro 是一个开源的多平台开发框架,旨在帮助开发者使用单一的代码库为多个平台(如微信小程序、支付宝小程序、H5、React Native 等)创建应用程序。为了更好地理解 Taro 的源代码,我们将简要介绍其核心原理和主要组件。
在上一章中,我们详细讨论了 Taro 的内部机制,并通过调试 build:weapp
: taro build --type weapp
命令来了解 @tarojs/cli
的工作原理。在本章中,我们将进一步探究执行 taro build
命令时发生的过程。
开始调试
我们已经在上一章节的 launch.json
文件中设置了 taro build
命令。
接下来,在入口文件中打上断点。
继续,我们来到了packages/taro-cli/src/cli.ts
文件,可以看到,当前断点在 build 命令中
那么build命令在这里做了什么呢? 我们在上一篇文章中分析了CLI的源码,主要涉及设置环境配置和收集插件和按顺序执行插件。
执行插件
我们通过断点查看 kernal 内部的执行流程。
这个函数的功能包括:
- 解析命令名称和选项
- 初始化插件和预设
- 执行插件
// packages/taro-service/src/Kernel.ts
async run(args: string | { name: string, opts?: any }) {
let name: string;
let opts: any;
// 省略...
// 调试日志
this.debugger('command:run');
this.debugger(`command:run:name:${name}`);
this.debugger('command:runOpts');
this.debugger(`command:runOpts:${JSON.stringify(opts, null, 2)}`);
// 设置运行选项
this.setRunOpts(opts);
this.debugger('initPresetsAndPlugins');
// 初始化预设和插件
this.initPresetsAndPlugins();
// 应用插件:在准备阶段
await this.applyPlugins('onReady');
// 省略...
// 应用插件:执行命令
await this.applyPlugins({
name,
opts
});
}
weapp 插件
其实这个插件的功能,在官方文档中有详细的介绍:taro-docs.jd.com/docs/platfo…
由于我们运行的是 weapp,我们需要确定是哪个插件被加载,并了解该插件的具体功能。
接下来,我们将重点放在 weapp 插件的实现上。为了找到这个插件的位置,我们可以在 CLI 代码中查找相关部分,因为那里有一段代码负责将所需的插件注入到 kernel 中。
packages/taro-cli/src/cli.ts
我们在项目中进行全局搜索,找到与插件注入相关的文件。
找到后,我们在这个文件中设置断点,以便进入调试模式查看插件的具体加载和执行过程。
我们来看下源码,这段代码主要是做了什么
这段代码实现了对微信小程序平台的特定配置和行为的管理,包括模板和 Webpack 配置的修改。
// packages/taro-weapp/src/program.ts
import { TaroPlatformBase } from '@tarojs/service'
import { components } from './components'
import { Template } from './template'
import type { IOptions } from './index'
const PACKAGE_NAME = '@tarojs/plugin-platform-weapp'
export default class Weapp extends TaroPlatformBase {
template: Template
platform = 'weapp' // 平台标识,这里是微信小程序
globalObject = 'wx' // 全局对象,微信小程序中为 'wx'
projectConfigJson: string = this.config.projectConfigName || 'project.config.json' // 项目配置文件名
runtimePath = `${PACKAGE_NAME}/dist/runtime` // 运行时路径
taroComponentsPath = `${PACKAGE_NAME}/dist/components-react` // Taro 组件路径
fileType = {
templ: '.wxml', // 模板文件后缀
style: '.wxss', // 样式文件后缀
config: '.json', // 配置文件后缀
script: '.js', // 脚本文件后缀
xs: '.wxs' // 微信小程序脚本文件后缀
}
/**
* 构造函数
* @param ctx - 上下文对象
* @param config - 配置对象
* @param pluginOptions - 插件选项
*/
constructor (ctx, config, pluginOptions?: IOptions) {
super(ctx, config)
this.template = new Template(pluginOptions) // 初始化模板对象
this.setupTransaction.addWrapper({
close () {
this.modifyTemplate(pluginOptions) // 修改模板
this.modifyWebpackConfig() // 修改 Webpack 配置
}
})
}
/**
* 增加组件或修改组件属性
* @param pluginOptions - 插件选项
*/
modifyTemplate (pluginOptions?: IOptions) {
const template = this.template
template.mergeComponents(this.ctx, components) // 合并组件
template.voidElements.add('voip-room') // 添加 void 元素
template.focusComponents.add('editor') // 添加 focus 组件
if (pluginOptions?.enablekeyboardAccessory) {
template.voidElements.delete('input') // 删除 input 元素
template.voidElements.delete('textarea') // 删除 textarea 元素
}
}
/**
* 修改 Webpack 配置
*/
modifyWebpackConfig () {
this.ctx.modifyWebpackChain(({ chain }) => {
// 解决微信小程序 sourcemap 映射失败的问题,#9412
chain.output.devtoolModuleFilenameTemplate((info) => {
const resourcePath = info.resourcePath.replace(/[/\\]/g, '_')
return `webpack://${info.namespace}/${resourcePath}`
})
})
}
}
那么,问题在于如何将我们编写的 React 代码转换成小程序特有的代码。这个模板(template)的作用是什么?它具体是用来做什么的?
用于处理微信小程序的模板生成和相关配置。 主要是将 js 的代码转换成小程序的代码
开始编译(下一节)
代码转换完成后,我们将进入下一个任务,即编译。关于这一部分的详细分析,我们将在下一节进行。
packages/taro-service/src/platform-plugin-base/mini.ts
总结
回答最初的问题:执行 taro build
命令时发生了什么事情:
- cli 负责配置好环境变量和收集需要的插件,传递给 kernel
- kernel 负责按顺序执行插件
- weapp 插件负责转换代码,生成对应的模板和组件,配置好编译流程,然后传输到 mini-runner 中做编译。
相关链接