一套 Taro 3 代码跑三端:条件编译与 API 差异的踩坑复盘
适用读者:正在用 Taro 3 + React 维护一套代码、准备往第二个或第三个小程序平台移植的前端工程师。 文中出现的报错、配置、数据量都来自一个正在运行的连锁门店会员小程序,三端分别是微信、支付宝、抖音。 如果你只发微信,这篇的大部分内容用不上;如果你已经开始移植第二个端,建议从「条件编译不是万能胶」那节顺着看。
同一份 package.json,同一个 dist 目录出来的三份构建产物。微信上跑得好好的页面,装到支付宝开发者工具里一片空白,控制台干干净净,编译阶段连一条警告都没有。查到第二天下午才发现问题出在一行写错层级的条件编译——它在编译期把半个文件的代码摇掉了,摇得很彻底,彻底到运行时没有任何东西可以报错。
这篇不做多端框架的选型对比,只复盘我们在真实业务里踩过的六个坑,以及最后沉淀下来的那层 API 适配写法。
项目背景:一个会员小程序要开三扇门
业务不复杂,但每个功能都正好扎在平台差异上。连锁门店的会员小程序,核心链路是「会员码 + 积分 + 优惠券核销 + 储值支付 + 到店核销」,涉及三方的东西一个都躲不掉:

- 会员身份:注册登录、手机号授权、会员卡展示
- 交易:储值充值走支付、券核销走扫码
- 门店:地图选点、列表分页
我们团队 4 个前端,排期 6 周,第 3 周结束开始往支付宝移植,第 5 周接抖音。下面这张表是我们立项时登记的三端基线,后面所有坑都与它相关。
| 维度 | 微信 | 支付宝 | 抖音 |
|---|---|---|---|
| TARO_ENV 取值 | weapp | alipay | tt |
| 逻辑层运行时 | V8 / JSCore | V8 | V8 |
| 视图层渲染 | WebView | WebView | Lynx / WebView |
| 支付 API | wx.requestPayment | my.tradePay | tt.pay |
| 登录 API | wx.login | my.getAuthCode | tt.login |
| 手机号获取 | button open-type | button open-type | button + 服务端换解密 |
表里最后一行是坑最深的。它看着像同一个东西——都是「点一个按钮弹授权框」,实际三端的参数名、回调时机、返回字段完全不通。
条件编译不是万能胶:粒度问题先看清楚
Taro 3 的 process.env.TARO_ENV 取值是 weapp、alipay、tt、swan、h5、rn。官方文档给的用法有三种形态:
| 写法 | 示例位置 | 编译期可消除 | 适合管什么 |
|---|---|---|---|
| 文件名后缀 | pay.weapp.ts、pay.tt.ts | 是,整文件替换 | 一整个模块的差异实现 |
| 文件内 if 分支 | 函数体里的 if / else | 是,条件是字面量比较时 | 少量几行的逻辑岔路 |
| JSX 三元表达式 | return 里的条件渲染 | 部分,看是否被拆成组件 | 属性值的细微差别 |
| 变量中转后判断 | const env = ...; if (env === 'alipay') | 否,整段留在产物里 | 不该这么写 |
第四行是我们第 3 周踩的坑。我当时为了「代码好看」,把平台名取出来放到一个常量里,想着后面好统一替换。结果这一行让上游的判断全部失去了编译期可分析性,三个平台的分支代码都进了同一份产物,包体直接涨了一截,更糟的是支付宝端把微信专属的 wx.xxx 调用也一起打包进去,在某些时机执行到了就白屏。
条件编译的底层机制:编译期常量替换与死代码消除
要理解为什么会这样,得看 Taro 3 是怎么把 process.env.TARO_ENV 变没的。整个链路分三步:
flowchart LR
A[源码 process.env.TARO_ENV] --> B[编译时注入常量 DefinePlugin]
B --> C[替换为字符串字面量 'alipay']
C --> D[比较表达式求值 true/false]
D --> E[minify 阶段死代码消除 DCE]
E --> F[产物仅保留命中的分支]
F --> G[未命中分支彻底消失]
关键在于第二步到第三步。process.env.TARO_ENV 被替换成的是一个字符串字面量,不是运行时读取的变量。替换完成后源码里的 if (process.env.TARO_ENV === 'weapp') 变成了 if ('alipay' === 'weapp'),这是个编译期就能算出结果的常量表达式,打包工具的静态分析能直接把它折叠成 false,再进入死代码消除环节把整个分支体删掉。
这套机制成立有三个前提,缺一条就失效:
- 判断必须沿着
process.env.TARO_ENV这条完整链路写,中间不能经过变量、参数、解构赋值或函数返回值中转 - 比较的另一侧必须是字面量字符串,不能是从配置文件读进来的变量
- 表达式不能放在会在运行时被重新求值的位置,比如 Hook 的依赖数组、传给高阶函数的回调闭包
第三点最隐蔽。我们有一个地方把平台判断塞进了 useMemo 的依赖里,编译后的代码保留了对两边都要求值的结构,导致支付宝产物里出现了 wx.getSystemInfoSync 的调用符号。真机上一旦那条路径被走到,控制台看到的报错是 wx is not defined,而不是「你的条件编译写错了」,定位成本非常高。
所以正确的写法是直接比。下面这段是我们后来统一的要求:
// 环境:Taro 3.6.x + React 18,@tarojs/cli 构建
// 反例:把环境名取出来中转,编译期无法折叠,三端代码全进产物
// const env = process.env.TARO_ENV
// export function isWeapp() { return env === 'weapp' }
// 正例:直接写完整链路,命中 weapp 时其余分支在产物中被删除
export function isWeapp() {
// 编译期就能确定结果,minify 阶段会把整个函数体折叠为 true
return process.env.TARO_ENV === 'weapp'
}
export function MemberPayBar({ order }) {
// 差异只到「组件」这一层,用文件名后缀做整模块替换最划算
// 同目录下准备 PayAction.weapp.tsx / PayAction.alipay.tsx / PayAction.tt.tsx
return (
<View className="pay-bar">
<PayAction order={order} />
</View>
)
}
一句大白话总结这条经验:涉及超过 15 行的差异就分文件,少于 15 行就在原地写 if。混着来最容易出事。
支付:requestPayment 在三端长着三张脸
Taro.requestPayment 在三端都存在,签名看着也很像,实际上入参几乎无一相同。这是三端差异里杀伤力最大的一处,因为它是交易链路的最后一环,出问题直接掉钱。
| 参数 | 微信 | 支付宝 | 抖音 |
|---|---|---|---|
| 订单标识字段 | package(形如 prepay_id=xxx) | tradeNO 或 orderStr | orderInfo 对象里的 order_id |
| 时间戳字段 | timeStamp(字符串秒) | 无,由服务端下发串承载 | 无 |
| 随机串 | nonceStr | 无 | 无 |
| 签名类型 | signType(MD5 / RSA) | 无 | 无 |
| 签名值 | paySign | 由服务端整体签名 | 由服务端整体签名 |
| 支付凭证参数类型 | 对象,字段平铺 | 字符串或对象,视端而定 | 对象,且必须带 service 字段 |
微信侧所有字段都要从服务端逐个返回给前端拼;支付宝和抖音干脆反过来,服务端直接给一个签好的整串或对象,前端原样透传。这个方向上的差异决定了:前端不应该试图用同一份数据结构去喂三个平台,硬凑的结果就是哪个平台都别扭。
我们的做法是加一层适配器,对外只暴露一个 pay(order),内部按 TARO_ENV 走各自的拼装逻辑:
// 环境:Taro 3.6.x + React 18 + TypeScript 4.9
// 目的:给业务层一个统一的 pay 入口,屏蔽三端 requestPayment 的入参差异
// 维护约定:新增第四个平台时,只在本文件补一个构造器
// 不要回头改业务代码,否则这套隔离就白做了
// 各端拼出来的参数结构完全不同,统一用一个宽类型接住
// 这里刻意不用联合类型收窄,避免把编译期分发写成运行时判断
type PayParams = Record<string, unknown>
// 微信:字段全部平铺,且 timeStamp 必须是字符串
function buildWeappParams(raw: WeappOrder): PayParams {
// package 是保留字风格字段,服务端下发的是 prepay_id=xxx 的完整串
return {
timeStamp: String(raw.timeStamp),
nonceStr: raw.nonceStr,
package: raw.package,
signType: raw.signType || 'RSA',
paySign: raw.paySign,
}
}
// 支付宝:服务端给的是整体签名串,前端原样透传,不要再拆
function buildAlipayParams(raw: AlipayOrder): PayParams {
// tradeNO 存在时优先用 tradeNO,否则退回 orderStr
return raw.tradeNO ? { tradeNO: raw.tradeNO } : { orderStr: raw.orderStr }
}
// 抖音:orderInfo 必须是对象,且 service 表明业务线,缺了直接失败
function buildTtParams(raw: TtOrder): PayParams {
return {
orderInfo: { order_id: raw.orderId, order_token: raw.orderToken },
service: 5,
}
}
// 统一入口:按编译期常量分发,未命中的分支不会进入产物
export async function pay(raw: AnyPlatformOrder): Promise<void> {
// 三份参数构造器只有一个会被打进对应端的产物
let params: PayParams = {}
// 以下三段刻意写成独立 if 而不是 if / else
// 常量折叠后压缩器更容易把整段消除干净
if (process.env.TARO_ENV === 'weapp') {
params = buildWeappParams(raw as WeappOrder)
}
if (process.env.TARO_ENV === 'alipay') {
params = buildAlipayParams(raw as AlipayOrder)
}
if (process.env.TARO_ENV === 'tt') {
params = buildTtParams(raw as TtOrder)
}
// 走到 Taro.requestPayment 时,参数已经是目标端认识的形状
await Taro.requestPayment(params as never)
return
}
这里的 if 全部用的是独立语句而不是 if / else。写成 if / else 也能过,但独立语句在某一端被折叠成常量后,剩下的语句会被整体优化掉,产物更干净。这是我们从构建产物的差异比对里看出来的,几次包体优化都靠这个习惯省下的几百 KB。
整个支付链路的调用时序如下,注意服务端那一步在三端也是分叉的:
sequenceDiagram
participant U as 用户
participant C as 小程序端 Taro 3
participant A as 适配层 pay
participant S as 业务服务端
participant P as 平台支付网关
U->>C: 点击立即支付
C->>S: 下单请求, 带上 platform 标识
S->>P: 调用对应平台统一下单接口
P-->>S: 返回已签名的支付凭证
S-->>C: 下发凭证, 结构按端定制
C->>A: pay 凭证
A->>A: 按 TARO_ENV 选择构造器
A->>P: Taro.requestPayment 目标端参数
P-->>U: 拉起收银台
U->>P: 确认支付
P-->>C: 回调 success 或 fail
这里有个实操细节值得单独拎出来:服务端一定要拿 platform 字段来做下单分叉,不要指望从请求头里猜。我们在测试环境用同一个 token 跨端跑过一次,服务端猜成了微信,返回的 package 串丢到支付宝端,报的错是笼统的「支付失败」,日志里翻了半天才对上。
登录授权:三条互不相干的链路
支付至少还都叫「支付」,登录这块三端连概念都不统一。
| 环节 | 微信 | 支付宝 | 抖音 |
|---|---|---|---|
| 取凭证 | wx.login 拿 code | my.getAuthCode 拿 authCode | tt.login 拿 code / anonymousCode |
| 换 openid | 服务端 code2session | 服务端换取 user_id | 服务端 code2session |
| 手机号 | 按钮 + 服务端解密 | 按钮授权后取加密串 | 按钮 + 服务端解密 |
| 静默失败场景 | code 过期需重新 login | 用户未授权 scope | 未登录且未绑定 |
支付宝在 my.getAuthCode 这里多一层 scopes 概念,auth_base 只能换 user_id,auth_user 才能拿头像昵称。我们最开始直接在主流程里要 auth_user,结果首次进入时授权弹窗拦住了会员码展示,运营那边反馈「一进来就跳窗」。后来改成进页面只拿 auth_base,头像昵称放到用户主动点「完善资料」时再要。
抖音侧的麻烦在于它有 code 和 anonymousCode 两个东西并存,登录态判定跟微信不完全一致。我们的处理是把「有没有拿到稳定用户标识」这件事收敛成一个布尔函数,业务层不直接碰平台 API:
// 环境:Taro 3.6.x + React 18 + TypeScript 4.9
// 目的:把三端登录收敛成 getAuthPayload 一个出口
// 约定:底层用户登录态由页面设计时决定要不要再走一次资料授权
// 要不要再走一次资料授权,交给调用方按页面设计决定
// 底层不做隐式跳转弹窗,免得运营再吐槽
interface AuthPayload {
// 平台标识,服务端据此选择对应的凭证换取接口
platform: string
// 平台下发的临时凭证
code: string
// 是否需要额外走一次用户信息授权
needUserProfile: boolean
}
// 微信:一次 login 拿到 code,用户资料走按钮单独授权
async function loginWeapp(): Promise<AuthPayload> {
const res = await Taro.login()
return { platform: 'weapp', code: res.code, needUserProfile: true }
}
// 支付宝:只静默拿 auth_base,避免首屏弹窗打断会员码展示
async function loginAlipay(): Promise<AuthPayload> {
const res = await Taro.getAuthCode({ scopes: 'auth_base' })
return { platform: 'alipay', code: res.authCode, needUserProfile: false }
}
// 抖音:优先用 code,拿不到再退到 anonymousCode 走游客态
async function loginTt(): Promise<AuthPayload> {
const res = await Taro.login()
return { platform: 'tt', code: res.code || res.anonymousCode, needUserProfile: true }
}
// 统一出口:业务层只认这个函数的返回值
export function getAuthPayload(): Promise<AuthPayload> {
// 三个分支在各自平台被折叠,其余两个在产物里不存在
if (process.env.TARO_ENV === 'weapp') return loginWeapp()
if (process.env.TARO_ENV === 'alipay') return loginAlipay()
return loginTt()
}
needUserProfile 这个字段是后来加的。加上它之前,业务代码里到处散着「这个平台要不要弹窗」的判断,改一次要翻五个文件。收敛之后,头像昵称的授权时机在适配层一处决定。
分包配置:字段名差不多,行为差很多
三端的分包配置都写在 src/app.config.ts 的 subPackages(或 subpackages)里,字段名一样,实际行为差别挺大。我们踩到的是这两处:
pages里的路径必须带root前缀,否则编译通过但真机 404- 微信支持
independent独立分包,支付宝端这个字段会被忽略,真机上仍然是普通分包
后者隐蔽的地方在于:我们给「券核销」这个分包开了 independent,期望它能跳过主包加载直接唤起。微信端一切正常,支付宝端因为退化成普通分包,主包要先下载完才进得去,冷启动多了几百毫秒。测试环境下包体积小看不出来,上线后门店网络差的时候才暴露。
另一个坑是分包 JS 的引用原则。独立分包里不能引用主包的 JS,但普通分包可以。微信端走通的方案,到了支付宝端因为 independent 失效反而变成「引用了主包也没报错」,这种反过来「侥幸通过」的情况比直接报错更难发现。
配置层面的差异我们最后统一到 config/index.js 里按端生成:
// 环境:Taro 3.6.x,@tarojs/cli 读取本文件
// 目的:按目标平台产出不同的分包与编译配置
// 注意:本文件在 Node 环境执行,不能写浏览器或小程序专有 API
// 从命令行里取到当前编译的目标平台
const target = process.env.TARO_ENV
// 各平台的分包清单保持一致,只在支持的平台开启 independent
const baseSubPackages = [
{
root: 'package-member',
pages: ['pages/card/index', 'pages/points/index'],
},
{
root: 'package-verify',
pages: ['pages/scan/index', 'pages/result/index'],
},
]
// 微信端把核销包变成独立分包,冷启动可以绕过主包
// 支付宝端该字段不生效,保持普通分包即可
const weappSubPackages = baseSubPackages.map((pkg) =>
pkg.root === 'package-verify' ? { ...pkg, independent: true } : pkg
)
// 本文件跑在 Node 里,只能写 CommonJS,不能碰任何端侧 API
const config = {
projectName: 'member-mall',
// 三端统一关闭 size 校验,由 CI 单独做体积门禁
mini: {
postcss: {
pxtransform: { enable: true, config: { selectorBlackList: ['no-px'] } },
},
},
}
module.exports = function (merge) {
// 只有微信端替换成带 independent 的清单
if (target === 'weapp') {
merge({ ...config, subPackages: weappSubPackages })
return
}
// 支付宝与抖音沿用基础清单,避免无效字段被静默忽略
merge({ ...config, subPackages: baseSubPackages })
}
CI 侧我们跑的是一个三端矩阵,每条流水线只传一个 --type,产物分目录落盘。这事儿看着啰嗦,但它保证了「某端独有的字段」在本地跑是会报错的,不会出现「我加了字段但忘了它在另一端不生效」的情况。
样式兼容:rpx 反倒省心,1px 边框才是麻烦
rpx 这块不用担心,三端都支持 750rpx 等于屏幕宽度的换算,px 转 rpx 也都有对应的 PostCSS 插件。真正出问题的是三件小事:
1px 边框。 高分屏上写 border: 1px solid #eee 会显得很粗。通用解法是伪元素 + transform: scaleY(0.5),但抖音端的部分渲染路径下,scaleY 配合 position: absolute 出现过边框消失的情况。我们的处理是给边框加兜底:
// 环境:Taro 3.6.x,PostCSS + Sass
// 目的:三端统一的细线边框,避免 1px 在高分屏变粗或丢失
// 伪元素实现一条线,用 scaleY 压到物理 0.5px
// transform-origin 必须写,否则缩完会偏移半个位置
@mixin hairline($color: #e5e5e5, $side: bottom) {
position: relative;
&::after {
content: '';
position: absolute;
// 兜底:抖音端个别渲染路径下 scaleY 会失效,保留 1px 至少线还在
height: 1px;
transform: scaleY(0.5);
transform-origin: 0 0;
background-color: $color;
@if $side == bottom {
left: 0;
right: 0;
bottom: 0;
} @else {
left: 0;
top: 0;
bottom: 0;
width: 1px;
transform: scaleX(0.5);
}
}
}
.card-item {
// 直接吃 mixin,不需要在每个页面重复写伪元素
@include hairline(#e5e5e5, bottom);
}
选择器支持不一致。 支付宝端对部分 CSS 选择器的支持比另外两端保守,尤其是通配符和某些伪类。我们有一条 CSS 规则用了 :last-child 去掉列表最后一项的边框,微信和抖音正常,支付宝端整条规则被丢掉,每一项的边框都露出来了。排查方式是打开支付宝开发者工具,看编译后的 wxss 对应文件里那条规则在不在——不在,就是选择器被裁了。
安全区适配。 env(safe-area-inset-bottom) 三端支持程度不同,底部操作栏不要用固定值撑高度。我们最终用 padding-bottom: env(safe-area-inset-bottom) 加一个 max(16rpx, ...) 的兜底值,注意部分端对 max() 的支持要实测,不支持的场景写两套规则用条件编译切文件。
组件库差异:button open-type 只有微信买账
这是拦住我们第 5 周进度的最后一个坑。会员注册要做「一键授权手机号」,业务侧期望的交互是三端一致:点按钮,弹系统授权框,拿到手机号。
实际是三套完全不同的 API 形态。需要说明的是,下面列出的取值是本项目上线时登记的版本,各平台 SDK 会迭代,用到时对着官方文档再核一遍。
| 平台 | 触发方式 | 取值形态 | 回调事件 |
|---|---|---|---|
| 微信 | button open-type | getPhoneNumber | onGetPhoneNumber |
| 支付宝 | button open-type | getAuthorize + scope=phoneNumber | onGetAuthorize |
| 抖音 | button open-type | getPhoneNumber | 事件回调 + 服务端解密 |
看着只有第二行不同,实际回调拿到的数据结构也完全不一样。微信给的是加密串要到服务端解密,支付宝给的是已经处理好的串,抖音的形态介于两者之间且依赖前一次 tt.login 的结果。
这类差异我们不建议用 if / else 在组件内部硬分叉,因为 JSX 里会同时出现两套属性名,可读性很差。用文件名后缀分文件更清晰:
// 文件:PhoneButton.weapp.tsx(仅微信端产物包含此文件)
// 环境:Taro 3.6.x + React 18
// 目的:微信端的手机号授权按钮实现
// 同名文件三个端各写一份,靠文件名后缀自动切换
export default function PhoneButton({ onSuccess }: { onSuccess: (iv: string, ed: string) => void }) {
// 微信返回的两个字段要一起送到服务端解密
const handle = (e) => {
onSuccess(e.detail.iv, e.detail.encryptedData)
}
return (
// 微信的 open-type 取值与另外两端不通用
<Button openType="getPhoneNumber" onGetPhoneNumber={handle}>
授权手机号
</Button>
)
}
// 文件:PhoneButton.alipay.tsx(仅支付宝端产物包含此文件)
// 环境:Taro 3.6.x + React 18
// 目的:支付宝端的手机号授权按钮实现
export default function PhoneButton({ onSuccess }: { onSuccess: (iv: string, ed: string) => void }) {
// 支付宝的回调结构与微信不同,这里统一裁第三段的形态
const handle = (e) => {
// result 里的串直接透传给服务端,形态由服务端兼容
onSuccess(e.detail?.response || '', '')
}
return (
// 支付宝要求 open-type 为 getAuthorize,并用 scope 声明权限
<Button openType="getAuthorize" scope="phoneNumber" onGetAuthorize={handle}>
授权手机号
</Button>
)
}
业务侧引用时不带后缀,import PhoneButton from './PhoneButton',构建器按 TARO_ENV 自动匹配。这种写法的好处是把差异彻底隔离到文件级别,同一个文件里只有一套 API,读代码不需要在脑子里跑分支。
工程约定:目录怎么切比技巧重要
复盘到后面我们发现,真正省时间的不是某个巧妙写法,而是几条硬约定:
src/platform/目录专放三端差异文件,业务代码禁止直接调Taro.xxx,必须过适配层- 平台专属文件用后缀区分,禁止在页面组件里直接写超过 15 行的平台分支
- 新增任一端特有能力时,同步补一份对另外两端的降级实现或空实现,避免编译不过
- CI 每端单独一条流水线,任何一端编译失败单独通知,不阻塞其他端发版
第三条我们是吃过亏才定下来的。有个同事在 platform/push.ts 里加了订阅消息能力,只写了微信版,结果抖音端构建直接找不到模块。后来的规则是:适配层文件必须三端齐全,缺哪端就补一个抛「本端暂不支持」的空实现,至少让错误在编译期而不是在运行时炸。
改造前后:数据说话
这套东西做完之后,我们对第 1 周到第 7 周的数据做了一次统计,口径是业务源码行数(不含 node_modules 与构建产物):
| 指标 | 第 3 周最初移植时 | 第 7 周适配层稳定后 | 变化 |
|---|---|---|---|
| 业务代码复用率 | 约 55% | 约 86% | 提升 31 个百分点 |
| 平台专属文件数 | 散落在 20 多个目录 | 集中 11 个目录 | 收敛约一半 |
| 新增一个端的耗时 | 11 人日 | 4 人日 | 下降约 64% |
| 三端全量构建耗时 | 6 分 40 秒 | 5 分 10 秒 | 下降 22% |
| 上线首月多端相关线上问题 | 23 条 | 5 条 | 下降约 78% |
复用率这一项我们统计的是「未被条件编译包裹的业务代码行数 / 总业务代码行数」。从 55% 到 86% 主要来自两件事:一是把散落的平台判断抽到了适配层,二是把大段差异用文件名后缀隔离,散落在 JSX 里的三元少了很多。
构建耗时下降 22% 属于意外收获。原因是错写的条件编译会让三端代码同时进产物,修完之后每端各自打各自的,体积和耗时都下来了。
平台差异不会消失,但可以被关进一层薄薄的适配层里。业务代码离平台 API 越远,移植一个新端就越接近改配置。
误区澄清与几个预判
复盘结束,说三个我们最初判断错的地方。
预判型需求是幻觉。我们一开始给「可能要上百度小程序」留了很多抽象,结果百度端始终没上,反而因为过度抽象多写了两千行没人用的代码。后来删掉了一半,至今没人问过这件事。没排期的端不要提前做兼容。
以为 CSS 能「大致共用」。实际是移植走到后期才集中爆雷,我们在末期连着改了三次样式,每次都是同一类选择器兼容问题。CSS 应该在第二端移植的第一周就整体过一遍,不要指望「看起来一样」。
把 H5 当成顺手产物。Taro 3 虽然能编译到 H5,但会员码、支付、授权这三块在浏览器里需要完全不同的实现,不是加几个 if 就能跑。我们最终没上 H5,避免了一次无效投入。
往后看,三个判断:一是各平台的私有能力只会更多不会更少,指望 API 收敛不现实,适配层应该是长期维护的一等公民;二是 RN 与鸿蒙方向的编译目标会在更多团队出现,到时候差异管理的复杂度是平方级上升;三是类型定义会成为跨端项目的关键资产,把三端的入参用 TypeScript 联合类型固化下来,比写文档有用得多。
这套写法不一定适合所有团队,如果你那边有更省事的处理方式,欢迎评论区贴一下做法。
参考与延伸
- Taro 官方文档(多端开发与条件编译):https://docs.taro.zone/
- 微信小程序开发框架:https://developers.weixin.qq.com/miniprogram/dev/framework/
- Taro 开源仓库 NervJS/taro:https://github.com/NervJS/taro
关键词:微信小程序开发、小程序 SEO、Taro 多端、条件编译、跨端适配、支付宝小程序、抖音小程序