Skip to content

多平台与条件编译 ​

同一项目同时运行多个平台时(例如两个终端分别跑 dev:mp-weixin 和 dev:mp-alipay),多个进程会读写同一个 pages.json。插件用文件锁和条件编译注释让各平台的配置共存。

工作方式 ​

每次生成只更新当前平台的部分,其他平台的条目保留在自己的 #ifdef 块里:

jsonc
{
  "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 条目的头部写入:

text
// 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 里视为同一平台。文件名后缀规则里没有这个别名,两者保持一致才能避免配置和文件名判断不一致。