UniApp 的核心价值是“一套代码多端运行”,但不同平台(H5、微信小程序、App 等)的 API、样式表现、组件能力存在差异。解决这些差异主要依赖三样武器:条件编译、多端适配策略和原生能力调用。
条件编译:让同一份代码在不同平台长出不同的样子
条件编译允许你在代码中标记某些区块“仅在特定平台编译时保留”,其他平台自动剔除。注释写法形如 #ifdef / #ifndef / #endif,支持 JS、CSS、HTML 以及页面文件路径。
<template>
<view>
<!-- #ifdef MP-WEIXIN -->
<view class="wx-only">微信小程序专属布局</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view class="h5-only">浏览器端专属提示</view>
<!-- #endif -->
<button @click="getLocation">获取位置</button>
</view>
</template>
<script>
export default {
methods: {
getLocation() {
// #ifdef APP-PLUS
// App 端使用 plus.geolocation
plus.geolocation.getCurrentPosition((pos) => {
console.log(pos.coords.latitude);
});
// #endif
// #ifdef MP-WEIXIN
// 小程序端使用 wx.getLocation
wx.getLocation({
type: 'wgs84',
success(res) {
console.log(res.latitude);
}
});
// #endif
// #ifdef H5
// H5 端使用浏览器 geolocation API
navigator.geolocation.getCurrentPosition((pos) => {
console.log(pos.coords.latitude);
});
// #endif
}
}
};
</script>
<style>
/* 全局样式 */
.title { font-size: 16px; }
/* 仅在小程序生效 */
/* #ifdef MP */
.title { font-size: 14px; }
/* #endif */
</style>
实用原则:
- 尽量少用:滥用条件编译会让代码难以维护。优先使用 UniApp 提供的统一 API (如
uni.getLocation),它们已内部封装了各平台差异。 - 集中管理:把平台特有逻辑封装成独立的 JS 模块或 mixin,通过条件编译引用不同实现,而不是在业务组件中到处插入。
- 样式兼容:小程序不支持某些 CSS 选择器、不支持
vh/vw单位的部分场景,可通过条件编译降级处理。
多端适配策略:不只是条件编译
除了代码级的条件编译,多端适配还需要在几个维度上做整体设计:
1. 页面布局适配
不同设备的屏幕宽高比、安全区域(如 iPhone 的底部横条)、标题栏高度各不相同。使用 UniApp 提供的 rpx(响应式像素)作为尺寸单位,它会根据屏幕宽度 750rpx 为基准等比缩放,基本解决大部分屏幕适配问题。对于特殊场景(如异形屏),结合 uni.getSystemInfo 获取安全区域偏移量,手动调整布局。
2. 组件选型与降级
某些组件只在特定平台可用(如 <map>、<video> 在各端表现不完全一致)。需要设计降级方案:在小程序用原生的 <map>,在 H5 用第三方地图库,并通过条件编译封装成统一的 <MyMap> 组件,对外暴露一致的 Props 和 Events。
3. 导航栏与页面跳转
小程序的导航栏样式限制较多,App 和 H5 相对灵活。通常采用自定义导航栏(navigationStyle: "custom",然后自行用 view 写导航)来抹平差异,保证各端外观统一,但需要手动处理状态栏高度。
4. 平台特性检测
运行时可通过 uni.getSystemInfoSync().platform 判断当前平台,做差异化逻辑,作为条件编译的补充。
const platform = uni.getSystemInfoSync().platform;
if (platform === 'ios') {
// 处理 iOS 特有的键盘弹出问题
}
原生能力调用:突破 Web 的限制
UniApp 统一封装了大量原生能力,通过 uni.xxx 系列 API 调用,如获焦、蓝牙、摄像头、推送等。但某些时候,你需要调用原生 SDK(如第三方支付、地图厂商的原生库),或需要极致的原生 UI 体验。
App 端(APP-PLUS)
UniApp 在 App 端底层是 WebView + Native 混合架构,可通过 plus 对象直接调用 Android/iOS 原生能力。此外,支持原生插件市场:下载别人写好的原生插件(如极光推送、高德定位),或自行编写原生插件(使用 Java/OC),再通过 JS 调用。
// 调用 App 端的原生二维码扫描(需提前集成原生插件)
const barcodeScan = uni.requireNativePlugin('barcode-scan');
barcodeScan.open({
defaultResult: '',
flashOn: false,
}, (result) => {
console.log('扫描结果:', result);
});
小程序端
通过 <button open-type> 等能力调用微信提供的原生功能(如获取用户信息、打开设置)。若需更底层能力,可开发微信小程序插件,通过条件编译引入。
H5 端
H5 能力受浏览器限制,可借助 JS SDK(如微信 JS-SDK)或通过 App 内置的 JSBridge(如果 H5 运行在自建 App 内)与原生通信。UniApp 在 H5 端会自动降级某些 API(如 uni.scanCode 会调起相机,但某些浏览器不支持)。
实用建议:
- 优先使用 uni API:它能覆盖绝大多数场景,并且跨平台一致性最好。
- 将原生调用封装成服务:例如创建一个
locationService.js,内部用条件编译区分实现,外部只暴露getCurrentLocation()方法,业务代码无需关心平台细节。 - 注意权限申请:原生能力往往需要权限(定位、摄像头、存储等),各平台的申请时机和文案不同,需在代码中做条件处理或统一在 manifest 中配置。
掌握这三板斧,就能够在“一套代码”的前提下,让应用在不同平台上都具备合格的原生体验。