uni-app 条件编译与多端差异化开发:#ifdef 实战指南

uni-app 条件编译与多端差异化开发:#ifdef 实战指南

admin
2026-07-20 / 0 评论 / 5 阅读

"一次开发,多端运行"是 uni-app 的招牌,但真实项目里各平台差异无处不在:微信有开放能力、H5 有跨域、App 有原生权限。条件编译(#ifdef)就是 uni-app 处理这些差异的官方答案。本文讲透条件编译的语法、边界和多端适配的完整套路。

一、条件编译基础

语法:#ifdef / #ifndef / #endif

// #ifdef MP-WEIXIN
console.log('只有微信小程序端会执行')
// #endif

// #ifndef H5
console.log('除了 H5 端,其他端都执行')
// #endif

// #ifdef MP-WEIXIN || MP-ALIPAY
console.log('微信或支付宝小程序')
// #endif

有效平台标识

标识平台
H5Web(含浏览器/移动端浏览器)
MP-WEIXIN微信小程序
MP-ALIPAY支付宝小程序
MP-BAIDU百度小程序
MP-TOUTIAO抖音小程序
APP-PLUSApp(5+ App / nvue)
APP-PLUS-NVUE仅 nvue 页面

条件编译可以用在哪里

不只是 JS——template、css、json 配置都能用

<template>
  <view>
    <!-- #ifdef MP-WEIXIN -->
    <button open-type="share">分享给好友</button>
    <!-- #endif -->

    <!-- #ifdef H5 -->
    <button @click="copyLink">复制链接</button>
    <!-- #endif -->
  </view>
</template>

<style>
/* #ifdef H5 */
.page { cursor: pointer; }
/* #endif */
</style>

注意 template 中的写法是 HTML 注释形式 <!-- #ifdef -->,CSS 中是块注释 /* #ifdef */,JS 中是行注释 // #ifdef三种注释形态对应三种文件区域,写错注释类型编译器识别不到。

pages.json 里的条件编译

连页面注册都能按平台区分:

{
  "pages": [
    { "path": "pages/index/index" },
    // #ifdef MP-WEIXIN
    { "path": "pages/live/live" },
    // #endif
    // #ifdef APP-PLUS
    { "path": "pages/push/push" },
    // #endif
    { "path": "pages/mine/mine" }
  ]
}

微信端才有直播间页,App 端才有推送设置页——页面级的差异化直接在源头解决。

二、多端 API 差异处理

支付:三端三套流程

支付是平台差异最深的领域,三端只能分别实现:

// utils/pay.js
export function pay(orderId) {
  return new Promise((resolve, reject) => {
    // #ifdef MP-WEIXIN
    uni.requestPayment({
      provider: 'wxpay',
      // 后端统一下单返回的微信支付参数
      timeStamp: payParams.timeStamp,
      nonceStr: payParams.nonceStr,
      package: payParams.package,
      signType: payParams.signType,
      paySign: payParams.paySign,
      success: resolve,
      fail: reject
    })
    // #endif

    // #ifdef APP-PLUS
    uni.requestPayment({
      provider: 'alipay', // App 端可自主选择支付宝/微信
      orderInfo: payParams.orderInfo,
      success: resolve,
      fail: reject
    })
    // #endif

    // #ifdef H5
    // H5 微信支付需要跳转 JSAPI 或唤起微信客户端
    location.href = payParams.mwebUrl
    // #endif
  })
}

登录:获取用户信息的方式差异

export function login() {
  // #ifdef MP-WEIXIN
  // 小程序:wx.login 换 code
  const [err, res] = await uni.login({ provider: 'weixin' })
  return await request.post('/auth/wx-login', { code: res.code })
  // #endif

  // #ifdef APP-PLUS
  // App:可以接手机号一键登录
  const [loginErr, loginRes] = await uni.login({ provider: 'univerify' })
  return await request.post('/auth/phone-login', {
    access_token: loginRes.authResult.access_token,
    openid: loginRes.authResult.openid
  })
  // #endif

  // #ifdef H5
  // H5:跳转微信 OAuth 网页授权
  const redirectUri = encodeURIComponent(location.origin + '/auth-callback')
  location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo`
  // #endif
}

API 可用性检测:uni.canIUse

平台差异不明确时,运行时检测比条件编译更灵活:

if (uni.canIUse('getUserProfile')) {
  uni.getUserProfile({ desc: '用于完善会员资料' })
} else {
  // 基础库不支持时的降级方案
  uni.showModal({ title: '提示', content: '请升级微信版本' })
}

三、样式多端适配

rpx 的本质与陷阱

rpx 以 750 屏宽为基准:750rpx = 屏幕宽度。设计稿 375px 宽的图,标注多少 px 就写多少 × 2 的 rpx,换算自动化。

但 rpx 有几个陷阱:

  1. H5 端 rpx 基于视口宽,横屏或宽屏设备(iPad)上元素会被拉得离谱
/* #ifdef H5 */
/* H5 端用 max-width 约束内容区 */
.page { max-width: 500px; margin: 0 auto; }
/* #endif */
  1. 1rpx 边框在部分设备上不显示:物理像素取整导致,用 1px + transform 或背景图实现细边框更稳
  2. 字体大小慎用 rpx:大屏上字会跟着放大,正文字号建议用 px

安全区适配(刘海屏)

.safe-bottom {
  /* 苹果底部安全区 */
  padding-bottom: constant(safe-area-inset-bottom); /* iOS < 11.2 */
  padding-bottom: env(safe-area-inset-bottom);      /* iOS >= 11.2 */
}
// pages.json 页面配置
{
  "path": "pages/index/index",
  "style": {
    "navigationStyle": "custom", // 自定义导航栏需要自己处理状态栏高度
    "navigationBarTitleText": "首页"
  }
}

自定义导航栏时获取状态栏高度:

onLoad() {
  const sysInfo = uni.getSystemInfoSync()
  this.statusBarHeight = sysInfo.statusBarHeight
}

四、多端架构组织实践

差异代码分层而不是满屏 #ifdef

条件编译用多了,代码可读性会急剧恶化。推荐架构:接口统一,实现分平台目录

src/
├── platform/
│   ├── wx/
│   │   ├── pay.js       # 微信小程序支付实现
│   │   └── login.js
│   ├── app/
│   │   ├── pay.js       # App 支付实现
│   │   └── login.js
│   └── h5/
│       ├── pay.js       # H5 支付实现
│       └── login.js
└── utils/
    └── pay.js           # 统一入口
// utils/pay.js —— 收敛所有条件编译到一个文件
// #ifdef MP-WEIXIN
import pay from '@/platform/wx/pay'
// #endif
// #ifdef APP-PLUS
import pay from '@/platform/app/pay'
// #endif
// #ifdef H5
import pay from '@/platform/h5/pay'
// #endif

export default pay

业务代码只 import 统一入口,完全无感知平台差异。条件编译被压缩在入口文件里,各端实现独立演进互不干扰。

何时用条件编译,何时用运行时判断

场景方案
代码只对某端存在(API、页面、组件)条件编译
某端逻辑完全不同条件编译 + 分平台目录
只是参数/样式的小差异运行时判断 + CSS 条件编译

原则:能在编译期裁剪掉的代码不要留到运行时,包体积是小程序的硬指标。

五、调试技巧

  • H5 端开发效率最高,先在 H5 调通逻辑再上小程序真机
  • process.env.UNI_PLATFORM 可在 JS 中判断当前编译平台(vite 版本),用于日志过滤
  • 小程序端用微信开发者工具的"编译模式"自定义启动页面和参数
  • 差异 bug 高发区:时间日期格式(iOS 不认 2026-08-23 短横线格式,要替换成 /)、position: fixed 在部分安卓机的表现、webview 与原生通信
// iOS 日期兼容:经典坑
const date = new Date('2026-08-23 12:00') // iOS 返回 Invalid Date
const fixed = new Date('2026-08-23 12:00'.replace(/-/g, '/')) // OK

总结

  • 条件编译三种注释形态分别对应 JS / template / CSS 区域
  • pages.json 支持条件编译,页面级差异从注册源头解决
  • "接口统一、实现分层"的架构能把 #ifdef 压缩到入口文件
  • 能编译期裁剪就不运行时判断,控制包体积
  • 高频坑:rpx 横屏、1rpx 边框、iOS 日期格式、uploadFile 字符串响应

多端开发的真实成本不在"写代码",而在"记住差异"。把本文的分层架构和坑清单用起来,多端项目就能保持可控。

0

评论 (0)

取消
0:00