Appearance
UniApp 条件编译
1. 为什么选择条件编译处理跨端兼容
uni-app 已将常用的组件、API 封装到框架中,开发者按照 uni-app 规范开发即可保证多平台兼容,大部分业务均可直接满足。
但每个平台有自己的一些特性,因此会存在一些无法跨平台的情况。
- 大量写 “if else”,会造成代码执行性能低下和管理混乱;
- 编译到不同的工程后二次修改,会让后续升级变的很麻烦;
- 为每个平台重写,明明主业务逻辑又一样;
在 C 语言中,通过 #ifdef、#ifndef 的方式,为 Windows、Mac 等不同 OS 编译不同的代码。
uni-app 团队参考这个思路,为 uni-app 提供了条件编译手段,在一个工程里优雅的完成了平台个性化实现。
2. 条件编译
条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。
2.1. 使用方法
以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。
#ifdef:“if defined” 仅在某平台存在;#ifndef:“if not defined” 除了某平台均存在;%PLATFORM%:平台名称;
| 条件编译写法 | 说明 |
|---|---|
C | 仅出现在 App 平台下的代码 |
C | 除了 H5 平台,其它平台均存在的代码(注意 if 后面有个 n) |
C | 在 H5 平台或微信小程序平台存在的代码(这里只有 ||,不可能出现 &&,因为没有交集) |
%PLATFORM% 可取值:
| 值 | 生效条件 | 版本支持 |
|---|---|---|
VUE3 | uni-app js 引擎版用于区分 vue2 和 3,详情 | HBuilderX 3.2.0+ |
VUE2 | uni-app js 引擎版用于区分 vue2 和 3,详情 | |
UNI-APP-X | 用于区分是否是 uni-app x 项目,详情 | HBuilderX 3.9.0+ |
uniVersion | 用于区分编译器的版本,详情 | HBuilderX 3.9.0+ |
APP | App | |
APP-PLUS | uni-app js 引擎版编译为 App 时 | |
APP-PLUS-NVUE 或 APP-NVUE | App nvue 页面 | |
APP-ANDROID | App Android 平台,详情 | |
APP-IOS | App iOS 平台,详情 | |
APP-HARMONY | App HarmonyOS Next 平台 | |
H5 | H5(推荐使用 WEB) | |
WEB | web(同 H5) | HBuilderX 3.6.3+ |
MP-WEIXIN | 微信小程序 | |
MP-ALIPAY | 支付宝小程序 | |
MP-BAIDU | 百度小程序 | |
MP-TOUTIAO | 抖音小程序 | |
MP-LARK | 飞书小程序 | |
MP-QQ | QQ 小程序 | |
MP-KUAISHOU | 快手小程序 | |
MP-JD | 京东小程序 | |
MP-360 | 360 小程序 | |
MP-HARMONY | 鸿蒙元服务 | HBuilderX 4.34+ |
MP-XHS | 小红书小程序 | |
MP | 微信小程序 / 支付宝小程序 / 百度小程序 / 抖音小程序 / 飞书小程序 / QQ 小程序 / 360 小程序 / 鸿蒙元服务 | |
QUICKAPP-WEBVIEW | 快应用通用(包含联盟、华为) | |
QUICKAPP-WEBVIEW-UNION | 快应用联盟 | |
QUICKAPP-WEBVIEW-HUAWEI | 快应用华为 |
支持的文件:
.vue/.nvue/.uvue.js/.uts.csspages.json- 各预编译语言文件,如:
.scss、.less、.stylus、.ts、.pug
注意:
条件编译是利用注释实现的,在不同语法里注释写法不一样,js/uts 使用
// 注释、css 使用/* 注释 */、vue/nvue/uvue 模板里使用<!-- 注释 -->;条件编译
APP-PLUS包含APP-NVUE和APP-VUE,APP-PLUS-NVUE和APP-NVUE没什么区别,为了简写后面出了APP-NVUE;对于未定义平台名称,可能是名称写错了,也可能是低版本 HBuilderX 还不认识这个平台。此时的条件编译,
#ifdef中的代码不会生效,而#ifndef中的代码会生效;使用条件编译请保证编译前和编译后文件的语法正确性,即要保障无论条件编译是否生效都能通过语法校验。比如:json 文件中不能有多余的逗号,js 中不能重复导入;
JSON 错误示例:
JSON{ "key": "a", // #ifdef MP-WEIXIN "key": "b" // #endif }JSON 正确示例:
JSON{ "key": "a" // #ifdef MP-WEIXIN ,"key": "b" // #endif }JS 错误示例
JavaScript// #ifdef MP-WEIXIN import a from 'a/wx' // #endif // #ifndef MP-WEIXIN import a from 'a/index' // #endifJS 正确示例
JavaScript// #ifdef MP-WEIXIN import a as aWx from 'a/wx' // #endif // #ifndef MP-WEIXIN import a as aIndex from 'a/index' // #endif var a // #ifdef MP-WEIXIN a = aWx // #endif // #ifndef MP-WEIXIN a = aIndex // #endif
VUE3 需要在项目的
manifest.json文件根节点配置"vueVersion" : "3"。
2.2. API 的条件编译
JavaScript
// #ifdef %PLATFORM%
平台特有的 API 实现
// #endif示例,如下代码仅在 App 下出现:
JavaScript
// #ifdef APP-PLUS
plus.push.addEventListener("click", function (msg) {
var payload = null;
var action = "";
if (msg.payload) {
if (typeof msg.payload === "String") {
payload = JSON.parse(msg.payload);
}
action = payload.action;
if (action === "open") {
plus.webview.open(payload.url);
}
}
});
// #endif示例,如下代码不会在 H5 平台上出现:
JavaScript
// #ifndef H5
uni.scanCode({
success: (res) => {
console.log(res.result);
},
});
// #endif除了支持单个平台的条件编译外,还支持多平台同时编译,使用 || 来分隔平台名称。
示例,如下代码会在 App 和 H5 平台上出现:
JavaScript
// #ifdef APP-PLUS || H5
uni.chooseVideo({
success: (res) => {
console.log(res.result);
},
});
// #endif2.3. 组件的条件编译
HTML
<!-- #ifdef %PLATFORM% -->
平台特有的组件
<!-- #endif -->示例,如下公众号关注组件仅会在微信小程序中出现:
HTML
<view>
<view>微信公众号关注组件</view>
<view>
<!-- uni-app 未封装,但可直接使用微信原生的 official-account 组件-->
<!-- #ifdef MP-WEIXIN -->
<official-account></official-account>
<!-- #endif -->
</view>
</view>2.4. 样式的条件编译
CSS
/* #ifdef %PLATFORM% */
平台特有样式
/* #endif */注意:样式的条件编译,无论是 css 还是 sass/scss/less/stylus 等预编译语言中,必须使用 /* 注释 */ 的写法。
正确写法
CSS/* #ifdef MP-WEIXIN */ .wx-color { color: #fff000; } /* #endif */错误写法
CSS// #ifdef MP-WEIXIN .wx-color { color: #fff000; } // #endif
2.5. pages.json 的条件编译
下面的页面,只有运行至 App 时才会编译进去。
JSON
// #ifdef APP-PLUS
{
"path": "pages/api/speech/speech",
"style": {
"navigationBarTitleText": "语音识别"
}
}
// #endif不同平台下的特有功能,以及小程序平台的分包,都可以通过 pages.json 的条件编译来更好地实现。这样,就不会在其它平台产生多余的资源,进而减小包体积。
json 的条件编译,如不同平台的 key 名称相同,cli 项目下开发者自己安装的校验器会报错,需自行关闭这些校验器对 json 相同 key 的校验规则。如果使用 HBuilderX 的校验器,无需在意此问题,HBuilderX 的语法校验器为此优化过。
2.6. static 目录的条件编译
这里也解释了 static 内的哪些目录是特殊的。
在不同平台,引用的静态资源可能也存在差异,通过 static 的条件编译可以解决此问题,static 目录下新建不同平台的专有目录,目录名称均为小写。
| 目录名称 | 说明 | 版本支持 |
|---|---|---|
app-plus | app(推荐使用 app) | |
app-harmony | HarmonyOS Next | |
app | app | uni-app 3.9+ |
h5 | H5(推荐使用 web) | |
web | web | uni-app 3.9+ |
mp-weixin | 微信小程序 | |
mp-alipay | 支付宝小程序 | |
mp-baidu | 百度小程序 | |
mp-qq | QQ 小程序 | |
mp-toutiao | 抖音小程序 | |
mp-lark | 飞书小程序 | |
mp-kuaishou | 快手小程序 | |
mp-harmony | 鸿蒙元服务 | |
mp-xhs | 小红书小程序 |
专有目录下的静态资源只有在特定平台才会编译进去。
如以下目录结构,a.png 只有在微信小程序平台才会编译进去,b.png 在所有平台都会被编译。
Text
┌─static
│ ├─mp-weixin
│ │ └─a.png
│ └─b.png
├─main.js
├─App.vue
├─manifest.json
└─pages.json1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
注意:
自 HBuilderX 3.9+ 起,App 平台
static目录同时支持app、app-plus目录,Web 平台static目录同时支持web、h5目录;自 HBuilderX 3.98+ 起,编译时增加
static下被忽略的非当前平台专有目录提示信息,如static下同时存在app、web,运行到 web 时,会提示static/app已被忽略;
2.7. 整体目录条件编译
如果想把各平台的页面文件更彻底的分开,也可以在 uni-app 项目根目录创建 platforms 目录,然后在下面进一步创建 app-plus、mp-weixin 等子目录,存放不同平台的文件。
注意:
platforms目录下只支持放置页面文件(即页面 vue 文件),如果需要对其他资源条件编译,建议使用static目录的条件编译。
2.8. uts 的条件编译
对于 APP-ANDROID 和 APP-IOS 两个平台:
- 在 uni-app 项目中,仅 uts 文件中支持(通常是 uts 插件里使用);
- 在 uni-app x 项目中,只要是条件编译支持的文件,均可以使用;
TypeScript
// #ifdef %PLATFORM%
平台特有的 API 实现
// #endif2.9. uni-app x 项目的条件编译
使用 UNI-APP-X 条件编译,来区分 uni-app x 项目和 uni-app 项目。
uni-app x 项目
JavaScript// #ifdef UNI-APP-X 代码会生效 // #endif // #ifndef UNI-APP-X 代码不会生效 // #endifuni-app 项目
JavaScript// #ifdef UNI-APP-X 代码不会生效 // #endif // #ifndef UNI-APP-X 代码会生效 // #endif
2.10. 版本的条件编译
信息
HBuilderX 3.9+
插件作者的插件,需要适配各种插件使用者,使用者的 uni-app 版本,可能有很多。
有些问题可以在运行期判断适配,有些则需要在编译器处理,尤其是不处理可能会导致低版本编译失败的情况。
为此,uni-app 的条件编译新增了 uniVersion。在 uni-app 和 uni-app x 中均可使用。
JavaScript
// #ifdef uniVersion > 3.9
编译器版本大于 3.9 时生效
// #endif注意,从 HBuilderX 3.9 起,版本号将由三段式字符串改为小数。
即 HBuilderX 3.9.1,将改为 3.91。
这样条件编译判断时,仅需输入一个数字即可。
注意低于 3.9 版本的 HBuilderX 的条件编译不识别 uniVersion。
2.11. HBuilderX 支持
HBuilderX 为 uni-app 的条件编译提供了丰富的支持:
代码块支持
在 HBuilderX 中开发 uni-app 时,通过输入
ifdef可快速生成条件编译的代码片段:
图 2.1 - 代码块支持 语法高亮
在 HBuilderX 中对条件编译的代码注释部分提供了语法高亮,可分辨出写法是否正确,使得代码更加清晰(独立 js 文件需在编辑器右下角切换 javascript es6+ 编辑器,独立 css 文件暂不支持高亮,但不高亮不影响使用)。

图 2.2 - 语法高亮 正确注释和快速选中
在 HBuilderX 中,
ctrl+alt+/即可生成正确注释(js:// 注释、css:/* 注释 */、vue/nvue 模板:<!-- 注释 -->)。
图 2.3 - 注释自动补全 点击
ifdef或endif可快速选中条件编译部分;点击左侧的折叠图标,可折叠条件编译部分代码。
图 2.4 - 代码折叠
2.12. 注意
Android 和 iOS 平台不支持通过条件编译来区分,如果需要区分 Android、iOS 平台,请通过调用
uni.getSystemInfo来获取平台信息。支持ifios、ifAndroid代码块,可方便编写判断。有些跨端工具可以提供 js 的条件编译或多态,但这对于实际开发远远不够。uni-app 不止是处理 js,任何代码都可以多端条件编译,才能真正解决实际项目的跨端问题。另外所谓多态在实际开发中会造成大量冗余代码,很不利于复用和维护。举例,微信小程序主题色是绿色,而百度支付宝小程序是蓝色,你的应用想分平台适配颜色,只有条件编译是代码量最低、最容易维护的。
有些公司的产品运营总是给不同平台提不同需求,但这不是拒绝 uni-app 的理由。关键在于项目里,复用的代码多还是个性的代码多,正常都是复用的代码多,所以仍然应该多端。而个性的代码放到不同平台的目录下,差异化维护。
3. 环境变量
uni-app 项目中配置环境变量主要有如下三种方式:
vue-config.js在
vue.config.js中可以修改 webpack 配置,包括环境变量,具体参考 vue-config.js。package.json在自定义条件编译平台时,可以在
package.json文件的env节点下配置环境变量,具体参考 package.json.envHBuilderX中的uni-app vue3和uni-app x(4.25+)项目及CLI创建的项目中可以在根目录中放置.env文件来指定环境变量,具体参考:Vue2,Vue3。
警告
uni-app x 不支持自定义环境变量。
环境条件编译与树摇
在 UniApp Vue3 项目中,可以使用 vite 的 import.meta.env 实现不同运行环境编译不同代码。如下 config.js(非对应环境的代码,将在编译时进行树摇):
JavaScript
const config = {
apiBaseURL: ""
debug: true
};
if (import.meta.env.PROD) {
Object.assign(config, {
apiBaseURL: "https://your.prod.domain/",
debug: false
});
} else {
Object.assign(config, {
apiBaseURL: "https://your.dev.domain/",
debug: true
});
}
export default config;