多平台与条件编译
同一项目同时运行多个平台时(例如两个终端分别跑 dev:mp-weixin 和 dev:mp-alipay),多个进程会读写同一个 pages.json。插件用文件锁和条件编译注释让各平台的配置共存。
工作方式
每次生成只更新当前平台的部分,其他平台的条目保留在自己的 #ifdef 块里:
{
"pages": [
// GENERATED BY UNI-PAGES, PLATFORM: H5 || MP-WEIXIN
{
"path": "pages/index/index"
},
// #ifdef H5
{
"path": "pages/h5-only"
},
// #endif
// #ifdef MP-WEIXIN
{
"path": "pages/wx-only"
}
// #endif
]
}内容相同的条目会合并成一条,平台列表叠在一起。内容不同的条目各自保留一个条件块。
整个读取、合并、写回流程在同一把文件锁内完成,写入使用临时文件和 rename。两个终端同时写入不会互相覆盖,也不会读到写了一半的文件。
生成标记
插件在 pages 数组和每个 subPackages 条目的头部写入:
// GENERATED BY UNI-PAGES, PLATFORM: H5 || MP-WEIXIN标记记录所有写入过该数组的平台。平台全集只增不减,并且始终包含当前运行的平台。
这有两个作用:
- 其他平台专属的条目会一直包在
#ifdef里,不会因为当前平台没有条目就裸输出到所有平台 - 插件能识别哪些条目是自己生成的,从而清理过期的页面和子包
没有标记的条目视为用户手写,不参与子包清理。例外是手写的 pages 条目:如果它既不在本次扫描结果里,也不在 pages.config.ts 的 pages 中,合并时会因为平台列表为空被丢弃,见下文。
不要手动编辑标记
改错标记会让条目被误判成手写,或者不加包裹地漏进其他平台。想彻底重置,直接删除生成的 pages.json,下次运行会从零生成。
自动清理
带生成标记但本次扫描不存在的条目会被处理:
| 对象 | 行为 |
|---|---|
| 主包页面 | 把当前平台从平台列表中移除,其他平台的条目保留 |
| 子包 | 当前平台看不到任何页面时,整个子包条目删除 |
子包 plugins | 配置里有就写入,配置里删掉就从生成的子包上清除 |
| 手写子包 | 原样保留,不受清理影响 |
子包整体删除是有意为之。当前平台的构建产物里没有这个分包目录,app.json 里留下空壳会指向不存在的 root。其他平台下次运行时会按自己的扫描结果把子包写回来。
手写的 pages 条目如果不在扫描结果里,也不在 pages.config.ts 的 pages 中,合并时会因为平台列表为空被丢弃。这是历史行为。需要长期保留的手写页面,请写进配置的 pages 数组。
tabBar 条目顺序
tabBar.list 的顺序用户直接可见,规则和 pages 不同:
- 本次配置覆盖全部既有条目时,整个列表按配置顺序输出。其他平台新增的条目会落在它声明的位置
- 没有覆盖全部条目时,既有条目保持磁盘顺序,本次新增的条目插到配置里最近的前驱之后
两个平台的配置都覆盖全部条目但顺序不同时,共享文件跟随最后写入者。条目内容相同而合并成一条时没有按平台区分的副本,另一个正在运行的平台在下一次生成前可能读到对方顺序。并发开发时建议各平台保持一致的完整 tab 顺序。
pages 数组不参与这个重排。首页条目由“首页提到最前”的规则单独管理,各平台声明的首页不同时,首页变体的相对顺序按首次出现保持稳定。
tabBar 顶层属性
color、selectedColor、custom、position、midButton 等属性同样按平台保存:
- 各平台取值不同时,每个值写在自己的
#ifdef块里,同一个属性名出现多次 - 取值相同时合并为一条不带条件的属性
- 只有声明了非空
list的运行才会写属性。list为空或没有 tabBar 的运行不会覆盖其他平台已写入的属性 - 某个平台不再声明某个属性时,这个平台上的旧值会被移除,不会留下过期的条件块
配置是 tabBar 属性的来源。手写 pages.json 里没有对应 #ifdef 包裹、也没有写进配置的属性,会在该平台下次运行时被移除。需要长期保留的值请写进 pages.config.ts,或者放进手写的 #ifndef 块。
uni-app 条件编译后,每个平台只会看到一个值。属性之间是独立的,同一个条件块包住多个属性时,插件会把它们拆成各自完整的包裹重新输出。
手写的 #ifndef
插件只会写 #ifdef。手写的 #ifndef 块表达不了“按平台列表”的正向规则,因此原样保留、不参与合并:
- 页面和 tabBar 条目:只包裹紧随其后的那一个条目
- tabBar 属性:一个
#ifndef块可以包住多个属性,重新输出时每个属性都有自己完整的开头和#endif - 扫描结果和某个
#ifndef条目内容相同时,会并进该条目,避免当前平台出现重复路由
两个边界需要注意:
- 当前平台恰好是被排除的平台时,内容相等的配置按
#ifndef语义同样被隐藏 - 同一个 tabBar 属性同时存在
#ifndef副本和各平台不同取值时,被多份副本覆盖的平台会出现重复键,按 JSON 规则后面的键覆盖前面的。当前平台自己的取值写在最后,优先生效
格式限制
多平台合并依赖注释,因此:
minify: true会丢掉全部注释,#ifdef和生成标记都会消失,属性变成最后写入者说了算indent: 0或空字符串会把输出压成单行,同样放不下注释
同一项目的所有平台应保持一致的格式化设置。推荐使用默认的多行输出。
平台标识
条件编译里的平台名使用大写形式,例如 H5、MP-WEIXIN。插件按 process.env.UNI_PLATFORM 判断当前平台,并在合并时统一转成大写。
h5 和 web 在 definePage 的条件 DSL 里视为同一平台。文件名后缀规则里没有这个别名,两者保持一致才能避免配置和文件名判断不一致。