让网页更像原生应用

2026年05月15日

网页不像原生应用,很多时候不是功能差异,而是交互细节里的默认网页行为太明显。


1.使用抽屉,而不是全屏页面

移动端更自然的方式是从底部拉起一个抽屉,让用户完成操作后回到原来的上下文。


2.禁止文字被选中

原生应用里的 UI 几乎不会出现蓝色选区,而网页默认允许拖动选中文字,滑动时很容易误触选中。

user-select: none;

3.禁用缩放手势

双指缩放和双击放大会打破应用的固定布局,也会让界面立刻回到网页的感觉。

<meta
    name="viewport"
    content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>

4.不需要 cursor: pointer

手指指针更像桌面网页的交互提示,原生应用通常不需要保留它。输入框里的文本指针除外。


5.确保输入框可见

问题在于 iOS PWA 第一次聚焦输入框时,键盘已经弹出,界面却不一定会跟着上移,输入框容易被挡住。

解决方式是监听 visualViewport,用真实可视区域算出输入框被遮住的距离,只滚动缺口那一段,退出输入时交给浏览器恢复。

// 给输入框和键盘之间留出一点呼吸空间。
const margin = 16
 
function keepInputVisible() {
    const viewport = window.visualViewport
    const input = document.activeElement
 
    // visualViewport 不存在,或当前焦点不是可定位元素时,不主动处理。
    if (!viewport || !(input instanceof HTMLElement)) {
        return
    }
 
    const rect = input.getBoundingClientRect()
 
    // 把输入框位置和可视区域统一换算到页面坐标,避免键盘改变视口后算错。
    const inputTop = rect.top + window.scrollY
    const inputBottom = rect.bottom + window.scrollY
    const visibleTop = viewport.pageTop + margin
    const visibleBottom = viewport.pageTop + viewport.height - margin
 
    // 只补被遮住的距离,不接管浏览器完整的滚动恢复逻辑。
    if (inputBottom > visibleBottom) {
        window.scrollBy(0, inputBottom - visibleBottom)
    } else if (inputTop < visibleTop) {
        window.scrollBy(0, inputTop - visibleTop)
    }
}
 
window.visualViewport?.addEventListener('resize', keepInputVisible)
window.visualViewport?.addEventListener('scroll', keepInputVisible)

最后,感谢你能看到这里。上面这些都是我在把网页项目转成 App 时遇到的细节问题,它们不复杂,却很容易影响使用感。

更重要的部分,还是 UI/UX 本身:信息是否清楚,操作是否顺手,反馈是否可靠。