Skip to content
目录

02 — 编译管线与 Runner(Webpack / Vite) ​

设计层:若希望用 阶段 / 类 IR / Lowering 描述 JS 与样式整体管线(而非仅步骤列表),见 10-CSS与JS转化引擎设计.md。

1. 从 taro build 到 Runner ​

整体顺序可概括为:

  1. CLI 读配置、解析插件(Kernel)。
  2. 平台插件(如 taro-platform-weapp)把 template、fileType、runtimePath 等注入构建配置。
  3. getRunner() 按 compiler 字段选择 webpack5-runner 或 vite-runner,传入 appPath 与合并后的配置。
  4. Runner 内部再挂 Webpack 插件(小程序场景核心是 MiniPlugin)或 Vite 插件(taro-vite-runner 的 miniPreset 等)。

mini.ts 中可见 Runner 包名分支(与 web 对称,同文件族)。

2. Webpack 侧:小程序入口 taro-loader ​

页面入口并不是直接 Page(业务组件),而是经 @tarojs/taro-loader/lib/page 包一层,注入 createPageConfig,并在小程序平台拼接 Page(taroOption),支持 behaviors、modifyInstantiate 等平台钩子:

52:77:packages/taro-loader/src/page.ts
let instantiatePage = `var inst = Page(createPageConfig(component, '${pageName}', {root:{cn:[]}}, config || {}))`

  // 上面保留的instantiatePage是为了避免影响存在modifyInstantiate的平台
  if (process.env.TARO_PLATFORM === PLATFORM_TYPE.MINI) {
    instantiatePage = `
var taroOption = createPageConfig(component, '${pageName}', {root:{cn:[]}}, config || {})
if (component && component.behaviors) {
  taroOption.${behaviorsName} = (taroOption.${behaviorsName} || []).concat(component.behaviors)
}
var inst = Page(taroOption)
`
  }
// ...
  return `import { createPageConfig } from '@tarojs/runtime'
import component from ${stringify(componentPath)}
var config = ${configString};
// ...
${instantiatePage}

设计原因(可简述为):

  • 统一由 @tarojs/runtime 的 createPageConfig 生成小程序 Page 配置对象,避免每个业务页手写生命周期与渲染挂载。
  • loaderMeta.modifyInstantiate 让 支付宝/京东等 在不变更核心 loader 的前提下改写实例化字符串(平台差异外置)。

3. MiniPlugin:页面图、分包、模板与资源 ​

TaroMiniPlugin(packages/taro-webpack5-runner/src/plugins/MiniPlugin.ts)是小程序 Webpack 构建的「编排中心」之一,构造函数里可见它接收 template(Recursive / UnRecursive)、fileType、framework、blended / newBlended、baseLevel、experimental.compileMode 等,直接影响 生成模板层级与是否走 compile mode:

114:149:packages/taro-webpack5-runner/src/plugins/MiniPlugin.ts
constructor (options: ITaroMiniPluginOptions) {
    const { combination } = options
    const miniBuildConfig = combination.config
    const { template, baseLevel = 16, experimental } = miniBuildConfig

    this.options = {
      sourceDir: combination.sourceDir,
      framework: miniBuildConfig.framework || 'react',
      // ...
      template,
      // ...
      fileType: miniBuildConfig.fileType,
      // ...
    }

    if (template.isSupportRecursive === false && baseLevel > 0) {
      (template as UnRecursiveTemplate).baseLevel = baseLevel
    }

    if (experimental?.useXsForTemplate === false) {
      (template as UnRecursiveTemplate).isUseXS = false
    }

    if (experimental?.compileMode === true) {
      template.isUseCompileMode = true
    }

依据:模板类在 shared/src/template.ts 文件头注释中明确区分了 模板是否支持递归、是否支持 xs/sjs 两类正交维度,插件层只是把 config 映射到模板实例行为上。

4. Vite 侧:taro-vite-runner ​

小程序 Vite 构建入口极薄:构造 TaroCompilerContext,注册 miniPreset,再 vite build:

12:48:packages/taro-vite-runner/src/index.mini.ts
export default async function (appPath: string, rawTaroConfig: ViteMiniBuildConfig) {
  const viteCompilerContext = new TaroCompilerContext(appPath, rawTaroConfig)
  const { taroConfig } = viteCompilerContext
  const plugins: UserConfig['plugins'] = [
    miniPreset(viteCompilerContext)
  ]
  // ...
  await build(commonConfig)
}

要点:Vite 路径下 TS/JSX 预处理大量交给 esbuild/插件,babel-preset-taro 里对 Vite 有明确分支(React/Vue/Solid 在 Vite 时跳过部分 preset),见下一节文档与 babel-preset-taro/index.js。

5. Babel:babel-preset-taro 与编译器分工 ​

babel-preset-taro/index.js 核心逻辑:

  • compiler === 'vite' 时:React / Solid / Vue3 的部分 preset 交给 Vite 生态处理,避免重复转译。
  • 小程序 + React 时:TARO_ENV === 'weapp' 会 unshift transform-taro-components,对 @tarojs/components 做 AST 级变换(如列表场景优化),见 packages/babel-preset-taro/transform-taro-components.js。
  • 非 Web 平台 默认加入 babel-plugin-dynamic-import-node,与小程序打包模型有关(避免原生 dynamic import 行为与小程序运行环境不一致)。
31:42:packages/babel-preset-taro/index.js
const isVite = options.compiler === 'vite'
  // vite 不需要 react 的 preset,在内部已经处理了
  const isReact = options.framework === 'react' || options.framework === 'preact' && !isVite
  // vite 不需要 solid 的 preset,在内部已经处理了
  const isSolid = options.framework === 'solid' && !isVite
  // vite 不需要 vue 的 preset,在内部已经处理了
  const isVue3 = options.framework === 'vue3' && !isVite
  // TODO:后续改为在 vite harmony 中实现对 ts 的支持
  const isHarmony = process.env.TARO_PLATFORM === 'harmony'
  // vite 不需要使用 babel 处理 ts,在 esbuild 中处理了
  const isTs = options.ts && (!isVite || isHarmony)

6. 流程串讲(建议背诵版) ​

JS/TSX:源码 →(Vite: esbuild + 插件;Webpack: babel-loader + preset)→ 标准 ES 模块与运行时调用 → bundler 打包成小程序 app.js / 分包 chunk。

模板:构建插件 + shared 的 RecursiveTemplate / UnRecursiveTemplate 生成各端 xml 模板片段,与业务 JS 通过 数据路径(setData) 绑定。

配置与静态资源:MiniPlugin 等扫描 app.config、pages、usingComponents,复制 tabBar 图标、处理独立分包等(细节见 MiniPlugin 后半与 tests)。

参考资料 ​

MIT License.