02 — 编译管线与 Runner(Webpack / Vite)
设计层:若希望用 阶段 / 类 IR / Lowering 描述 JS 与样式整体管线(而非仅步骤列表),见 10-CSS与JS转化引擎设计.md。
1. 从 taro build 到 Runner
整体顺序可概括为:
- CLI 读配置、解析插件(
Kernel)。 - 平台插件(如
taro-platform-weapp)把template、fileType、runtimePath等注入构建配置。 getRunner()按compiler字段选择 webpack5-runner 或 vite-runner,传入appPath与合并后的配置。- 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 等平台钩子:
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:
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:
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'会unshifttransform-taro-components,对@tarojs/components做 AST 级变换(如列表场景优化),见packages/babel-preset-taro/transform-taro-components.js。 - 非 Web 平台 默认加入
babel-plugin-dynamic-import-node,与小程序打包模型有关(避免原生 dynamic import 行为与小程序运行环境不一致)。
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)。
参考资料
- 本仓库
packages/taro-webpack5-runner、packages/taro-vite-runner - Taro 编译配置详情