uni-app 跨平台 & 条件编译
一、uni-app 跨平台架构
1. 跨平台实现原理
1.1 编译时架构
uni-app 采用三层编译体系处理多平台适配:
- 模板转换层:将 Vue SFC 模板编译为目标平台的模板语法(WXML、AXML、SWAN 等)
- 样式转换层:通过 PostCSS 插件体系实现(rpx/upx)等单位转换和平台样式适配
- 逻辑处理层:通过 Babel 插件实现 API 转换和平台特性 polyfill
编译流程示例:
Vue 组件 ▶ AST 解析 ▶ 平台适配转换 ▶ 生成目标平台代码
1.2 运行时架构
uni-app 运行时包含三个核心模块:
虚拟 DOM 系统:基于 Vue 2.x 的响应式系统,实现跨平台渲染抽象
原生渲染引擎:
- 小程序:基于各平台原生组件
- H5:基于标准 Web 渲染(HTML/CSS/JavaScript)
- App:通过自研原生渲染引擎(或兼容 Weex 引擎)
统一 API 系统:
// 编译时根据平台直接替换为对应实现
// 编译后 H5 端代码:
uni.request = function(options) {
return new XMLHttpRequest(...);
}
// 编译后微信小程序端代码:
uni.request = function(options) {
return wx.request(...);
}
// ...
2. 多端差异处理机制
2.1 平台识别系统
uni-app 通过 UNI_PLATFORM 环境变量识别多个平台,处理策略矩阵:
特征维度 | 处理方式 |
---|---|
API 差异 | 统一 API + 条件编译 |
组件差异 | 虚拟组件 + 平台组件映射 |
样式差异 | 自动前缀 + 单位转换 |
生命周期差异 | 事件代理 + 适配层 |
2.2 典型差异处理
自动适配(覆盖80%场景)
/* 输入 */
.box { margin: 10rpx; }
/* 输出到 H5(假设设计稿宽度 750px) */
.box { margin: calc(10 / 750 * 100vw); }
/* 输出到小程序 */
.box { margin: 10rpx; }条件编译(平台特定逻辑)
// #ifdef MP-WEIXIN
wx.login()
// #endif扩展机制(深度定制需求)
uni.requireNativePlugin('MyCustomModule')
二、条件编译
条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。
1. 基本用法
以 #ifdef
或 #ifndef
加 %PLATFORM%
开头,以 #endif
结尾。
#ifdef
:if defined 仅在某平台存在#ifndef
:if not defined 除了某平台均存在%PLATFORM%
:平台名称
仅出现在 App 平台下的代码
#ifdef APP-PLUS
...
#endif
除了 H5 平台,其它平台均存在的代码(注意if后面有个n)
#ifndef H5
...
#endif
在 H5 平台或微信小程序平台存在的代码(这里只有||,不可能出现&&,因为没有交集)
#ifdef H5 || MP-WEIXIN
...
#endif
2. 高级用法
2.1 组合条件表达式
支持复杂逻辑表达式:
// #if MP-WEIXIN || (H5 && VUE3)
const useNewAPI = true
// #endif
2.2 环境变量注入
自定义编译条件:
// package.json
{
"uni-app": {
"scripts": {
"mp-weixin": {
"define": { "ENABLE_DEBUG": true }
}
}
}
}
三、工程化最佳实践
1. 代码组织规范
推荐目录结构:
src/
├── common/ # 全平台通用代码
├── platform/
│ ├── h5/ # H5 专用代码
│ ├── mp-weixin/ # 微信小程序专用代码
│ └── app/ # App 专用代码
└── main.js # 统一入口
2. 最佳实践模式
2.1 平台服务抽象层
// network-service.js
export default {
// #ifdef H5
request: axios,
// #endif
// #ifdef MP-WEIXIN
request: wx.request,
// #endif
}
2.2 构建优化
- 条件编译分包
- Tree Shaking 优化
分包条件编译:
// vue.config.js
module.exports = {
chainWebpack(config) {
config.plugin('define').tap(args => {
args[0]['process.env.UNI_SUBPACKAGE'] = JSON.stringify(process.env.UNI_SUBPACKAGE)
return args
})
}
}
Tree Shaking 优化:
通过条件编译标记(如 #ifdef
)在编译时实现静态代码剪枝(Static Code Pruning),结合 Rollup 的 Tree Shaking 机制,移除未满足编译条件的冗余代码:
// 编译前
function unused() {} // #ifdef DEBUG
// 生产环境编译后(无 DEBUG 标记)
// 函数被完全移除
3. 质量保障
3.1 多端测试方案
差异化测试用例标记:
describe('支付功能', () => {
// #ifdef MP-WEIXIN
it('微信支付', () => {...})
// #endif
// #ifdef APP
it('原生支付', () => {...})
// #endif
})
3.2 静态代码检查
自定义 ESLint 规则示例:
module.exports = {
rules: {
'no-platform-specific-api': {
create(context) {
return {
MemberExpression(node) {
if (node.object.name === 'wx' && !isInConditionalBlock(node)) {
context.report('直接使用 wx API 需包裹条件编译')
}
}
}
}
}
}
}