1. iOS 审核域名限制问题
问题描述
iOS 审核人员位于国外,App 内访问的 API 域名需要能在外网访问,否则可能导致审核失败。
解决方案
确保所有 API 域名都能在外网访问,或提供审核专用的 API 环境。
代码示例
javascript
// 配置 API 域名时考虑审核需求
const isProduction = process.env.NODE_ENV === 'production';
const isReview = process.env.VUE_APP_REVIEW === 'true';
// 审核环境使用可外网访问的域名
const baseUrl = isReview ? 'https://review-api.example.com' :
isProduction ? 'https://api.example.com' :
'http://dev-api.example.com';
export default {
baseUrl
};
2. iOS scroll-view 内 fixed 定位弹框被遮挡
问题描述
在 iOS 设备上,scroll-view 组件内部的 fixed 定位弹框内容会被遮挡,无法正常显示。
解决方案
将弹框移出 scroll-view 组件,或使用其他定位方式。
代码示例
html
<!-- 错误示例:fixed 定位弹框在 scroll-view 内部 -->
<scroll-view scroll-y="true" style="height: 100vh;">
<view>scroll-view 内容</view>
<view class="popup" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);">
弹框内容
</view>
</scroll-view>
<!-- 正确示例:fixed 定位弹框在 scroll-view 外部 -->
<scroll-view scroll-y="true" style="height: 100vh;">
<view>scroll-view 内容</view>
</scroll-view>
<view class="popup" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);">
弹框内容
</view>
3. iOS plus.io.chooseFile() 返回路径处理问题
问题描述
在 iOS 上,plus.io.chooseFile().then(res => {}) 返回的 res.files[0] 已经是可直接读取的绝对路径,不能再用 plus.io.convertLocalFileSystemURL 去转换,否则会拼接出不合法的路径,导致 uni.uploadFile 读取不到文件体。
解决方案
根据平台判断是否需要转换路径。
代码示例
javascript
plus.io.chooseFile({
title: '选择文件',
filter: {
mimeTypes: ['image/*']
}
}).then(res => {
let filePath = res.files[0];
// 根据平台判断是否需要转换路径
if (uni.getSystemInfoSync().platform !== 'ios') {
// 非 iOS 平台需要转换路径
filePath = plus.io.convertLocalFileSystemURL(filePath);
}
// 使用转换后的路径进行文件上传
uni.uploadFile({
url: 'https://api.example.com/upload',
filePath: filePath,
name: 'file',
success: (uploadRes) => {
console.log('上传成功', uploadRes);
},
fail: (err) => {
console.error('上传失败', err);
}
});
});
4. HarmonyOS picker 组件异步数据渲染问题
问题描述
在 HarmonyOS 设备上,内置组件 picker 的 range 使用异步数据时,需要使用 v-if 或 v-show 控制在获取到数据后再渲染 picker 组件,否则 picker 组件弹框不会显示。
解决方案
使用 v-if 或 v-show 控制 picker 组件的渲染时机,确保在数据加载完成后再渲染。
代码示例
vue
<template>
<view>
<button @click="showPicker = true">显示选择器</button>
<!-- 使用 v-if 控制 picker 组件渲染时机 -->
<picker
v-if="pickerData.length > 0"
v-model="selectedIndex"
:range="pickerData"
@change="onPickerChange"
v-show="showPicker"
></picker>
</view>
</template>
<script>
export default {
data() {
return {
pickerData: [],
selectedIndex: 0,
showPicker: false
};
},
onLoad() {
// 异步获取数据
this.loadPickerData();
},
methods: {
loadPickerData() {
// 模拟异步请求
setTimeout(() => {
this.pickerData = ['选项1', '选项2', '选项3', '选项4', '选项5'];
}, 1000);
},
onPickerChange(e) {
console.log('选择了', this.pickerData[e.detail.value]);
this.showPicker = false;
}
}
};
</script>
5. HarmonyOS uni.getLocation 坐标系转换问题
问题描述
在鸿蒙设备上使用 uni.getLocation 获取定位是通过鸿蒙系统定位,获取到的定位坐标系是国际坐标系(wgs84),需要进行坐标系转换得到国测局坐标系(gcj02)才能在高德地图 API 上使用。
解决方案
使用坐标系转换算法将 wgs84 转换为 gcj02。
代码示例
javascript
// 坐标系转换算法(wgs84 转 gcj02)
function wgs84togcj02(lng, lat) {
const pi = 3.1415926535897932384626;
const a = 6378245.0;
const ee = 0.00669342162296594323;
if (out_of_china(lng, lat)) {
return [lng, lat];
} else {
let dlat = transformlat(lng - 105.0, lat - 35.0);
let dlng = transformlng(lng - 105.0, lat - 35.0);
const radlat = lat / 180.0 * pi;
let magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
const sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * pi);
const mglat = lat + dlat;
const mglng = lng + dlng;
return [mglng, mglat];
}
}
function transformlat(lng, lat) {
const pi = 3.1415926535897932384626;
let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * pi) + 40.0 * Math.sin(lat / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * pi) + 320 * Math.sin(lat * pi / 30.0)) * 2.0 / 3.0;
return ret;
}
function transformlng(lng, lat) {
const pi = 3.1415926535897932384626;
let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * pi) + 20.0 * Math.sin(2.0 * lng * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lng * pi) + 40.0 * Math.sin(lng / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lng / 12.0 * pi) + 300.0 * Math.sin(lng / 30.0 * pi)) * 2.0 / 3.0;
return ret;
}
function out_of_china(lng, lat) {
return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
}
// 使用示例
uni.getLocation({
type: 'gcj02',
success: (res) => {
let lng = res.longitude;
let lat = res.latitude;
// 如果是鸿蒙设备,需要进行坐标系转换
const systemInfo = uni.getSystemInfoSync();
if (systemInfo.platform === 'harmony') {
// 鸿蒙设备获取的是 wgs84 坐标系,需要转换为 gcj02
const gcj02 = wgs84togcj02(lng, lat);
lng = gcj02[0];
lat = gcj02[1];
}
console.log('转换后的坐标', lng, lat);
// 使用转换后的坐标调用高德地图 API
}
});
6. HarmonyOS uni.chooseImage 不兼容 5.0
问题描述
uni.chooseImage 不兼容 HarmonyOS 5.0,在该版本上无法正常使用。
解决方案
使用 plus.io.chooseFile 替代 uni.chooseImage,或使用其他兼容方案。
代码示例
javascript
// 兼容 HarmonyOS 5.0 的图片选择方法
function chooseImage(options) {
const systemInfo = uni.getSystemInfoSync();
// 如果是 HarmonyOS 5.0 或以上版本,使用 plus.io.chooseFile
if (systemInfo.platform === 'harmony' && parseFloat(systemInfo.osVersion) >= 5.0) {
return new Promise((resolve, reject) => {
plus.io.chooseFile({
title: '选择图片',
filter: {
mimeTypes: ['image/*']
},
multiple: options.count > 1,
maxSelect: options.count
}).then(res => {
const tempFilePaths = res.files.map(file => {
return plus.io.convertLocalFileSystemURL(file);
});
resolve({ tempFilePaths });
}).catch(err => {
reject(err);
});
});
} else {
// 其他平台使用 uni.chooseImage
return new Promise((resolve, reject) => {
uni.chooseImage({
count: options.count,
success: resolve,
fail: reject
});
});
}
}
// 使用示例
chooseImage({ count: 1 }).then(res => {
console.log('选择的图片', res.tempFilePaths);
}).catch(err => {
console.error('选择图片失败', err);
});
7. 高频同步 I/O 操作导致卡顿/闪退
问题描述
在列表渲染期间,高频地在主线程上执行同步 I/O 操作,会严重阻塞 UI 渲染,导致应用卡顿、无响应(ANR),而在一些对主线程管控更严格的系统(如 HarmonyOS Next)上,这很容易被判定为异常并导致闪退。
解决方案
将同步 I/O 操作改为异步,或减少调用次数,或使用缓存。
代码示例
vue
<template>
<view>
<list>
<list-item v-for="item in listData" :key="item.id">
<text>{{ item.title }}</text>
<text>{{ item.status }}</text>
</list-item>
</list>
</view>
</template>
<script>
export default {
data() {
return {
listData: []
};
},
onLoad() {
this.loadListData();
},
methods: {
// 错误示例:列表渲染时多次调用 uni.getStorageSync
loadListData() {
// 模拟获取列表数据
const list = [];
for (let i = 0; i < 100; i++) {
list.push({ id: i, title: '项目' + i });
}
// 错误:在循环中多次调用同步 I/O 操作
list.forEach(item => {
// 这会导致严重的性能问题
const status = uni.getStorageSync(`item_status_${item.id}`);
item.status = status || '默认状态';
});
this.listData = list;
}
}
};
</script>
<script>
// 正确示例:使用异步方法或减少调用次数
export default {
data() {
return {
listData: []
};
},
onLoad() {
this.loadListData();
},
methods: {
async loadListData() {
// 模拟获取列表数据
const list = [];
for (let i = 0; i < 100; i++) {
list.push({ id: i, title: '项目' + i });
}
// 正确:一次性获取所有需要的数据
const allStatus = await this.getAllItemStatus(list);
// 合并数据
list.forEach(item => {
item.status = allStatus[`item_status_${item.id}`] || '默认状态';
});
this.listData = list;
},
// 一次性获取所有需要的数据
getAllItemStatus(list) {
return new Promise((resolve) => {
// 在实际应用中,可以使用异步方法批量获取数据
// 这里模拟一次性获取所有状态
const result = {};
// 模拟异步操作
setTimeout(() => {
list.forEach(item => {
result[`item_status_${item.id}`] = '状态' + item.id;
});
resolve(result);
}, 100);
});
}
}
};
</script>
8. HarmonyOS 页面多次调用 uni.getStorageSync 性能问题
问题描述
页面中多次调用 uni.getStorageSync 类似的同步方法,在鸿蒙设备上非常消耗性能,会造成页面加载渲染卡顿。
解决方案
将多次调用改为单次调用,或使用异步方法,或使用缓存。
代码示例
javascript
// 错误示例:多次调用 uni.getStorageSync
function loadUserData() {
const userId = uni.getStorageSync('user_id');
const userName = uni.getStorageSync('user_name');
const userAvatar = uni.getStorageSync('user_avatar');
const userGender = uni.getStorageSync('user_gender');
const userAge = uni.getStorageSync('user_age');
return {
userId,
userName,
userAvatar,
userGender,
userAge
};
}
// 正确示例:使用异步方法一次性获取所有数据
async function loadUserData() {
// 方式一:使用异步方法
const userInfo = await uni.getStorage({ key: 'user_info' });
// 方式二:如果数据分散存储,使用 Promise.all 并行获取
/*
const [userIdRes, userNameRes, userAvatarRes, userGenderRes, userAgeRes] = await Promise.all([
uni.getStorage({ key: 'user_id' }),
uni.getStorage({ key: 'user_name' }),
uni.getStorage({ key: 'user_avatar' }),
uni.getStorage({ key: 'user_gender' }),
uni.getStorage({ key: 'user_age' })
]);
const userInfo = {
userId: userIdRes.data,
userName: userNameRes.data,
userAvatar: userAvatarRes.data,
userGender: userGenderRes.data,
userAge: userAgeRes.data
};
*/
return userInfo.data;
}
// 正确示例:将分散的数据合并存储
// 存储数据时
uni.setStorageSync('user_info', {
userId: '123',
userName: '张三',
userAvatar: 'https://example.com/avatar.jpg',
userGender: '男',
userAge: 25
});
9. uni. <math xmlns="http://www.w3.org/1998/Math/MathML"> o n 、 u n i . on、uni. </math>on、uni.emit 跨页面通信在 H5 打包后失效
问题描述
uni.$on、uni.$emit 事件,在打包成 H5 页面后不能跨页面通信。
解决方案
使用其他跨页面通信方式,如 URL 参数、localStorage、vuex 等。
代码示例
javascript
// 方案一:使用 URL 参数传递数据
// 页面 A
uni.navigateTo({
url: `/pages/pageB/pageB?data=hello`
});
// 页面 B
onLoad(options) {
console.log(options.data); // hello
}
// 方案二:使用 localStorage 进行跨页面通信
// 页面 A
uni.setStorageSync('pageData', { message: 'hello' });
// 页面 B
onShow() {
const data = uni.getStorageSync('pageData');
console.log(data.message); // hello
}
// 方案三:使用 vuex 进行状态管理
// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
message: ''
},
mutations: {
setMessage(state, message) {
state.message = message;
}
},
actions: {
updateMessage({ commit }, message) {
commit('setMessage', message);
}
},
getters: {
getMessage(state) {
return state.message;
}
}
});
// 页面 A
import store from '@/store';
store.dispatch('updateMessage', 'hello');
// 页面 B
import store from '@/store';
onShow() {
console.log(store.getters.getMessage); // hello
}
// 方案四:使用事件总线(适用于 Vue 2)
// main.js
Vue.prototype.$bus = new Vue();
// 页面 A
this.$bus.$emit('customEvent', { message: 'hello' });
// 页面 B
created() {
this.$bus.$on('customEvent', (data) => {
console.log(data.message); // hello
});
}
总结
以上是 iOS 和 HarmonyOS 兼容开发中的常见问题及解决方案,包括:
- iOS 审核域名限制问题
- iOS scroll-view 内 fixed 定位弹框被遮挡
- iOS plus.io.chooseFile() 返回路径处理问题
- HarmonyOS picker 组件异步数据渲染问题
- HarmonyOS uni.getLocation 坐标系转换问题
- HarmonyOS uni.chooseImage 不兼容 5.0
- 高频同步 I/O 操作导致卡顿/闪退
- HarmonyOS 页面多次调用 uni.getStorageSync 性能问题
- uni. <math xmlns="http://www.w3.org/1998/Math/MathML"> o n 、 u n i . on、uni. </math>on、uni.emit 跨页面通信在 H5 打包后失效
在开发跨平台应用时,需要充分考虑不同平台的特性和限制,编写兼容代码,确保应用在各平台上都能正常运行。