
rem缩放的本质是改html的font-size
rem单位始终相对于根元素html的font-size计算,所以全局等比缩放只有一条路径:动态改document.documentElement.style.fontSize。不是改body,不是用transform: scale(),更不是靠媒体查询硬写死多套尺寸——那些要么失效,要么破坏布局流或交互坐标。
常见错误现象:rem值没变但页面没缩放、缩放后表单控件错位、vh/vw和rem比例脱节、iOS Safari里缩放卡顿。
- 缩放必须在
DOMContentLoaded后执行,否则可能被样式表覆盖 - 建议用
document.documentElement.style.fontSize = '62.5%'这类百分比写法,避免绝对像素值在DPR变化时失准 - 如果项目用了
postcss-pxtorem,确保其rootValue与运行时设置的font-size一致,否则编译出的rem会错位
响应式缩放要避开window.innerWidth直接映射
很多人一上来就写fontSize = innerWidth / 375 * 100 + 'px',看似按iPhone 6基准换算,实际在折叠屏、横屏、Zoom浏览器(如Chrome强制缩放)下全崩。真正稳定的依据是设备独立像素(DIP)与CSS像素的比值,即window.devicePixelRatio本身不可靠,而screen.width / window.innerWidth又受缩放干扰。
更稳妥的做法是基于设计稿宽度做比例缩放,并预留最小/最大限制:
立即学习“前端免费学习笔记(深入)”;
function setRem() {
const baseWidth = 375; // 设计稿宽度
const scale = Math.min(Math.max(window.innerWidth / baseWidth, 0.5), 2);
document.documentElement.style.fontSize = `${scale * 100}px`;
}
window.addEventListener('resize', setRem);
setRem();- 不推荐用
vw单位替代(如html { font-size: 100vw / 3.75 }),它在竖屏/横屏切换时会触发重排且无法控制上下限 - 缩放后需手动触发
resize事件给依赖它的第三方库(如某些图表组件) - 微信内置浏览器对
font-size突变有渲染延迟,可加setTimeout(() => forceRepaint(), 0)简单触发重绘
字体大小别跟着rem无脑缩放
全局rem缩放后,font-size也会同比例变大,但小字号文字(如12px标签、10px图标说明)极易糊掉或超出容器。这不是bug,是设计约束没分离。
解决方案是字体大小走px或em,其他间距、宽高用rem:
p { font-size: 14px; } /* 固定 */
.card { width: 10rem; padding: 1.2rem; } /* 缩放 */- 用
clamp()兜底(如font-size: clamp(12px, 1.2rem, 16px)),但注意IE不支持 - 若必须用
rem设字体,需在JS里单独计算一个“字体缩放系数”,和全局缩放解耦 - 缩放后
line-height若设为无单位数值(如1.5),它会随font-size自动缩放,这是正确行为;若设1.5rem,就会双重缩放
测试时最容易漏掉iframe和shadow DOM
主页面缩放了,嵌入的iframe内容不会自动同步,因为它的html根元素是独立作用域。同理,Web Component里的shadowRoot也得单独设置font-size。
-
iframe内需在自身上下文里重复执行setRem()逻辑,不能靠父页透传 - Shadow DOM中修改根节点需用
shadowRoot.host.parentNode找宿主,再取其fontSize计算,不能直接读getComputedStyle(document.documentElement) - 打印样式表(
@media print)默认忽略font-size动态变更,需要额外用matchMedia('print').addEventListener监听并重置
缩放逻辑越简单越可靠,但根元素font-size一旦被多个脚本反复覆盖,就容易打架。上线前务必在折叠屏、iPad横屏、Windows高DPI+缩放设置下实测输入框焦点位置和滚动锚点是否偏移。










