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)把 templatefileTyperuntimePath 等注入构建配置。
  3. getRunner()compiler 字段选择 webpack5-runnervite-runner,传入 appPath 与合并后的配置。
  4. Runner 内部再挂 Webpack 插件(小程序场景核心是 MiniPlugin)或 Vite 插件taro-vite-runnerminiPreset 等)。

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

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

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

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/runtimecreatePageConfig 生成小程序 Page 配置对象,避免每个业务页手写生命周期与渲染挂载。
  • loaderMeta.modifyInstantiate支付宝/京东等 在不变更核心 loader 的前提下改写实例化字符串(平台差异外置)。

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

TaroMiniPluginpackages/taro-webpack5-runner/src/plugins/MiniPlugin.ts)是小程序 Webpack 构建的「编排中心」之一,构造函数里可见它接收 template(Recursive / UnRecursive)fileTypeframeworkblended / newBlendedbaseLevelexperimental.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。

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

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

参考资料

MIT License.