🔍 Viewport 视口与适配基础

Viewport 是移动端开发最基础也最重要的概念。没有正确配置 viewport,页面在手机上会以缩放形式显示,用户体验极差。

三大视口概念

视口类型含义获取方式
布局视口CSS 布局的基准区域,默认约 980pxdocument.documentElement.clientWidth
视觉视口用户当前看到的区域,可缩放window.visualViewport.width
理想视口设备屏幕的 CSS 像素宽度screen.width

视口联动机制

三个视口并非独立运作,它们之间的联动关系直接影响页面表现:

  • 缩放时:用户双指缩放时,布局视口宽度不变,视觉视口变小(可看到的内容减少),这是浏览器缩放的本质
  • 横竖屏切换:布局视口宽高会交换(clientWidthclientHeight 互换),需用媒体查询 @media (orientation) 处理
  • initial-scale=1.0 的作用:使布局视口宽度等于理想视口宽度,确保 CSS 像素与设备宽度 1:1 对应,是适配的基石

CSS 像素、物理像素与设备像素比

理解这三种像素是掌握移动端适配的前提:

概念含义示例(iPhone 14)
物理像素屏幕真实的物理发光点,由硬件决定1170 × 2532
CSS 像素(逻辑像素)CSS 中使用的 px 单位,与屏幕密度无关390 × 844
设备像素比(DPR)物理像素 / CSS 像素,window.devicePixelRatio 可获取3(物理/CSS = 1170/390)
💡 理解记忆

CSS 中写 width: 375px,在 DPR=2 的设备上实际占用 750 个物理像素点。这就是为什么 1px 在 Retina 屏上看着"太细"——实际上它只占了一半的物理像素密度。

标准 Viewport Meta 标签

<!-- ⭐ 移动端必须添加,放在 <head> 最前面 -->
<meta name="viewport"
      content="width=device-width, initial-scale=1.0,
               maximum-scale=1.0, minimum-scale=1.0,
               user-scalable=no, viewport-fit=cover">

<!-- 参数详解 -->
<!-- width=device-width   → 布局视口 = 设备宽度(理想视口)-->
<!-- initial-scale=1.0    → 初始缩放比例 1:1 -->
<!-- maximum-scale=1.0    → 最大缩放比例(禁止放大)-->
<!-- minimum-scale=1.0    → 最小缩放比例 -->
<!-- user-scalable=no     → 禁止用户手动缩放 -->
<!-- viewport-fit=cover   → 适配 iPhone X+ 刘海屏安全区域 -->
⚠️ 关于 user-scalable=no
  • 应用型页面(SPA)可以设置禁止缩放,防止双击误触缩放
  • 内容型页面(文章、文档)建议保留缩放能力,保障无障碍访问
  • iOS 10+ 中该属性可能被 Safari 忽略
💡 内容型 vs 应用型页面的视口配置推荐
  • 内容型页面(文章、文档、博客):保留缩放能力,保障无障碍访问——
    content="width=device-width, initial-scale=1.0, maximum-scale=3.0, minimum-scale=0.5, user-scalable=yes"
  • 应用型页面(SPA、后台管理、工具类):禁止缩放,防止误触——
    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"

通过 JS 获取设备与屏幕信息

// ===== 获取设备与屏幕信息 =====
const deviceInfo = {
  // 设备像素比(DPR):物理像素 / CSS 像素
  dpr: window.devicePixelRatio || 1,

  // 屏幕尺寸(CSS 像素)
  screenWidth: screen.width,
  screenHeight: screen.height,

  // 可视区域尺寸(布局视口)
  viewportWidth: document.documentElement.clientWidth,
  viewportHeight: document.documentElement.clientHeight,

  // 物理分辨率
  physicalWidth: screen.width * (window.devicePixelRatio || 1),
  physicalHeight: screen.height * (window.devicePixelRatio || 1),

  // 判断设备类型
  isMobile: /Mobi|Android|iPhone/i.test(navigator.userAgent),
  isIOS: /iPhone|iPad|iPod/i.test(navigator.userAgent),
  isAndroid: /Android/i.test(navigator.userAgent),
  isWeChat: /MicroMessenger/i.test(navigator.userAgent),
};

console.table(deviceInfo);
监听 visualViewport 变化(软键盘弹起 / 地址栏收起时触发)
// ===== 监听 visualViewport 变化 =====
// 适用场景:软键盘弹起、地址栏收起/展开、分屏模式切换
// API 支持度:iOS Safari 9+ / Android Chrome 61+
if (window.visualViewport) {
  window.visualViewport.addEventListener('resize', function () {
    console.log({
      visualWidth: window.visualViewport.width,
      visualHeight: window.visualViewport.height,
      offsetTop: window.visualViewport.offsetTop,
    });
  });
} else {
  // 降级方案:监听 window.resize
  window.addEventListener('resize', function () {
    console.log('innerHeight 变化:', window.innerHeight);
  });
}

常见视口问题 FAQ

问题现象原因解决方案
页面在手机上显示得很小,像桌面缩放的版本 缺少 viewport meta 标签,浏览器使用默认布局视口(约 980px) <head> 中添加 <meta name="viewport" content="width=device-width, initial-scale=1.0">
iOS Safari 上 100vh 元素超出屏幕底部,出现滚动条 Safari 的地址栏/工具栏会影响 vh 的计算,100vh 包含了地址栏高度 使用新的 dvh(Dynamic Viewport Height)单位,或通过 JS 动态设置 window.innerHeight
横屏后页面布局错乱,内容显示不全 布局视口宽度随横竖屏切换而变化,未做相应适配 使用 @media (orientation: landscape) 媒体查询调整布局,给容器设置 min-width: 320px
华为/三星折叠屏设备上页面显示异常 折叠屏展开时 device-width 动态变化,视口宽度翻倍 使用 window.visualViewport 监听尺寸变化;配合 CSS @media (device-posture: folded) 或宽高比媒体查询