移动端问题
使用vh
- 在移动端使用vh 单位,主要是因为移动浏览器的地址栏和工具栏会动态隐藏/显示,导致视口高度变化。
底部安全区域问题
- iphoneX以上会出现底部安全区域
- 对于动态高度的计算,需要处理安全区域问题
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);
示例
// 处理安全区域,底部安全区域
const height = computed(() => {
const safeAreaTop = 'env(safe-area-inset-top, 0px)'
const safeAreaBottom = 'env(safe-area-inset-bottom, 0px)'
if (showBottomAction.value) {
return `calc(100vh - ${safeAreaTop} - ${safeAreaBottom} - ${px2rem(180)})`
} else {
return `calc(100vh - ${safeAreaTop} - ${safeAreaBottom})`
}
})
env用法
env(safe-area-inset-top, 10px) 中的第二个参数 0px 是回退值(fallback value)。
-
当浏览器不支持 safe-area-inset-top 环境变量时
-
或者当前设备没有安全区域(比如非刘海屏设备)时
-
就会使用回退值 10px
window.innerHeight问题
- 在内嵌的app中,是全屏的,需要加上app的状态栏的高度
移动端 Webview 兼容性 (滑动重复渲染问题)
-
在某些 iOS 设备上,当一个页面本身不可滚动时(比如你的页面是用绝对定位和固定定位布局的),用户的上下滑动操作(尤其是在页面顶部或底部快速滑动)有时会被 Webview 误解。
iOS 的 Webview 可能会把这个滑动操作当成是“下拉刷新”或者“侧滑返回”这样的原生手势。如果你的 App 外壳处理了这个手势并重新加载了页面,就会出现你看到的“渲染了多个组件 的情况,本质上是页面被重复加载或堆叠了。
-
问题根源:在 iOS 上,当页面内容本身不可滚动时,用户的滑动操作(特别是快速的上下滑动)有时会被 Webview 误解为“下拉刷新”或“边缘滑动手势”,从而触发了页面的重新加载或导航,导致你看到的组件重复渲染。
解决
- 外层容器设置
- contain:刹车 + 筑墙。它既阻止了原生的边界效果(如“下拉刷新”),也阻止了滚动事件传递给父元素(滚动链)
- 告诉浏览器:“当这个元素在垂直方向上滚动到尽头时,请刹车,不要把滚动事件传递出去,也不要触发任何原生的边界效果
- 会阻止浏览器因为用户的滑动操作而触发原生的下拉刷新功能,从而解决了你的页面被重复渲染的问题。
- none:只刹车,不筑墙。它只阻止了原生的边界效果,但不会阻止滚动事件传递给父元素
- contain:刹车 + 筑墙。它既阻止了原生的边界效果(如“下拉刷新”),也阻止了滚动事件传递给父元素(滚动链)
overscroll-behavior-y: contain;
- 简单来说,就是用户快速滑动,禁止触发原生的下拉刷新功能,避免刷新页面
移动端粘贴方法问题
- navigator.clipboard.readText()
- 必须是 HTTPS
- Safari(iOS)可能不兼容,有些版本不支持
- 读取剪贴板 = 潜在隐私泄露,因此在移动端不允许页面主动读取用户剪贴板
弹窗遮罩滚动穿透问题
不要只靠
overflow: hidden最稳妥、跨端一致性最好的是:position: fixed锁 body + 记录滚动位置
- 当弹窗显示的时候,设置body为hidden,可解决滚动穿透问题
watch(() => props.visible, (val) => {
if (val) {
document.body.style.overflow = 'hidden'
}
else {
document.body.style.overflow = ''
}
})
onUnmounted(() => {
document.body.style.overflow = ''
})
- 修复滚动穿透问题。
overflow: hidden在某些移动端浏览器(特别是 iOS Safari)上不够可靠,需要使用position: fixed方案
export default function useBodyScroll() {
// 记录滚动位置
let scrollPosition = 0
// 禁用滚动穿透
const disableBodyScroll = () => {
if (!process.client)
return
scrollPosition = window.pageYOffset || document.documentElement.scrollTop
document.body.style.position = 'fixed'
document.body.style.top = `-${scrollPosition}px`
document.body.style.width = '100%'
document.body.style.overflow = 'hidden'
}
// 恢复滚动
const enableBodyScroll = () => {
if (!process.client)
return
document.body.style.position = ''
document.body.style.top = ''
document.body.style.width = ''
document.body.style.overflow = ''
window.scrollTo(0, scrollPosition)
}
return {
disableBodyScroll,
enableBodyScroll,
}
}
overflow: hidden
- 在移动端(尤其 iOS)极不稳定
- 不靠谱,只适合 PC
常见问题:
- iOS Safari 仍然可以滚动(橡皮筋回弹)
- 内部元素滚动时会“带着 body 一起动”
touchmove仍然能触发页面滚动
position: fixed
- 真正锁死 body
- iOS / Android / WebView / 微信 全部生效
- 不受橡皮筋影响
- 是各大 UI 库的最终选择
需要处理的问题
- 页面会“跳回顶部”
- 关闭弹窗需要恢复原滚动位置
语言切换问题
- navigator.languages 返回的是浏览器的语言设置,但在移动端,这个设置默认是跟随手机系统语言的