Taro 源码共读 - taro build

260 阅读4分钟

Taro 是一个开源的多平台开发框架,旨在帮助开发者使用单一的代码库为多个平台(如微信小程序、支付宝小程序、H5、React Native 等)创建应用程序。为了更好地理解 Taro 的源代码,我们将简要介绍其核心原理和主要组件。

在上一章中,我们详细讨论了 Taro 的内部机制,并通过调试 build:weapp: taro build --type weapp 命令来了解 @tarojs/cli 的工作原理。在本章中,我们将进一步探究执行 taro build 命令时发生的过程。

开始调试

我们已经在上一章节的 launch.json 文件中设置了 taro build 命令。
接下来,在入口文件中打上断点。
screely-1721325522552.png
继续,我们来到了packages/taro-cli/src/cli.ts文件,可以看到,当前断点在 build 命令中
screely-1721326091909.png

那么build命令在这里做了什么呢? 我们在上一篇文章中分析了CLI的源码,主要涉及设置环境配置和收集插件和按顺序执行插件。

执行插件

screely-1721402975128.png
我们通过断点查看 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…
image.png
由于我们运行的是 weapp,我们需要确定是哪个插件被加载,并了解该插件的具体功能。
接下来,我们将重点放在 weapp 插件的实现上。为了找到这个插件的位置,我们可以在 CLI 代码中查找相关部分,因为那里有一段代码负责将所需的插件注入到 kernel 中。

packages/taro-cli/src/cli.ts

image.png
我们在项目中进行全局搜索,找到与插件注入相关的文件。
找到后,我们在这个文件中设置断点,以便进入调试模式查看插件的具体加载和执行过程。
image.png
image.png
我们来看下源码,这段代码主要是做了什么

这段代码实现了对微信小程序平台的特定配置和行为的管理,包括模板和 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 的代码转换成小程序的代码

image.png
image.png

开始编译(下一节)

代码转换完成后,我们将进入下一个任务,即编译。关于这一部分的详细分析,我们将在下一节进行。

packages/taro-service/src/platform-plugin-base/mini.ts

image.png

总结

回答最初的问题:执行 taro build 命令时发生了什么事情:image.png

  • cli 负责配置好环境变量和收集需要的插件,传递给 kernel
  • kernel 负责按顺序执行插件
  • weapp 插件负责转换代码,生成对应的模板和组件,配置好编译流程,然后传输到 mini-runner 中做编译。

相关链接