转载自: UniApp 踩坑日记
前言
UniApp 可以实现跨平台的打包和编辑,支持小程序和 App,方便开发的同时也带来了一些系列兼容性的问题,曾和 UniApp 官方的开发人员有过沟通,对于兼容性的问题很多时候官方也没有办法去解决,我们只能通过产品的角度去寻求另外的一种解决方式。
不过对于简单的应用,也不用同时兼容太多的平台,UniApp 也是企业和开发者的选择之一,今天这篇文章主要记录了我在使用 UniApp 的过程中遇到了一些问题和解决方案。
iOS 底部安全距离
/\*兼容 IOS<11.2\*/
padding-bottom: constant(safe-area-inset-bottom);
/\*兼容 IOS>11.2\*/
padding-bottom: env(safe-area-inset-bottom);
App 状态栏距离
<view class="page">
<view class="status\_bar"></view>
</view>
<style scoped>
.status\_bar {
height: var(--status-bar-height);
width: 100%;
}
</style>
textarea 组件的层级问题
- 使用原生组件
cover-view
覆盖掉样式 - 动态渲染原生组件
textarea 属于原生组件,层级高于其他组件,在小程序端处出现层级穿透的问题,对此官方提供了 cover-view 原生组件,不过非常鸡肋。
在这里我们更加推荐使用 动态渲染组件的形式。
<template>
<view>
<textarea placeholder="这是一个多行文本域" v-if="showPopup" />
<uni-popup ref="popup" @change="handleChange">
这是一个弹出框
</uni-popup>
</view>
</template>
<script>
export default {
data() {
return {
showPopup: false
}
},
onLoad() {
this.$nextTick(() => {
this.$refs.popup.open('top')
})
},
methods: {
handleChange(e) {
this.showPopup = e.show
}
}
}
</script>
App 实现热更新
App 热更新是 App 端常用的升级手段,但是因为 iOS 不允许 App 热更新上架应用市场,这里值介绍了 Android 端的使用。
同时需要注意的是如果热更新的代码包需要与 uniapp 云服务打包的版本号相同,否则会出现不兼容的问题。
var dtask = plus.downloader.createDownload(this.upgradeUrl, {}, (d, status) => {
if(status === 200) {
plus.runtime.install(d.filename, {}, () => {
plus.nativeUI.closeWaiting();
console.log("安装wgt文件成功!", this.isforce);
// 重启App
plus.runtime.restart();
}, (e) => {
console.log("安装wgt文件失败["+e.code+"]:"+e.message);
plus.nativeUI.alert("安装wgt文件失败["+e.code+"]:"+e.message);
});
} else {
console.log('网络异常');
plus.downloader.clear(dtask);
}
})
// 监听下载进度
dtask.addEventListener('statechanged', (task) => {
let { totalSize, downloadedSize } = task;
console.log('下载进度', Math.floor(downloadedSize / totalSize * 100) || 0)
})
App 被第三方调用
打开 manifest.json => App常用其它配置 => UrlSchemes
; 设置 App 的 UrlSchemes
- H5 打开 App
<a href="test://dl">打开App<a>
- App 监听打开的参数
onLaunch: () => {
// URLSchemes
this.checkSchemes();
// 重点是以下: 一定要监听后台恢复 !一定要
plus.globalEvent.addEventListener("newintent", () => {
this.checkSchemes();
});
}
methods: {
// APP 检查 URLSchemes
checkSchemes() {
let args = plus.runtime.arguments;
console.log('获取到的参数', args)
}
}
App 打开小程序
plus.share.getServices(res => {
var sweixin = null;
for (var i = 0; i < res.length; i++) {
var t = res[i];
if (t.id == 'weixin') {
sweixin = t;
}
}
if (sweixin) {
sweixin.launchMiniProgram({
id: 'xxxx', // 小程序原始id
path: 'xxxxx', // 小程序路径
type: 0 // 0-正式版; 1-测试版; 2-体验版。
})
}
})
跨组件通信
// componentA
// 添加监听关闭 splash 事件
uni.$on("closeSplashscreen", () => {
plus.navigator.closeSplashscreen();
});
// componentB
uni.$emit("closeSplashscreen");
页面跳转通信
// pageA 监听被打开页面的事件
uni.navigateTo({
url: 'xxxx',
events: {
event: () => {
console.log('enent')
}
}
})
// pageB 触发事件
const eventChannel = this.getOpenerEventChannel();
eventChannel.emit && eventChannel.emit('event');
input 最大长度问题
在 iOS input 输入文字时的拼音的长度大于最大的长度无法输入的问题,当你遇见这个问题的时候,恭喜你无法解决,你只能通过修改产品方案,不限制用户输入的长度,在提交表单时在进行校验即可。
下拉刷新时数据重复的问题
这个问题是因为我们在对组件进行 for 循环时的 key 值问题,具体原因可能还是 uniapp 编译机制的问题吧,如果你出现了这个问题,建议通过 uid 来作为 for 循环的 key 值。
<template>
<view>
<view v-for="item in list" :key="item.\_uid"></view>
</view>
</template>
<script>
export default {
data(){
return {
list: []
}
},
onLoad() {
// 模拟请求后的数据
this.list = [a,b,c].map((item) => {
// 设置uid
return { ...item, \_uid: this.guid() }
})
},
methods: {
guid(len = 32, firstU = true, radix = null) {
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
const uuid = [];
radix = radix || chars.length;
if (len) {
// 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位
for (let i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() \* radix)];
} else {
let r;
// rfc4122标准要求返回的uuid中,某些位为固定的字符
uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
uuid[14] = "4";
for (let i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | (Math.random() \* 16);
uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
}
}
}
// 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class
if (firstU) {
uuid.shift();
return `u${uuid.join("")}`;
}
return uuid.join("");
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
隐私合规问题
如果你的 App 需要上架应用市场,就必须的注重 App 申请权限的问题。对此我们需要注意一下几点:
- 使用原生隐私政策提示框
打开项目的 manifest.json 文件,切换到“App 启动界面配置”,在“Android 启动界面样式”中勾选“使用原生隐私政策提示框”
{
"version" : "1",
"prompt" : "template",
"title" : "个人信息保护说明",
"message" : "<font color='#000000' size='2'>\t\t\t\t欢迎您使用xxx平台,为帮助您注册使用,浏览推荐,发布投诉,互动交流,我们将手机您的部分必要信息;<br/><br/>\t\t\t\t我们手机的相关信息,未经您的同意,我们不会从第三方获取、共享或提供您的信息;<br/><br/>\t\t\t\t本协议有您与平台的经营者共同缔结,本协议具有合同效力。<br/><br/>\t\t\t\t请查看<a href=\"https://xxxx\">《用户服务协议》</a>及<a href=\"https://xxxx\">《隐私政策》</a></font>",
"buttonAccept" : "同意并接受",
"buttonRefuse" : "暂不同意",
"styles" : {
"borderRadius" : "5px",
"buttonAccept" : {
"color" : "#2cbe9a"
}
}
}
- 详细的描述引用了什么 SDK 以及作用和时机
友盟统计(com.umeng)使用场景:统计 APP 下载量;使用目的:用于数据统计分析;涉及个人信息:设备信息(IMEI、ANDROID_ID、DEVICE_ID、IMSI)、应用已安装列表、网络信息;收集规则:APP 前台运行时获取 SIM 序列号、SD 卡数据和设备序列号用于数据统计分析;个人信息处理规则: http://xxxx
- 关闭 App 在首次启动时申请存储、照片和拨打电话的权限
// 在"app-plus" -> "distribute" -> "android" 节点下添加 permissionExternalStorage 节点
// 禁止App在启动时存储、照片的权限
"permissionExternalStorage": {
"request": "always",
"prompt": "应用保存运行状态等信息,需要获取读写手机存储(系统提示为访问设备上的照片、媒体内容和文件)权限,请允许。"
}
// 禁止App在启动时获取拨打电话的权限
"permissionPhoneState" : {
"request" : "none",
"prompt" : "为保证您正常、安全地使用,需要获取设备识别码(部分手机提示为获取手机号码)使用权限,请允许。"
}
键盘遮挡样式的问题
// pages.json 设置键盘的弹出模式
"app-plus":{
"softinputMode": "adjustResize"
}
打包体积过大
在 Uniapp 中 App 云打包有体积的限制,而在小程序中也存在体积大小的限制。
- 小程序分包加载
"subPackages": [{
"root": "packageA",
"pages": [{
"path": "pages/index",
}]
}, {
"root": "packageB",
"pages": [{
"path": "pages/index",
}]
}
]
- 静态资源 CDN
占用我们资源包大小的莫过于图片了,我们可以将图片上 CDN 存储减少打包体积大小。
当然在开发过程中为了方便,我们可以在本地建立服务,通过 ip 来访问图片。
<template>
<image :src="getImageUrl('/public/logo.png')" mode=""></image>
</template>
<script>
export default {
methods: {
getImageUrl(path) {
return process.env.NODE\_ENV === 'production' ? `http://CDN地址 ${path}` : `http://本地服务地址 ${path}`
}
}
}
</script>
总结
如果你决定了使用 UniApp 做为当前项目的框架并且适配多平台,那么需要开始处理让人头疼的兼容性问题。
很多时候我们依赖一些第三方的云插件,云插件的更新或者云插件的 Bug 可能会影响你的 App 打包失败。
很多时候这些兼容性的问题不是必现的,出现的可能有很多的因素,比如 UniApp 更新了版本。
最后
感谢你的阅读~
如果你有任何的疑问欢迎您在后台私信,我们一同探讨学习!
如果觉得这篇文章对你有所帮助,点赞、在看是最大的支持!