Files
dabai_web_f/docs/多端适配方案.md
2026-03-17 14:52:07 +08:00

7.8 KiB
Raw Permalink Blame History

多端适配方案

一、平台差异处理

1.1 条件编译

使用 UniApp 的条件编译处理不同平台的差异:

// #ifdef MP-WEIXIN
// 微信小程序特有代码
wx.login()
// #endif

// #ifdef H5
// H5特有代码
window.location.href = '/login'
// #endif

// #ifdef APP-PLUS
// App特有代码
plus.runtime.restart()
// #endif

1.2 平台判断工具

使用封装的平台判断工具:

import { getPlatform, isMiniProgram, isH5, isApp } from '@/utils/platform'

if (isMiniProgram()) {
  // 小程序端逻辑
} else if (isH5()) {
  // H5端逻辑
} else if (isApp()) {
  // App端逻辑
}

二、API 适配

2.1 统一 API 封装

对不同平台的 API 进行统一封装:

// 跨端导航
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 第三方登录适配

不同平台的第三方登录实现:

// 微信登录
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 尺寸单位适配

  • 小程序端:使用 rpx750rpx = 屏幕宽度)
  • H5端:使用 remvw
  • App端:使用 rpxupx
// 统一使用 rpxUniApp 会自动转换
.container {
  width: 750rpx;
  padding: 30rpx;
  font-size: 28rpx;
}

3.2 安全区域适配

处理刘海屏、底部安全区域:

.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 状态栏高度适配

// 获取状态栏高度
const systemInfo = uni.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight

// 在样式中使用
const navBarHeight = statusBarHeight + 44 // 44为导航栏高度

四、功能适配

4.1 分享功能

// 小程序分享
// #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 支付功能

// 统一支付接口
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 定位功能

// 获取位置
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 小程序分包

{
  "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 图片懒加载

<template>
  <!-- 小程序端使用 lazy-load -->
  <image 
    :src="imageSrc" 
    lazy-load 
    mode="aspectFill"
  />
  
  <!-- H5端使用 Intersection Observer -->
  <img 
    v-lazy="imageSrc" 
    alt="图片"
  />
</template>

5.3 长列表优化

<template>
  <!-- 使用虚拟列表 -->
  <recycle-list :list="dataList">
    <template v-slot="{ item }">
      <view class="list-item">
        {{ item.title }}
      </view>
    </template>
  </recycle-list>
</template>

六、兼容性处理

6.1 API 兼容性检查

// 检查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 样式兼容性

// 使用 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 日志输出

// 开发环境输出日志
if (process.env.NODE_ENV === 'development') {
  console.log('调试信息:', data)
}

// 使用条件编译输出平台信息
// #ifdef MP-WEIXIN
console.log('当前平台:微信小程序')
// #endif

7.3 错误监控

// 全局错误捕获
uni.onError((error) => {
  console.error('全局错误:', error)
  // 上报到错误监控平台
})

// 网络错误监控
uni.onNetworkStatusChange((res) => {
  if (!res.isConnected) {
    uni.showToast({
      title: '网络已断开',
      icon: 'none'
    })
  }
})