Skip to main content

uni-app 跨平台 & 条件编译

一、uni-app 跨平台架构

1. 跨平台实现原理

1.1 编译时架构

uni-app 采用三层编译体系处理多平台适配:

  1. 模板转换层:将 Vue SFC 模板编译为目标平台的模板语法(WXML、AXML、SWAN 等)
  2. 样式转换层:通过 PostCSS 插件体系实现(rpx/upx)等单位转换和平台样式适配
  3. 逻辑处理层:通过 Babel 插件实现 API 转换和平台特性 polyfill

编译流程示例:

Vue 组件 ▶ AST 解析 ▶ 平台适配转换 ▶ 生成目标平台代码

1.2 运行时架构

uni-app 运行时包含三个核心模块:

  1. 虚拟 DOM 系统:基于 Vue 2.x 的响应式系统,实现跨平台渲染抽象

  2. 原生渲染引擎

    • 小程序:基于各平台原生组件
    • H5:基于标准 Web 渲染(HTML/CSS/JavaScript)
    • App:通过自研原生渲染引擎(或兼容 Weex 引擎)
  3. 统一 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 典型差异处理

  1. 自动适配(覆盖80%场景)

    /* 输入 */
    .box { margin: 10rpx; }

    /* 输出到 H5(假设设计稿宽度 750px) */
    .box { margin: calc(10 / 750 * 100vw); }

    /* 输出到小程序 */
    .box { margin: 10rpx; }
  2. 条件编译(平台特定逻辑)

    // #ifdef MP-WEIXIN
    wx.login()
    // #endif
  3. 扩展机制(深度定制需求)

    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

点击查看 %PLATFORM% 可取值

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 需包裹条件编译')
}
}
}
}
}
}
}