09 — 常见问题(含编译深挖、多端与样式)
本文在基础问答之上,并入原独立成篇的 小程序/RN 编译、iOS·Android、转换层复杂度、抖音/支付宝等平台、CSSOM·TaroElement 等要点,统一用 Q&A 呈现,便于一面到二面递进。
架构总览
Q:Taro 3 和 Taro 1/2 最大区别是什么?
A:Taro 3+ 以 运行时适配 为主:用 类 DOM + 各框架 Reconciler 承接框架语义;小程序再通过 hydrate + 模板 + setData 渲染。Taro 1/2 更偏 编译时把代码变成小程序形态(迁移见官方《版本升级权威指南》)。
Q:CLI 里插件是怎么加载的?
A:Kernel 合并 presets/plugins,resolvePresetsOrPlugins 解析路径,必要时 createSwcRegister 让 TS 插件可加载;平台插件再注册 build 钩子并拉起 Runner(packages/taro-service/src/Kernel.ts)。
编译与构建(Webpack / Vite / RN)
Q:Webpack 和 Vite 在 Taro 里如何切换?
A:config.compiler 为 webpack5 或 vite(对象则取 type),非法值回退 webpack5(packages/taro-service/src/platform-plugin-base/platform.ts)。Runner 为 @tarojs/webpack5-runner 或 @tarojs/vite-runner。
注意:RN 不走这条线,而是 taro-rn-runner → Metro;compiler: vite 不改变 Metro。
Q:页面入口为什么要有 taro-loader?
A:把页面组件包装成 createPageConfig + Page(...),合并 .config、behaviors;支付宝等用 behaviorsName(如 mixins)、modifyInstantiate 改实例化字符串(packages/taro-loader/src/page.ts)。
Q:小程序从 taro build 到 dist 主路径是什么?
A:index.mini.ts:MiniCombination.make() → Prebundle.run()(失败则跳过并告警)→ webpack();wxml/json 等 大量由 MiniPlugin.generateMiniFiles / generateTemplateFile 写入 compilation assets,不是全靠手写(packages/taro-webpack5-runner)。官方《实现细节》里旧称 mini-runner,4.x 默认是 webpack5-runner,机制一致。
Q:Prebundle 做什么?失败会怎样?
A:@tarojs/webpack5-prebundle 按平台调整依赖图、chunk 名、别名等,缓解小程序对 npm 形态的敏感;失败时 跳过预构建,构建仍继续,可能变慢(index.mini.ts 的 try/catch)。
小程序编译深挖
Q:MiniWebpackModule 大致管哪些资源?
A:脚本(含 compileMode 时的 miniCompilerLoader)、extract + postcss → wxss、静态 .wxml → asset/resource + miniTemplateLoader(sax 解析 src 依赖)、wxs/sjs 资源规则(MiniWebpackModule.ts)。
Q:独立分包在编译层怎么实现?
A:MiniPlugin.compileIndependentPages 为每个独立分包 createChildCompiler:输出到 ${output}/${name},chunkLoadingGlobal: subpackage_${name},子图内再挂 SplitChunks、RuntimeChunk、TaroLoadChunksPlugin;页面用 taro-loader/lib/independentPage,在分包内 mockApp + window.__taroAppConfig 再 Page(createPageConfig(…)),因不能假设主包已执行 App()(MiniPlugin.ts、independentPage.ts)。
Q:MiniBaseConfig 相对 H5 特别在哪?
A:Terser 倾向 ES5、ascii_only;@tarojs/runtime / shared alias 到项目顶层,避免 link 出多份 runtime;regenerator-runtime 等与小程序环境对齐(MiniBaseConfig.ts)。
Q:分包公共依赖怎么拆?
A:MiniSplitChunksPlugin 处理 subPackages,抽 sub-common / sub-vendors 等,与主包 common / vendors / taro 等并存(MiniSplitChunksPlugin.ts)。
Q:RecursiveTemplate vs UnRecursiveTemplate?
A:部分宿主 模板不能自递归,须多层 tmpl + baseLevel + comp 自定义组件接力;见 shared/src/template.ts 文首注释与 MiniPlugin 里对 baseLevel、template.isSupportRecursive 的处理。
Q:WXS/SJS 能关吗?
A:可 experimental.useXsForTemplate: false;compileMode 走 miniCompilerLoader + MiniCompileModePlugin 等链路。
小程序渲染与 setData
Q:React 在小程序里如何渲染?
A:react-reconciler 的 hostConfig 操作 Taro document(实为 TaroElement 树),再 hydrate 序列化 为小程序 data,模板绑定 i.st、i.cl 等(reconciler.ts、hydrate.ts)。
Q:如何减少 setData 压力?
A:enqueueUpdate 合并、performUpdate 批量;custom-wrapper 局部 setData;Shortcuts 短键;列表相关 transform-taro-components;scheduleTask 用 setTimeout 避免连续 setData(root.ts 注释)。
RN、iOS 与 Android(Taro 边界)
Q:Taro 如何打出 iOS / Android 包?
A:生产:taro-rn-runner 调 react-native bundle --platform ios|android,bundle-output / assets-dest / sourcemap 等按 deviceType 选 config.output.ios* 或 android*(packages/taro-rn-runner/src/index.ts)。
开发:react-native start,Metro 配置来自 @tarojs/rn-supporter 的 getMetroConfig(taroTransformer → 样式 / taro-rn-transformer → metro-babel-transformer)。
Q:taro-rn-transformer 做什么?
A:在 transform 期读 app.config 的 pages/subPackages,生成入口侧 页面 import 与路由注册;globalAny 缓存在 Metro 多 worker 下重复解析(packages/taro-rn-transformer/src/index.ts)。
Q:iOS 和 Android 在 Taro 侧资源路径为何不同?
A:previewProd:ios 用 扁平路径拼 assetsDest;android 映射 drawable-* / raw 与资源命名规则(packages/taro-rn-supporter/src/preview.ts)。与 RN 原生资源目录习惯一致。
Q:Taro 还管不管 Xcode / Gradle?
A:不管。Hermes、原生 Module、签名、Pod/Maven 等属 RN + 业务 ios/、android/ 工程。Taro 停在 Metro + Babel + 调 RN CLI。
参考:bundle-output 与 assets-dest。
小程序 vs RN:编译期哪里不一样?
| 维度 | 小程序 | RN |
|---|---|---|
| Bundler | Webpack5 / Vite | Metro |
| Babel | babel-preset-taro(weapp 下可有 transform-taro-components 等) | babel-preset-taro/rn + Metro preset + transform-imports-api → taro-rn 路径 |
| 样式 | PostCSS → wxss | rn-style-transformer + transform-react-jsx-to-rn-stylesheet |
| 视图产物 | wxml/json/wxss + js | JS bundle + assets |
Issue 线索:#2803 样式形态差异;#12961 Metro 与 RN 版本绑定;#17168 Webpack/Vite 与小程序体验(与 Metro 无关)。
转换层「到底有多复杂」?
Q:为什么说前面文档容易显得「浅」?
A:主线可以三句话讲完,但真实实现是 Babel 多插件 + Webpack 多插件(MiniPlugin 等单文件千行级)+ shared/template 大文件模板生成 + PostCSS 链 + Vite 平行实现 + RN 独立 transformer,再乘 框架 × 端 × 编译器 × 分包模式。教学文档会 裁剪,不等于实现简单。
Q:想系统读转换层从哪开始?
A:MiniCombination → MiniWebpackModule → MiniPlugin → shared/src/template.ts → babel-preset-taro → taro-vite-runner/mini → rn-supporter + taro-rn-transformer。
抖音、支付宝等多端小程序
Q:字节/支付宝和微信差在哪?Taro 怎么接?
A:不重做整套编译器,用 @tarojs/plugin-platform-*:program.ts 设 globalObject(tt/my)、fileType(.ttml/.axml 等)、template 实例、runtimePath;template.ts 换 Adapter(tt:if、a:if)、SJS 标签、replacePropName / modifyLoopContainer 等;runtime-utils 里 hostConfig 补生命周期与事件。
支付宝另有 modifyMiniConfigs 键名映射、去掉 navigator polyfill、.browserslistrc(#16560)。
详见:packages/taro-platform-tt、taro-platform-alipay;端平台插件概述。
样式:px、wxss 与「CSSOM」
Q:px 在各端怎么处理?
A:postcss-pxtransform:小程序常 rpx,H5 rem,RN 有对应模式;支持按文件 designWidth 函数(postcss-pxtransform/README.md)。外链见 05-CSS与样式转换.md。
Q:CSSOM 和 TaroElement 怎么对应?小程序里有完整 CSSOM 吗?
A:没有完整浏览器 CSSOM(无样式表规则树在 JS 里)。内联样式由 Style 类模拟 CSSStyleDeclaration(style_properties + initStyle),cssText 序列化为字符串,经 enqueueUpdate 路径 …st(Shortcuts.Style) 与 hydrate 写 st;className 走 cl,与 wxss 在宿主匹配。setAttribute('style') → this.style.cssText;getAttribute('style') 也读 style.cssText(element.ts、style.ts、hydrate.ts)。
性能与工程
Q:@tarojs/binding 是渲染引擎吗?
A:不是。是 CLI 脚手架 native 绑定(如 create 命令);渲染指 runtime + 模板 + reconciler。
最佳实践与错误用例对照(结合官方文档与社区 Issue)
下面把「直接用 Taro 写业务」时容易踩坑的点,用 推荐 vs 反例 对照;依据来自 官方《小程序性能优化指南》(含「写法最佳实践」)、跨平台开发,以及对 NervJS/taro Issues 的归纳(Issue 仅作现象佐证,以当前版本文档为准)。
1. setData 体量与状态结构
| 推荐 | 反例 / 风险 | 说明与依据 |
|---|---|---|
| 大列表用 VirtualList、分页;热点区域用 CustomWrapper 做 局部 setData | 把 成千上万条明细和 高频 UI 状态塞在同一个 state,一次更新连带巨大 payload | 官方指出性能主要受 setData 数据量 与 调用频率 影响;社区有「数据量过大导致跳转后卡顿」等讨论(如 #6002、早期 #279 类问题) |
关注 路径深度:复杂页用 CustomWrapper 或调 mini.baseLevel(理解 flex 跨原生层、选择器 >>> 等副作用) | 深层 DOM + 整页 setData,低端机更新路径极长 | 官方 optimized - CustomWrapper / baseLevel |
| 超大数据初次渲染考虑 prerender(按需) | 首屏一次性灌入巨型 data 导致白屏 | 同页「优化初次渲染」 |
平台差异提醒:个别宿主历史上出现过 更新策略与微信不一致 的现象(例如百度小程序相关 #7293),跨端项目应 真机多端验证,不要假设「微信上没问题 = 所有小程序没问题」。
2. React 写法与基础组件(小程序端)
| 推荐 | 反例 / 风险 | 依据 |
|---|---|---|
传给 Map 等 的 markers 等 对象/数组 用 useState / useMemo 等保持引用稳定 | 每次 render 内联 [] / {},浅比较认为变了 → 多余 setData | optimized - 基础组件属性保持引用 |
| 只使用 文档中的标准属性 | 给 View/Text 等挂 非标准自定义属性(仍会进 setData,宿主忽略 → 冗余数据) | 同页「不要挂载额外属性」 |
删除大段子树时,用 额外 View 包住再条件渲染,隔离兄弟 | 在与 复杂兄弟楼层 同级直接 {cond && <Heavy />},删除时可能牵动大量兄弟节点更新 | 同页「删除楼层节点要谨慎处理」(官方也提到后续算法优化方向,仍建议按文档写法规避) |
3. 跨端:DOM / BOM 与 window
| 推荐 | 反例 / 风险 | 依据 |
|---|---|---|
用 Taro.* API、p、多端文件(.weapp.ts / .h5.ts 等)抹平差异 | 假设 浏览器完整 document/window;在小程序里滥用 querySelector、复杂布局计算 | Taro DOM 说明(能力为 模拟子集);Issue 如 #8021(runtime 下 window 与预期不符)、#8917(模块包装与 window 注入引发第三方判断问题) |
| 引入第三方库前查 是否强依赖浏览器全局 | 库内 typeof window、全局 polyfill 与小程序环境 打架 | 与上同类 Issue 讨论 |
4. 依赖与版本工程
| 推荐 | 反例 / 风险 | 依据 |
|---|---|---|
所有 @tarojs/* 与 CLI 保持同一主版本线,按官方升级指南整体升 | 混装 3.x CLI + 4.x 运行时、或手动升降个别包 | #15632 等「初始化后再装旧版导致冲突」类讨论 |
| 升级前核对 UI 库与 Taro 主版本兼容性(NutUI、taroify、taro-ui 等以各库说明为准) | Taro 4.x 与 部分旧 UI 库未对齐仍强上 | #17078 等兼容性讨论 |
5. 进阶与取舍(官方已写明 trade-off)
- CompileMode / WXS:在 React 小程序里可按文档使用 视图层脚本 做性能或能力扩展;要接受 维护成本与端支持范围(见 optimized、半编译模式 complier-mode)。
useXsForTemplate: false:可减模板拼接耗时,但可能 增大包体(官方在 optimized 中说明)。
6. RN / H5(各记一条,别和小程序混谈)
| 端 | 推荐 | 反例 |
|---|---|---|
| RN | 构建走 Metro(taro-rn-runner / rn-supporter),样式与组件按 RN 链路理解;原生能力在 ios/、android/ 工程里接 | 把 Webpack/Vite 小程序优化当成 RN 也会自动生效;RN 与 Metro 版本 强绑定,乱升易踩坑(如 #12961) |
| H5 | 在浏览器端可合理使用 真 DOM;与小程序共用的逻辑用 p 或多端文件隔离 | 把 仅 H5 可用的 API 无隔离写进共享模块,小程序编译期或运行时报错 |
7. 一句话
最佳实践的核心是:认清 Taro 3 是「重运行时 + setData 协议」,在写法上 控数据量、控引用、控更新范围;跨端 不要用满血浏览器假设;工程上 锁死依赖代数。反例往往是与这三条相反的组合,并在 Issue 里以「卡顿、闪烁、兼容、装包失败」等形式暴露。
对比题
Q:Taro 和 uni-app 的核心差异?
A:Taro 以 真实框架运行时 + 宿主适配 为主线;uni-app 传统上更偏 Vue 编译 + 运行时裁剪、条件编译更多。见 07-与uni-app等横向对比.md。
开放题
Q:如果要支持一个新小程序平台,你会改哪里?
A:新增 plugin-platform-xxx:program(fileType、globalObject)、template(Adapter、递归能力、特殊循环体)、runtime-utils(hostConfig)、components/apis;对齐 Webpack 产物后缀;重大变更走 RFC。
Q:重大功能与提 Issue 流程?
A:README 约定 RFC + Issue Helper。
与其它文档的分工
| 文档 | 内容 |
|---|---|
| 01~07 | 分主题精读 |
| 08 | 设计决策条目 + 依据(精简) |
| 10 | CSS/JS 转化引擎的设计语言(阶段、类 IR、多后端) |
| 本文 09 | 问答 + 深挖合集 |
| 实现细节 | 官方总览(注意包名迭代) |
逐步代码走读仍以 packages/taro-webpack5-runner、taro-loader、taro-runtime、shared、taro-rn-* 为准。