rem自适应核心是html根字体大小须随屏幕宽度实时等比变化,需用clientWidth而非screen.width计算,避开DPR干扰与viewport缩放陷阱,并在DOMContentLoaded、resize及orientationchange时重算,配合动态initial-scale与合理basePx(推荐37.5)实现精准适配。

直接说结论:rem 自适应不是“写完就跑”,核心在于 html 根字体大小必须随屏幕宽度实时、等比变化,且这个变化要避开 devicePixelRatio 干扰、绕过 viewport 缩放陷阱、扛住横竖屏切换。光套个脚本不调参数,90% 的项目会在 iPhone 15 Pro、折叠屏或安卓高分屏上出错。
为什么动态设置 html.style.fontSize 必须用 clientWidth 而不是 screen.width
因为 screen.width 返回的是设备物理像素宽度(比如 iPhone 14 Pro 是 1170),而 CSS 布局只认视口宽度(document.documentElement.clientWidth,通常是 390 或 430)。用 screen.width 算出来的 font-size 会放大 2–3 倍,导致所有元素巨大且溢出。
-
clientWidth是 layout viewport 的 CSS 像素宽度,才是你设计稿换算的基准 - 必须在
DOMContentLoaded和resize(含orientationchange)时都触发重算 - 横屏时
clientWidth可能突然变大(如从 390→844),需加判断防止字号突变:若clientWidth > 750 * dpr,则按750 * dpr截断
viewport meta 标签里 initial-scale 为什么不能硬写 1
硬设 initial-scale=1 在 DPR > 1 的设备(几乎所有现代手机)上,会导致页面被浏览器自动缩放,使 clientWidth 失真。例如 iPhone 13(DPR=3)下,clientWidth 可能从预期的 390 变成 130,整个 rem 比例崩掉。
- 正确做法是根据
devicePixelRatio动态计算scale:设scale = 1 / dpr - 同时在 meta 中写入
content="width=device-width, initial-scale="+scale+", maximum-scale="+scale+", minimum-scale="+scale+" - iOS 下要额外处理 iframe 场景:
window.top === window.self才启用缩放,否则禁用
设计稿 750px 时,1rem 到底该等于多少 px?别信“固定 100px”
所谓“750px 设计稿 → 1rem = 100px”只是方便心算的约定,实际换算系数由脚本决定。关键看公式:html.style.fontSize = (clientWidth / 750) * basePx。这个 basePx 选 100 还是 75,直接影响开发体验和小屏精度。
- 选
basePx = 100:换算简单(200px → 2rem),但 iPhone SE(320px)下font-size= 42.67px,小数点后两位可能触发渲染抖动 - 选
basePx = 75(flexible.js 方案):320px 下为 32px,整数更稳;但 100px 元素得写1.333rem,需靠 VSCode 插件cssrem实时转 - 真正该做的是:统一用 PostCSS 插件
postcss-pxtorem,配置rootValue: 37.5(对应 750/20),这样 100px → 2.666rem,保留三位小数且兼容性最好
flexible.js 已废弃,但它的核心逻辑仍值得手写复用
淘宝早已停止维护 flexible.js,但它暴露的关键问题没过时:DPR 判断不准、横屏未处理、无最大最小字号限制。现在应手写精简版,控制在 20 行内。
(function (doc, win) {
var docEl = doc.documentElement;
var recalc = function () {
var width = docEl.clientWidth;
var dpr = win.devicePixelRatio || 1;
if (width / dpr > 750) width = 750 * dpr;
docEl.style.fontSize = (width / 750) * 37.5 + 'px';
};
win.addEventListener('resize', recalc);
win.addEventListener('orientationchange', recalc);
doc.addEventListener('DOMContentLoaded', recalc);
})(document, window);
- 删掉所有 iOS/Android UA 判断——
devicePixelRatio现代浏览器已稳定返回 - 去掉
setInterval强刷逻辑,只靠事件监听足够 - 加
if (!width) return防空值报错,避免 SSR 渲染时崩溃
最易被忽略的点:rem 只解决宽度等比缩放,高度、行高、border、阴影依然要单独适配;图片资源必须提供 2x/3x 版本并用 background-image 或 srcset 切换,否则在高 DPR 屏上发虚——这些和 rem 无关,但上线前必查。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











