viewport 标签必须硬编码在 head 最前面且紧贴 meta charset 后,内容须为 width=device-width, initial-scale=1.0;禁用 user-scalable=no 和 maximum-scale=1.0;页面溢出需排查 CSS 溢出而非仅依赖 viewport。

viewport 必须硬编码在 head 最前面
浏览器解析 HTML 是顺序执行的,viewport 标签必须在首次渲染前生效。塞在 <body> 里、用 JS 动态插入(比如 document.head.appendChild())、或被框架(如 Vue 的 <client-only>、Next.js 的 SSR 模板漏传)包裹,Safari 和多数 Android WebView 都会直接忽略。
✅ 正确位置:紧贴 <meta charset> 后,<head> 内最靠前
❌ 常见错误:CMS 自动生成两个 viewport(以第一个为准,但中间已错乱)、SSR 模板未透出、动态 hydrate 时补写
⚠️ 调试方法:document.querySelector('meta[name="viewport"]')?.content 确认值未被覆盖;DevTools → Elements → 搜索 viewport
立即学习“前端免费学习笔记(深入)”;
width=device-width 和 initial-scale=1.0 必须配对
只写 width=device-width,iOS Safari 仍可能按 980px 渲染再缩放,导致文字模糊、touch 坐标偏移、媒体查询断点失效;只写 initial-scale=1.0,某些安卓 WebView 会无视设备宽度,引发横向滚动。
✅ 最小可用组合:<meta name="viewport" content="width=device-width, initial-scale=1.0">
❌ 危险写法:width=375(横屏崩)、width=1200(小屏拉伸)、initial-scale=1 不带 width=device-width
⚠️ 注意:device-width 是经 DPR 换算后的 CSS 像素宽度,不是物理像素,硬写死数值毫无意义
禁止使用 user-scalable=no 和 maximum-scale=1.0
加了 user-scalable=no,iOS 13+ 已基本无视;Android 行为不一致;而 maximum-scale=1.0 会禁用系统级「更大字体」和辅助缩放,违反 WCAG 可访问性要求,苹果也明确不推荐。
✅ 合理替代:touch-action: manipulation 控制区域手势,或用 CSS 限制特定容器可缩放
❌ 高危组合:user-scalable=no, maximum-scale=1.0, minimum-scale=1.0 —— 锁死所有缩放能力,老年用户和视障用户无法使用
⚠️ 特殊例外仅限 Kiosk 自助终端、或嵌入 WebView 且 native 层已统一接管缩放(需客户端配合)
viewport 写对了,但页面还是溢出?先查 CSS 是否撑破视口
initial-scale=1.0 不是万能锁。常见真因是 CSS 或内容溢出:<html> 或 <body> 上写了 font-size: 62.5% 却没配合 rem 计算,导致所有 rem 值错乱;图片没加 max-width: 100%; height: auto;;外层容器用了固定 width: 375px;或 flex 子元素在老 iOS 上被压缩(min-width 失效)。
✅ 必做检查项:
- 删掉所有硬编码
width、font-size的 px 值,改用%、rem、vw - 给所有
<img>加max-width: 100%; height: auto; - 检查
<html>或<body>是否有意外的margin、padding或border - 老 iOS(≤ iOS 12)中,flex 子元素优先用
flex-shrink: 0替代min-width
真正容易被忽略的是:问题只在真机 DPR > 1 时才暴露,模拟器和桌面 Chrome DevTools 往往看不出 —— 必须真机连调。



















