404 lines
7.8 KiB
Markdown
404 lines
7.8 KiB
Markdown
|
||
|
||
|
||
# 多端适配方案
|
||
|
||
## 一、平台差异处理
|
||
|
||
### 1.1 条件编译
|
||
使用 UniApp 的条件编译处理不同平台的差异:
|
||
|
||
```javascript
|
||
// #ifdef MP-WEIXIN
|
||
// 微信小程序特有代码
|
||
wx.login()
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
// H5特有代码
|
||
window.location.href = '/login'
|
||
// #endif
|
||
|
||
// #ifdef APP-PLUS
|
||
// App特有代码
|
||
plus.runtime.restart()
|
||
// #endif
|
||
```
|
||
|
||
### 1.2 平台判断工具
|
||
使用封装的平台判断工具:
|
||
|
||
```javascript
|
||
import { getPlatform, isMiniProgram, isH5, isApp } from '@/utils/platform'
|
||
|
||
if (isMiniProgram()) {
|
||
// 小程序端逻辑
|
||
} else if (isH5()) {
|
||
// H5端逻辑
|
||
} else if (isApp()) {
|
||
// App端逻辑
|
||
}
|
||
```
|
||
|
||
## 二、API 适配
|
||
|
||
### 2.1 统一 API 封装
|
||
对不同平台的 API 进行统一封装:
|
||
|
||
```javascript
|
||
// 跨端导航
|
||
export const navigateTo = (url, params = {}) => {
|
||
const query = Object.keys(params)
|
||
.map(key => `${key}=${encodeURIComponent(params[key])}`)
|
||
.join('&')
|
||
const fullUrl = query ? `${url}?${query}` : url
|
||
|
||
uni.navigateTo({ url: fullUrl })
|
||
}
|
||
|
||
// 跨端存储
|
||
export const setStorage = (key, value) => {
|
||
// #ifdef MP-WEIXIN
|
||
wx.setStorageSync(key, value)
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
localStorage.setItem(key, JSON.stringify(value))
|
||
// #endif
|
||
|
||
// #ifdef APP-PLUS
|
||
plus.storage.setItem(key, JSON.stringify(value))
|
||
// #endif
|
||
}
|
||
```
|
||
|
||
### 2.2 第三方登录适配
|
||
不同平台的第三方登录实现:
|
||
|
||
```javascript
|
||
// 微信登录
|
||
export const wechatLogin = () => {
|
||
return new Promise((resolve, reject) => {
|
||
// #ifdef MP-WEIXIN
|
||
wx.login({
|
||
success: (res) => {
|
||
resolve(res.code)
|
||
},
|
||
fail: reject
|
||
})
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
// H5微信授权登录
|
||
const appId = 'YOUR_APP_ID'
|
||
const redirectUri = encodeURIComponent(window.location.href)
|
||
window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_userinfo#wechat_redirect`
|
||
// #endif
|
||
})
|
||
}
|
||
```
|
||
|
||
## 三、样式适配
|
||
|
||
### 3.1 尺寸单位适配
|
||
- **小程序端**:使用 `rpx`(750rpx = 屏幕宽度)
|
||
- **H5端**:使用 `rem` 或 `vw`
|
||
- **App端**:使用 `rpx` 或 `upx`
|
||
|
||
```scss
|
||
// 统一使用 rpx,UniApp 会自动转换
|
||
.container {
|
||
width: 750rpx;
|
||
padding: 30rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
```
|
||
|
||
### 3.2 安全区域适配
|
||
处理刘海屏、底部安全区域:
|
||
|
||
```scss
|
||
.page {
|
||
// 顶部安全区域
|
||
padding-top: constant(safe-area-inset-top);
|
||
padding-top: env(safe-area-inset-top);
|
||
|
||
// 底部安全区域
|
||
padding-bottom: constant(safe-area-inset-bottom);
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
```
|
||
|
||
### 3.3 状态栏高度适配
|
||
```javascript
|
||
// 获取状态栏高度
|
||
const systemInfo = uni.getSystemInfoSync()
|
||
const statusBarHeight = systemInfo.statusBarHeight
|
||
|
||
// 在样式中使用
|
||
const navBarHeight = statusBarHeight + 44 // 44为导航栏高度
|
||
```
|
||
|
||
## 四、功能适配
|
||
|
||
### 4.1 分享功能
|
||
```javascript
|
||
// 小程序分享
|
||
// #ifdef MP-WEIXIN
|
||
onShareAppMessage() {
|
||
return {
|
||
title: '分享标题',
|
||
path: '/pages/index/index',
|
||
imageUrl: '/static/share.png'
|
||
}
|
||
}
|
||
// #endif
|
||
|
||
// H5分享
|
||
// #ifdef H5
|
||
const shareToWechat = () => {
|
||
// 调用微信 JS-SDK
|
||
wx.updateAppMessageShareData({
|
||
title: '分享标题',
|
||
desc: '分享描述',
|
||
link: window.location.href,
|
||
imgUrl: 'https://example.com/share.png'
|
||
})
|
||
}
|
||
// #endif
|
||
```
|
||
|
||
### 4.2 支付功能
|
||
```javascript
|
||
// 统一支付接口
|
||
export const pay = (orderInfo) => {
|
||
return new Promise((resolve, reject) => {
|
||
// #ifdef MP-WEIXIN
|
||
wx.requestPayment({
|
||
timeStamp: orderInfo.timeStamp,
|
||
nonceStr: orderInfo.nonceStr,
|
||
package: orderInfo.package,
|
||
signType: 'MD5',
|
||
paySign: orderInfo.paySign,
|
||
success: resolve,
|
||
fail: reject
|
||
})
|
||
// #endif
|
||
|
||
// #ifdef H5
|
||
// H5微信支付
|
||
WeixinJSBridge.invoke('getBrandWCPayRequest', orderInfo, (res) => {
|
||
if (res.err_msg === 'get_brand_wcpay_request:ok') {
|
||
resolve(res)
|
||
} else {
|
||
reject(res)
|
||
}
|
||
})
|
||
// #endif
|
||
|
||
// #ifdef APP-PLUS
|
||
// App支付
|
||
plus.payment.request('wxpay', orderInfo, resolve, reject)
|
||
// #endif
|
||
})
|
||
}
|
||
```
|
||
|
||
### 4.3 定位功能
|
||
```javascript
|
||
// 获取位置
|
||
export const getLocation = () => {
|
||
return new Promise((resolve, reject) => {
|
||
uni.getLocation({
|
||
type: 'gcj02',
|
||
success: (res) => {
|
||
resolve({
|
||
latitude: res.latitude,
|
||
longitude: res.longitude
|
||
})
|
||
},
|
||
fail: (err) => {
|
||
// #ifdef H5
|
||
// H5使用浏览器定位API
|
||
if (navigator.geolocation) {
|
||
navigator.geolocation.getCurrentPosition(
|
||
(position) => {
|
||
resolve({
|
||
latitude: position.coords.latitude,
|
||
longitude: position.coords.longitude
|
||
})
|
||
},
|
||
reject
|
||
)
|
||
} else {
|
||
reject(new Error('浏览器不支持定位'))
|
||
}
|
||
// #endif
|
||
|
||
// #ifndef H5
|
||
reject(err)
|
||
// #endif
|
||
}
|
||
})
|
||
})
|
||
}
|
||
```
|
||
|
||
## 五、性能适配
|
||
|
||
### 5.1 小程序分包
|
||
```json
|
||
{
|
||
"pages": [
|
||
"pages/index/index",
|
||
"pages/user/index"
|
||
],
|
||
"subPackages": [
|
||
{
|
||
"root": "subPackages/order",
|
||
"pages": [
|
||
"list/index",
|
||
"detail/index"
|
||
]
|
||
},
|
||
{
|
||
"root": "subPackages/product",
|
||
"pages": [
|
||
"list/index",
|
||
"detail/index"
|
||
]
|
||
}
|
||
],
|
||
"preloadRule": {
|
||
"pages/index/index": {
|
||
"network": "all",
|
||
"packages": ["subPackages/order"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.2 图片懒加载
|
||
```vue
|
||
<template>
|
||
<!-- 小程序端使用 lazy-load -->
|
||
<image
|
||
:src="imageSrc"
|
||
lazy-load
|
||
mode="aspectFill"
|
||
/>
|
||
|
||
<!-- H5端使用 Intersection Observer -->
|
||
<img
|
||
v-lazy="imageSrc"
|
||
alt="图片"
|
||
/>
|
||
</template>
|
||
```
|
||
|
||
### 5.3 长列表优化
|
||
```vue
|
||
<template>
|
||
<!-- 使用虚拟列表 -->
|
||
<recycle-list :list="dataList">
|
||
<template v-slot="{ item }">
|
||
<view class="list-item">
|
||
{{ item.title }}
|
||
</view>
|
||
</template>
|
||
</recycle-list>
|
||
</template>
|
||
```
|
||
|
||
## 六、兼容性处理
|
||
|
||
### 6.1 API 兼容性检查
|
||
```javascript
|
||
// 检查API是否支持
|
||
if (uni.canIUse('getSystemInfoSync')) {
|
||
const systemInfo = uni.getSystemInfoSync()
|
||
}
|
||
|
||
// 版本号比较
|
||
const compareVersion = (v1, v2) => {
|
||
const arr1 = v1.split('.')
|
||
const arr2 = v2.split('.')
|
||
const len = Math.max(arr1.length, arr2.length)
|
||
|
||
for (let i = 0; i < len; i++) {
|
||
const num1 = parseInt(arr1[i] || 0)
|
||
const num2 = parseInt(arr2[i] || 0)
|
||
|
||
if (num1 > num2) return 1
|
||
if (num1 < num2) return -1
|
||
}
|
||
|
||
return 0
|
||
}
|
||
|
||
// 使用示例
|
||
const systemInfo = uni.getSystemInfoSync()
|
||
if (compareVersion(systemInfo.SDKVersion, '2.10.0') >= 0) {
|
||
// 支持新API
|
||
}
|
||
```
|
||
|
||
### 6.2 样式兼容性
|
||
```scss
|
||
// 使用 CSS 变量实现主题切换
|
||
:root {
|
||
--primary-color: #1890ff;
|
||
--text-color: #333;
|
||
}
|
||
|
||
.button {
|
||
background-color: var(--primary-color);
|
||
color: var(--text-color);
|
||
}
|
||
|
||
// 使用 autoprefixer 自动添加前缀
|
||
.flex-container {
|
||
display: flex;
|
||
// 自动添加 -webkit-flex
|
||
}
|
||
```
|
||
|
||
## 七、调试技巧
|
||
|
||
### 7.1 多端调试
|
||
- **微信小程序**:使用微信开发者工具
|
||
- **H5**:使用浏览器开发者工具
|
||
- **App**:使用 HBuilderX 真机调试
|
||
|
||
### 7.2 日志输出
|
||
```javascript
|
||
// 开发环境输出日志
|
||
if (process.env.NODE_ENV === 'development') {
|
||
console.log('调试信息:', data)
|
||
}
|
||
|
||
// 使用条件编译输出平台信息
|
||
// #ifdef MP-WEIXIN
|
||
console.log('当前平台:微信小程序')
|
||
// #endif
|
||
```
|
||
|
||
### 7.3 错误监控
|
||
```javascript
|
||
// 全局错误捕获
|
||
uni.onError((error) => {
|
||
console.error('全局错误:', error)
|
||
// 上报到错误监控平台
|
||
})
|
||
|
||
// 网络错误监控
|
||
uni.onNetworkStatusChange((res) => {
|
||
if (!res.isConnected) {
|
||
uni.showToast({
|
||
title: '网络已断开',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
})
|
||
```
|