从设备像素、CSS 像素、DPR 和 viewport 出发,梳理移动端页面适配的核心概念和工程落地方案。

2022-09-15 03:35:27

移动端适配

移动端适配要解决的不是「让所有设备显示得完全一样」,而是让页面在不同屏幕尺寸、分辨率、DPR、横竖屏和安全区域下,都能保持可读、可用、比例合理。

如果只写固定宽度,页面可能会出现这些问题:

  • 小屏设备内容被挤压,文字过小。
  • 大屏设备留白过多,视觉比例失衡。
  • 高清屏图片模糊,边框粗细不一致。
  • 刘海屏、圆角屏、底部手势条遮挡内容。

所以移动端适配通常不是单一技术点,而是一组方案组合:先确定 viewport,再处理布局、尺寸、资源和特殊屏幕细节。

1. 设备像素

设备像素也叫物理像素,指屏幕上真实存在的发光点。

对于显示设备来说,一个像素可以通过红、绿、蓝三种子像素混合出不同颜色。设备分辨率描述的就是屏幕在水平和垂直方向上分别有多少个物理像素。

例如某些 iPhone 的屏幕参数会写成:

2796 × 1290 像素分辨率,460 ppi

它表示屏幕水平方向有 2796 个物理像素,垂直方向有 1290 个物理像素。

需要注意的是,手机通常竖向使用,但分辨率参数仍然经常按「宽 × 高」描述。只是设备被竖着拿起来后,我们直观感受到的是纵向更长。

2. 屏幕尺寸和 PPI

屏幕尺寸通常指屏幕对角线的长度,单位是英寸。

PPIPixels Per Inch 的缩写,表示每英寸包含多少个物理像素。PPI 越高,同样尺寸下能容纳的物理像素越多,屏幕看起来也越细腻。

PPI 可以通过分辨率和屏幕尺寸计算:

PPI = √(水平像素² + 垂直像素²) / 屏幕尺寸

例如一个屏幕分辨率为 2796 × 1290,屏幕尺寸为 6.7 英寸,那么它的 PPI 约等于:

PPI = √(2796² + 1290²) / 6.7

在移动端开发中,我们通常不会直接用 PPI 写样式,但理解它可以帮助我们理解为什么现代手机屏幕越来越清晰,以及为什么同样的 CSS 尺寸在不同设备上仍然能保持相近的视觉大小。

3. CSS 像素

CSS 像素是浏览器布局使用的抽象单位,也就是我们平时写的 px

它不一定等于设备像素。

如果 CSS 像素和设备像素永远保持 1:1,那么随着屏幕物理像素越来越多,同样 100px 的元素会变得越来越小,页面会难以阅读。

因此,浏览器会在 CSS 像素和设备像素之间建立一层映射关系:

  • 开发者用 CSS px 描述布局尺寸。
  • 浏览器根据设备特性把 CSS px 映射到多个物理像素。
  • 用户看到的是更稳定的视觉尺寸,而不是被物理像素数量直接影响的尺寸。

这也是移动端页面能够跨设备显示的基础。

4. DPR

DPRDevice Pixel Ratio,表示设备像素比。

它描述的是一个方向上,多少个设备像素对应一个 CSS 像素:

DPR = 设备像素 / CSS 像素

可以通过 window.devicePixelRatio 获取当前设备的 DPR

console.log(window.devicePixelRatio)

常见情况如下:

DPR含义
11CSS 像素对应 1 个设备像素
21CSS 像素对应 2 × 2 个设备像素
31CSS 像素对应 3 × 3 个设备像素

这也是高清屏图片适配的原因。

如果一个图片在页面中显示为 100px × 100px,在 DPR = 2 的设备上,最好提供 200px × 200px 的图片资源;在 DPR = 3 的设备上,最好提供 300px × 300px 的图片资源。

否则,浏览器会把低分辨率图片放大显示,图片就容易变模糊。

5. viewport

早期移动浏览器需要兼容大量只为 PC 端设计的网页。

这些网页通常有固定宽度,比如 980px。如果直接用手机屏幕宽度渲染,页面会被挤坏。所以移动浏览器默认会使用一个较宽的布局视口,再把页面整体缩小显示。

这解决了兼容问题,但不适合现代移动端页面。

现代移动端页面通常会显式设置 viewport


<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">

各字段含义如下:

配置作用
width=device-width让布局视口宽度等于设备的 CSS 像素宽度
initial-scale=1.0页面初始缩放比例为 1
viewport-fit=cover允许页面覆盖整个屏幕区域,配合安全区域变量处理刘海屏和底部手势条

viewport 是移动端适配的第一步。

没有正确设置它,后续的 remvw、媒体查询和响应式布局都可能建立在错误的视口基础上。

6. 推荐适配方案

移动端适配可以拆成四层来看:

层级目标常用方案
视口确定页面基准宽度和缩放方式viewport meta
布局让结构能够随屏幕变化flexgrid、百分比、媒体查询
尺寸控制文字、间距、模块大小remclamp()vw
资源保证图片、图标、边框清晰srcsetSVG、高清图、1px 边框

实际项目里,通常不建议只依赖一种方案。

更稳妥的做法是:

  • viewport 修正移动端布局视口。
  • flexgrid 和百分比完成主体布局。
  • remclamp()vw 控制尺寸变化。
  • srcsetSVG、安全区域和细节样式处理高清屏与异形屏。

7. 响应式布局优先

对于博客、官网、后台、内容页,优先考虑响应式布局。

这类页面更关注内容可读性和结构稳定性,不需要所有元素严格按设计稿等比缩放。布局应该根据空间变化自动调整。

Flex 示例

.card-list {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
}

.card {
    flex: 1 1 280px;
}

这段代码表示:

  • 卡片最小参考宽度是 280px
  • 空间足够时多列展示。
  • 空间不足时自动换行。

Grid 示例

.article-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 24px;
}

auto-fitminmax() 可以让网格根据容器宽度自动决定列数,适合卡片列表、文章列表和商品列表。

clamp 示例

.article-title {
    font-size: clamp(24px, 6vw, 40px);
    line-height: 1.25;
}

.article-content {
    font-size: clamp(16px, 4vw, 18px);
    line-height: 1.8;
}

clamp(min, preferred, max) 的含义是:

最终值 = 在最小值和最大值之间选择一个合适的首选值

这样既可以让字体随屏幕变化,又能避免小屏过小、大屏过大。

8. rem 等比适配

rem 的核心思路是:所有尺寸都相对于根元素 htmlfont-size 计算。

如果动态改变 htmlfont-size,页面中使用 rem 的元素就会按比例缩放。

这种方案适合:

  • H5 活动页。
  • 营销落地页。
  • 强视觉还原页面。
  • 设计稿比例要求较高的移动端页面。

常见做法是以 375px 设计稿为基准,动态设置根字号:

function setRootFontSize() {
  const designWidth = 375
  const maxWidth = 540
  const width = Math.min(document.documentElement.clientWidth, maxWidth)

  document.documentElement.style.fontSize = `${width / designWidth * 16}px`
}

setRootFontSize()
window.addEventListener('resize', setRootFontSize)

然后在样式中使用 rem

.banner {
    width: 20rem;
    padding: 1rem;
    border-radius: 0.75rem;
}

需要注意的是,rem 等比缩放要限制最大宽度。

如果不限制,大屏手机、折叠屏或平板上可能会被放得过大,导致页面比例失控。通常可以给页面容器设置最大宽度并居中:

.page {
    max-width: 540px;
    min-height: 100dvh;
    margin: 0 auto;
}

9. vw 适配

vw 是视口宽度单位。

1vw = 视口宽度的 1%

如果屏幕宽度是 375px,那么 1vw = 3.75px

在设计稿为 375px 时,某个元素宽度是 75px,就可以换算为:

75 / 375 × 100 = 20vw

直接使用 vw 很方便,但也有一个问题:尺寸会一直跟着视口变化。屏幕过小或过大时,元素可能失控。

因此更推荐把 vwclamp() 组合使用:

.action-button {
    width: clamp(160px, 48vw, 240px);
    height: clamp(44px, 12vw, 56px);
    font-size: clamp(14px, 4vw, 18px);
}

这样可以保留 vw 的弹性,同时给尺寸设置上下限。

10. 高清屏图片适配

在高 DPR 设备上,一个 CSS 像素会对应多个物理像素。

如果图片资源只按 CSS 显示尺寸提供,浏览器就需要把图片拉伸到更多物理像素上显示,结果可能变模糊。

比如图片显示尺寸是 160px × 160px

DPR推荐图片资源
1160px × 160px
2320px × 320px
3480px × 480px

可以使用 srcset 让浏览器根据设备情况选择合适资源:

<img
        src="avatar@1x.png"
        srcset="avatar@2x.png 2x, avatar@3x.png 3x"
        alt="用户头像"
>

对于图标、线性图形和简单插画,优先使用 SVG。它是矢量图,在不同 DPR 下都能保持清晰。

11. 安全区域适配

全面屏手机会遇到安全区域问题,例如:

  • 顶部刘海遮挡内容。
  • 屏幕圆角裁切内容。
  • 底部手势条覆盖按钮。

如果设置了:


<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">

就可以配合 env(safe-area-inset-*) 获取安全区域距离:

.page {
    min-height: 100dvh;
    padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}

.fixed-footer {
    position: fixed;
    right: 0;
    bottom: 0;
    left: 0;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
    background: #fff;
}

这里的 100dvh 表示动态视口高度,比传统 100vh 更适合移动端浏览器。

因为移动端浏览器地址栏、工具栏可能会展开或收起,100vh 有时会导致内容被遮挡或出现多余滚动,而 100dvh 会随着可视区域变化动态更新。