本文主要是介绍移动端开发踩坑记录(持续记录中···),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这里记录一下近期开发移动端时遇到的一些坑,以前没这个意识,现在突然想总结一下!
一、微信页面或者内嵌h5页面,上传图片时候,页面刷新闪退或者webview直接刷新,最终导致页面填充数据丢失问题
现象:
1、在上传组件中(哪怕是最简单的),当选择好本地图片或者调取系统摄像头拍好照片后,网页被刷新。这一问题无法100%重现,但概率不低,在如红米等低端安卓机上发生的概率会比较高。
2、与用系统自带的浏览器打开的情况相比,在微信中打开的情况下该问题出现的更为频繁。
3、与选择本地图片上传相比,调取系统摄像头拍照上传的情况下该问题出现的更为频繁。
原因:
1、当在Android的浏览器上调用文件上传功能时,Android系统将把当前进程从“浏览器”切换到“文件选择器”(根据不同的用户选择,这一新的进程可能是图片选择器,也可能是系统摄像头,或者其它别的),此时浏览器进程将变为后台进程。
2、由于Android操作系统的设计缺陷,此时浏览器进程的留存优先级(不被系统kill掉的优先级)与所有其它的后台进程是一样的,因此如果操作系统认为内存不足需要进行清理,此时浏览器进程将不会得到任何保护 – 很不幸,因为浏览器占用内存一般都比较大,所以这次kill操作很容易kill掉浏览器进程。
3、当用户选择好图片返回浏览器时,浏览器进程已经不复存在了,按照Android的机制,浏览器进程将进行恢复,试图恢复到kill之前的状态 – 很不幸,恢复到什么状态是由浏览器来决定的,而浏览器不可能100%恢复到选择文件之前的那个状态。因此最终用户所看到的现象就是:选择文件完成后,浏览器刷新了一下,而刷新到什么状态由浏览器决定。
4、对于这一问题,Android技术团队的态度是回避(将bug标记为obsolete) – 可能是因为此bug涉及了OS底层进程切换的机制,修复起来风险太高;而随着手机设备内存容量的增大,这一问题所发生的概率会越来越小。
5、理解了问题的原因,对问题的一些表征也就有了合理的解释:低端安卓机的内存更小,因此发生问题的频率更高;而与选择本地图片相比,调取系统摄像头所耗的内存更大(当拍摄有大量文字的图片时尤其如此),因此调取摄像头时问题发生的更加频繁;当用户已经开了很多别的App时,问题发生的概率更大。
解决办法:
目前感觉也没什么好的解决办法,前端针对此问题,只能尽可能的去优化用户体验了;
建议做法是,监控用户上传图片的操作,在上传图片之前,对表单数据进行缓存。这样可以针对上传之后的表现去做相应的处理;
如果上传的时候,页面闪退了或者webview刷新了,再次进入此页面的时候,判断缓存存在,则弹出用户内存太小,请清除内存再操作上传图片等友好提示,然后回填数据(将上传图片改为不必填)后,将缓存清除;
如果上传的时候,没有出现闪退刷新情况,则在上传图片完成之后,清除缓存即可;
二、微信、企微内嵌h5登录授权重定向之后,退出应用需要后撤两次问题
现象以及原因:
1、这里要提一下h5微信登录授权的流程:
(1)微信或者企微跳转到h5线上地址,h5空白授权页或者路由守卫中判断当前url中是否含有code;
(2)code存在,则通过code向后台获取token实现登录;code不存在,则拼接微信授权地址并通过location.href进行跳转
location.href = https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
(3)location.href跳转之后,微信授权成功后,会拼接code跳回h5,h5拿到code通过code获取token登录成功;
2、登录成功之后,由于授权的这一系列重定向操作,使得当前路由历史栈中多了一个微信授权地址的路由,导致在h5应用中,点击后退按钮或者手势退出时,会回到微信授权地址,出现无限重复授权的情况,需要操作两次后退才能退出当前h5应用
不使用location.replace的原因:
刚开始是考虑使用location.replace()来替代location.href跳转,后面发现怎么调试都没有效果,根本原因是因为location.replace在微信浏览器中不生效,而且ios,安卓,微信开发者工具里使用location.replace的表现不一致
ios真机:location.replace完全不起作用,效果等同于location.href , 页面栈中会存在一个无code的授权页,有code的授权页
安卓真机与微信开发者工具:页面栈中会存在一个无code的授权页
从location.replace产生的页面栈其实也可以看出来,解决这个问题的关键在于,在知道用户执行了返回操作时,根据页面栈的数量,执行js方面的页面后撤多次并关闭应用即可。
这里就涉及到了如何去知道用户在首页或者说第一个子页面中,执行了返回操作
若是在react项目中,没有路由守卫,那肯定是有一个空白授权页存在的,即微信授权登录逻辑所在的页面。
若是在vue项目中,一般都是在路由守卫中拦截并执行微信授权登录逻辑的。
这里延申一个知识点,那就是window.history.pushState(state, title, url);,这个方法可以在不刷新页面的情况下,直接更改页面的url,然后可以通过window.addEventListener(‘popstate’, handleGoBack, false)事件去监控页面url的回退,所以可以用这个办法来监控页面的回退,但是这个方法也存在一个缺陷,那就是在安卓端微信浏览器中,如果页面无交互,则默认不会触发onPopstate事件,在ios中则无影响······
const useReturnApp = () => {let timer = setTimeout(() => {document.addEventListener('WeixinJSBridgeReady',() => {if (window.WeixinJSBridge) window.WeixinJSBridge.call('closeWindow')},false,)if (window.WeixinJSBridge) window.WeixinJSBridge.call('closeWindow')clearTimeout(timer)timer = null}, 100)const router = useRouter()if (ios) {router.go(-2)} else {router.go(-1)}
}// vue3中自定义hook,使用pushState结合popstate事件监控页面操作退出
// 不过popstate在安卓端微信浏览器中,如果页面无交互,则默认不会触发
export const useFixExitApp = (props) => {const { onGoBack, isListen = true } = props || {}const handleGoBack = () => {if (onGoBack && onGoBack instanceof Function) {onGoBack()} else {useReturnApp()}}const AddListenPopstate = () => {const flag =isListen && isListen instanceof Function ? !!isListen() : !!isListenif (window.history && window.history.pushState && flag) {// 该方法仅可解决安卓机型下x5内核webview浏览器中的popstate默认不触发的bug// https://developers.weixin.qq.com/community/develop/doc/000a2a57968cc0bc9d7aa0b6b5b800const { type } = useGetAppType()if (type !== 'IOS') {try {window.tbs_bridge.nativeExec('network', 'type', 0, null)} catch (e) {console.error('weixin network:', e)}}window.history.pushState(null, null, document.URL)window.addEventListener('popstate', handleGoBack, false)}}const removeListenPopstate = () => {window.removeEventListener('popstate', handleGoBack, false)}onMounted(() => {AddListenPopstate()})onUnmounted(() => {removeListenPopstate()})
}// 在有code的页面调用登录接口成功之后,我们向sessionStorage中存一个临时值再跳转到子页面,然后修改空白授权页的初始化逻辑即可:
// 演示代码
const code = getSearchParams("code") // 获取url search中的code
const signValue = sessionStorage.getItem(signValueKey)
if (signValue) {// 进行返回逻辑sessionStorage.removeItem(‘key’)useReturnApp()
} else {if (!code) {// 没有code,跳转微信授权oauth地址location.replace("微信授权地址")}else {// 有code,调用登录接口$.ajax(login_api, {data: {code}}).then(res => {// 储存临时值sessionStorage.setItem(‘key’, code)// 跳转到子页面location.replace(子页面地址)])}
}
三、移动端h5页面布局,ios中布局存在差异问题,归根结底还是底部安全距离和position:fixed问题
现象以及解决办法:
1、在移动端h5项目中,ios刘海屏系列手机,会出现一个包含在100vh中的底部遮挡区域,这块区域则是所谓的安全距离
// 比如经常用到的底部安全距离样式设置
height: calc(100vh - constant(safe-area-inset-bottom));
height: calc(100vh - env(safe-area-inset-bottom));
// or
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
2、在移动端h5项目中,ios不支持fixed定位,会导致fixed的元素位置错乱,这时候结合业务实际,可以使用postion:absolute来替代
四、移动端h5在ios特有的橡皮筋效果,有时候会导致底部按钮,在页面滚动到底部的时候,一直往上拉,出现橡皮筋效果,导致按钮被遮挡等体验不好的问题
现象以及解决办法:
1、现象如上,这里推荐使用inobounce插件,禁用橡皮筋效果来解决此问题
// npm install inobounce -s
import inobounce from 'inobounce'onMounted(() => {// 开启禁用if (appType === 'IOS') inobounce.enable()
})onUnmounted(() => {// 关闭禁用if (appType === 'IOS') inobounce.disable()
})
五、移动端h5内容比较多,产生滚动条的时候,在textarea元素上滚动,会因为textarea自带的滚动事件与页面的滚动事件重叠,导致滚动失效,造成卡顿现象的问题
现象以及解决办法:
1、现象如上,这里只需要在textarea上设置overflow: hidden即可解决此问题;
这篇关于移动端开发踩坑记录(持续记录中···)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!