移动端页面自适应:用动态根字号 + rem 实现等比缩放
写移动端页面时,我在一个项目里用了一个很常见的自适应方案:根据屏幕宽度动态设置 html 的 font-size,然后页面尺寸尽量用 rem 来写。
这个方案在今天看不算新,但它很适合用来理解移动端自适应的底层思路:不是每个元素都单独计算,而是先确定一个页面基准,再让所有 rem 尺寸跟着根字号一起变化。
先看核心代码
在这个项目的很多 HTML 页面底部,都能看到这样一行代码:
document.getElementsByTagName("html")[0].style.fontSize =
document.documentElement.clientWidth / 20 + "px";
它的意思是:
- 获取当前页面可视宽度:
document.documentElement.clientWidth - 把屏幕宽度分成 20 份
- 每一份作为
html的根字号 - 页面中使用
rem的尺寸,都会随着根字号变化
比如手机宽度是 375px:
375 / 20 = 18.75
那么:
1rem = 18.75px
如果手机宽度是 320px:
320 / 20 = 16
那么:
1rem = 16px
这样一来,同样写 10rem,在不同屏幕上的实际像素值就不同,页面比例也会跟着屏幕宽度变化。
为什么除以 20
这套写法本质上是把屏幕宽度抽象成 20rem。
也就是说,不管用户手机宽度是多少,页面设计时都可以把整个屏幕宽度理解成:
width: 20rem;
如果一个元素宽度写成:
width: 10rem;
它就相当于占屏幕宽度的一半。
如果写成:
left: 2rem;
width: 16rem;
它就表示左右各留 2rem,中间内容占 16rem。
在项目样式里可以看到这种写法:
.z-jihe {
width: 16rem;
position: absolute;
top: 40%;
left: 2rem;
}
.z-btnan {
width: 12rem;
margin-left: 4rem;
}
这两个例子都能看出一个设计习惯:页面宽度按 20rem 算,元素宽度和左右间距用 rem 分配。
viewport 也很关键
只设置根字号还不够,移动端页面通常还需要写 viewport:
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
>
这段的作用是告诉浏览器:页面宽度按设备宽度来,不要用默认的虚拟布局宽度。
如果没有这段,clientWidth 可能不是我们以为的手机实际宽度,后面的 clientWidth / 20 也就不准了。
所以这个方案的完整思路是:
viewport 控制页面视口
↓
JS 读取当前视口宽度
↓
设置 html 根字号
↓
CSS 用 rem 写尺寸
↓
不同屏幕下页面等比变化
这是不是 zoom 缩放
严格来说,这不是 CSS 的 zoom 缩放。
项目里确实有一段:
.clear {
zoom: 1;
}
但这里的 zoom: 1 是早期常见的清除浮动写法,主要是为了触发旧版 IE 的 hasLayout,不是用来缩放整个页面。
真正让页面适配不同屏幕的,是这句:
document.documentElement.clientWidth / 20
再配合 CSS 里的 rem。
所以更准确地说,这个项目用的是:
动态根字号 + rem 自适应
而不是:
zoom 缩放网页
一个具体例子
假设页面里有一个按钮:
.z-btnan {
width: 12rem;
margin-left: 4rem;
}
因为整个页面宽度被抽象成 20rem,所以:
- 左边距
4rem - 按钮宽度
12rem - 右边剩余
4rem
按钮就会在页面中水平居中。
在 375px 屏幕上:
1rem = 18.75px
按钮宽度 = 12 * 18.75 = 225px
左边距 = 4 * 18.75 = 75px
在 320px 屏幕上:
1rem = 16px
按钮宽度 = 12 * 16 = 192px
左边距 = 4 * 16 = 64px
比例没变,只是实际像素变小了。
这就是 rem 自适应的核心。
当时这种写法的优点
这个方案有几个优点:
- 代码很少,一行 JS 就能确定全局缩放基准。
- 页面比例比较稳定,适合移动端活动页、商城页、表单页。
- 不依赖构建工具,普通 HTML 页面也能用。
- 对早期静态页面很友好,复制到多个页面就能生效。
在这个旧项目里,这行代码出现在很多页面中,说明它当时承担的是整套移动端页面的统一适配方案。
这个写法的问题
现在回头看,它也有一些明显问题。
第一,代码重复。每个页面都写一遍:
document.getElementsByTagName("html")[0].style.fontSize =
document.documentElement.clientWidth / 20 + "px";
更好的方式是抽成一个公共 JS 文件。
第二,它只在页面加载时执行。如果用户旋转屏幕,或者窗口宽度变化,根字号不会自动重新计算。更完整的写法应该监听 resize。
第三,所有屏幕都按比例缩放,不一定适合大屏。比如在平板或 PC 浏览器里,页面可能被放得过大。因此通常要设置一个最大宽度。
可以改成一个更完整的版本
如果现在手写,我会这样封装:
(function () {
function setRootFontSize() {
var docEl = document.documentElement;
var width = docEl.clientWidth;
// 避免在平板或 PC 上被无限放大
if (width > 750) {
width = 750;
}
docEl.style.fontSize = width / 20 + "px";
}
setRootFontSize();
window.addEventListener("resize", setRootFontSize);
})();
这样做有三个改进:
- 封装成函数,方便复用。
- 页面尺寸变化时重新计算。
- 限制最大宽度,避免大屏过度放大。
和现代方案对比
现在做移动端自适应,常见选择更多:
rem:适合按设计稿比例缩放的移动端页面。vw:可以直接按视口宽度计算,不需要 JS。- Flex/Grid:适合结构响应式,而不是整体等比缩放。
- 媒体查询:适合不同屏幕下切换布局。
如果只是写一个移动端 H5 活动页,rem 方案仍然容易理解。如果是复杂应用,就不能只靠等比缩放,还要考虑内容流动、断点、组件布局和可读性。
总结
这段旧代码最有价值的地方,是它用非常少的代码解决了一个真实问题:不同手机屏幕宽度下,页面如何保持相近的视觉比例。
核心公式就是:
htmlFontSize = viewportWidth / 20
然后 CSS 用 rem 表达尺寸。
它不是严格意义上的 zoom 页面缩放,而是“动态根字号 + rem”的移动端自适应方案。理解了这个思路,再去看 lib-flexible、px2rem、vw,就会轻松很多。

浙公网安备 33010602011771号