rem和vw布局
前言
在学习本章之前,应该先建立一个概念,rem属于旧的布局方案,vw属于新的布局解决方案,rem和vw属于旧往新迁移的方案,当然这不是说rem完全淘汰,因为使用rem的存量项目基数庞大,rem布局依然非常有效,但是如果新项目,完全可以使用vw来布局
需要注意的是,这里说的是布局方案,并不是rem一点不用,而是rem用在字体相关上,不用在布局上,需要强调的是,在现代 CSS 布局的最佳实践,是根据实际需求综合使用不同的布局技术和尺寸单位,而不是依赖某一种方案解决所有问题。例如这里的rem和vw布局,解决的是等比例缩放问题,但是这个这里还不是响应式,移动端网页如果直接pc打开,会非常丑陋,需要借助媒体查询来改变一些布局样式。
另外一个如果使用px和媒体查询可以吗,答案也是可以,但是px属于绝对单位,媒体查询会较为复杂,在断点与断点之间是不会有变化的,而使用vw却可以天然支持动态变化(这里的断点,不是指调试的断点,而是页面在某个宽度下样式发生变化,断点可以自己随意设置,但是按照经验之谈,可以基本划分几个区间,如一些CSS框架就有断点区间,需要注意断点不是CSS规定死的,而是一种约定俗成的东西)
在一些教学视频中,会经常出现两个工具库:postcss-pxtorem和postcss-px-to-viewport,它们是将项目写的px转为rem或者vw,同样的转为rem,就需要写js脚本去动态修改html下的字体大小,vw是根据视口的,它们都有一个配置文件,来配置需要设置转换属性、忽略规则和适用范围,因为不能全部的px转为rem或者vw,如边框、图标、固定控件未必需要等比例缩放
rem布局方案
原理
em(root em)是 CSS 中的相对长度单位,它的大小取决于根元素 html 的 font-size。
rem 布局的核心原理:通过 JavaScript 根据视口宽度动态修改 html 的 font-size,使使用 rem 单位的元素随屏幕宽度等比例缩放。
例如:
html { font-size: 10px;}
.box { width: 10rem; /* 100px */ height: 5rem; /* 50px */}当 html 的 font-size 变成 5px 时:
html { font-size: 5px;}元素的实际尺寸随之改变:
- 宽度:10 × 5 = 50px
- 高度:5 × 5 = 25px
因此,只需要动态调整根元素的字体大小,就能控制所有使用 rem 单位的元素尺寸。
实现
以 750px 宽度的移动端设计稿为例,实现底部 Tabbar 的等比例缩放。确定设计稿的基准值,假设设计稿尺寸:
- 设计稿宽度:750px
- Tabbar 高度:96px
为了方便计算,规定设计稿中:
1rem = 10px那么:
[ 750 \div 10 = 75rem ]
Tabbar 的高度:
[ 96 \div 10 = 9.6rem ]
因此,我们需要在不同屏幕下始终保持:
[ htmlFontSize = \frac{viewWidth}{75} ]
动态设置根元素字体大小
const docEl = document.documentElement;
const setHtmlFontSize = () => { const viewWidth = docEl.clientWidth; const htmlFontSize = viewWidth / 75;
docEl.style.fontSize = htmlFontSize + 'px';};
setHtmlFontSize();
window.addEventListener('resize', setHtmlFontSize);代码说明:
document.documentElement:获取根元素html。clientWidth:获取当前布局视口宽度。viewWidth / 75:计算当前屏幕对应的根字体大小。style.fontSize:动态设置html的字体大小。resize:视口宽度变化时重新计算。
不同屏幕下的缩放效果
| 视口宽度 | html font-size | Tabbar 高度 |
|---|---|---|
| 320px | 4.267px | 40.96px |
| 375px | 5px | 48px |
| 390px | 5.2px | 49.92px |
| 414px | 5.52px | 52.99px |
| 750px | 10px | 96px |
完整实现
<!DOCTYPE html><html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>rem 布局方案</title>
<style> * { box-sizing: border-box; }
body { margin: 0; font-size: 16px; }
.tabbar-layout { position: fixed; bottom: 0; left: 0; width: 100%; height: 9.6rem; box-shadow: 0 -4px 4px rgba(0, 0, 0, 0.15); }
.tabbar-layout img { display: block; width: 100%; } </style>
<script> const docEl = document.documentElement;
const setHtmlFontSize = () => { const viewWidth = docEl.clientWidth; const htmlFontSize = viewWidth / 75;
docEl.style.fontSize = htmlFontSize + 'px'; };
setHtmlFontSize();
window.addEventListener('resize', setHtmlFontSize); </script> </head>
<body> <p>段落</p>
<div class="tabbar-layout"> <img src="./tabbar.png" alt="标签栏" /> </div> </body></html>这里有两个值得注意的地方:
第一,body 使用 font-size: 16px,因此段落文字不会随着根元素字体大小的变化而等比例缩放。这可以避免文字随着屏幕宽度无限放大或缩小。
第二,Tabbar 使用 width: 100%,高度使用 rem。也可以将宽度设置为 75rem,但当前情况下使用百分比更简单。
扩展flexible
什么是 flexible?
lib-flexible 是阿里巴巴移动前端团队推出的一套移动端适配方案,主要用于实现基于 rem 的等比例布局。
GitHub:amfe/lib-flexible
它的核心功能与前面手写的 JavaScript 一样:根据视口宽度,自动设置根元素 html 的 font-size。
不过,它采用了不同的换算规则。
flexible 的计算原理
flexible 经典的计算方式是将视口宽度划分为 10 份:
const docEl = document.documentElement;
const setRemUnit = () => { const rem = docEl.clientWidth / 10;
docEl.style.fontSize = rem + 'px';};以 750px 设计稿为例:
[ 750 \div 10 = 75px ]
因此:
1rem = 75px(设计稿基准下)
Tabbar 高度为 96px,换算为:
[ 96 \div 75 = 1.28rem ]
CSS:
.tabbar-layout { width: 100%; height: 1.28rem;}这样在 375px 的视口下:
[ 1.28 \times (375 \div 10) = 48px ]
flexible 与手写方案的区别
| 对比项 | 手写方案 | flexible |
|---|---|---|
| 根字体计算 | 视口宽度 ÷ 75 | 视口宽度 ÷ 10 |
| 750px 视口下 1rem | 10px | 75px |
| 96px 设计尺寸 | 9.6rem | 1.28rem |
| 核心机制 | 动态修改 font-size | 动态修改 font-size |
| 缩放效果 | 等比例缩放 | 等比例缩放 |
两者没有本质区别,只是采用了不同的 rem 换算基准。
拓展-VScode插件
在开发中,会计算rem的值,这非常麻烦,这里推荐一个插件px2rem
vw+rem布局方案
原理
rem + vw 布局方案结合了两种单位的特点:
- vw:根据视口宽度动态计算根元素的字体大小。
- rem:根据根元素的字体大小计算元素尺寸。
核心原理:使用 vw 动态设置 html 的 font-size,再使用 rem 实现元素的等比例缩放。
与传统的 JS + rem 方案相比,不再需要 JavaScript 动态计算根元素字体大小,而是直接通过 CSS 实现。
实现
假设设计稿宽度为 750px,规定在设计稿宽度下:
1rem = 100px那么:
html { font-size: calc(100vw / 7.5);}计算结果:
| 视口宽度 | html 的 font-size | 1rem |
|---|---|---|
| 320px | 42.67px | 42.67px |
| 375px | 50px | 50px |
| 414px | 55.2px | 55.2px |
| 750px | 100px | 100px |
例如设计稿中的底部导航栏宽度为 750px,高度为 96px。
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>rem + vw 布局方案</title> <style> * { box-sizing: border-box; }
html { font-size: calc(100vw / 7.5); }
body { margin: 0; font-size: 16px; }
.tabbar-layout { position: fixed; bottom: 0; left: 0; width: 7.5rem; height: 0.96rem; background-color: #eee; } </style></head><body> <div class="tabbar-layout">底部导航栏</div></body></html>在不同设备下:
| 视口宽度 | 导航栏宽度 | 导航栏高度 |
|---|---|---|
| 320px | 320px | 40.96px |
| 375px | 375px | 48px |
| 414px | 414px | 52.99px |
| 750px | 750px | 96px |
最终实现:视口宽度发生变化时,rem 对应的像素值自动变化,从而实现元素等比例缩放。
怎么计算
为什么是 100vw / 7.5?
假设:
- 设计稿宽度:
750px - 规定在设计稿宽度下:
1rem = 100px
首先:
100vw = 750px /* 当视口宽度为 750px 时 */那么,要让根元素的 font-size 等于 100px:
[ 750 \div 7.5 = 100 ]
所以:
html { font-size: calc(100vw / 7.5);}其中 7.5 的来源就是:
[ 7.5 = \frac{750}{100} ]
如果 1rem = 10px 呢?
设计稿还是 750px,只是将基准改为:
1rem = 10px那么:
[ 750 \div 10 = 75 ]
代码变成:
html { font-size: calc(100vw / 75);}对比一下:
| 视口宽度 | 1rem = 100px 方案 | 1rem = 10px 方案 |
|---|---|---|
| 320px | 42.67px | 4.27px |
| 375px | 50px | 5px |
| 750px | 100px | 10px |
这里的 1rem = 100px 和 1rem = 10px 都是指设计稿对应的 750px 视口宽度下的值,并不是在所有屏幕下固定不变。
通用计算公式
例如设计稿宽度为 750px:
除数=设计稿宽度 / 设计稿设定的1rem像素值
| 设定的 1rem | 计算过程 | CSS |
|---|---|---|
| 100px | 750 ÷ 100 = 7.5 | calc(100vw / 7.5) |
| 50px | 750 ÷ 50 = 15 | calc(100vw / 15) |
| 10px | 750 ÷ 10 = 75 | calc(100vw / 75) |
vw布局方案
原理
vw 是 CSS 的视口相对单位,1vw 等于视口宽度的 1%。
vw 布局方案的核心原理:将设计稿中的 px 单位换算为 vw,让元素尺寸随着视口宽度变化而等比例缩放。
例如:
| 视口宽度 | 1vw | 10vw |
|---|---|---|
| 320px | 3.2px | 32px |
| 375px | 3.75px | 37.5px |
| 750px | 7.5px | 75px |
换算公式
[ vw = \frac{设计稿中元素的尺寸}{设计稿宽度}\times100 ]
假设设计稿宽度为 750px,其中一个元素的高度为 96px。
[ \frac{96}{750}\times100=12.8vw ]
.tabbar { height: 12.8vw;}实际效果:
| 视口宽度 | 元素高度 |
|---|---|
| 320px | 40.96px |
| 375px | 48px |
| 750px | 96px |
因此,元素能够根据视口宽度进行等比例缩放。
实现
<!DOCTYPE html><html lang="en">
<head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>VW 布局方案</title> <style> img { width: 100%; } body { font-size: 16px; } .tabbar-layout { position: fixed; bottom: 0; left: 0; box-shadow: 0 -.4rem .4rem rgba(0, 0, 0, 0.15);
/* width: 100%; height: 48px; */ width: 100vw; height: 12.88vw; } </style> <style>
</style> <script> // 计算vw单位的值,以750px为基准 // 100vw = 750px // 测量的ox/750px(基准) = ?VW/100vw // ?VW = (测量的px/750px(基准)) * 100vw </script></head>
<body> <p>段落</p> <div class="tabbar-layout"> <img src="./tabbar.png" alt="标签栏" /> </div></body>
</html>拓展-VScode插件
在开发中,会计算rem的值,这非常麻烦,这里推荐一个插件px2rem
实战
结合以上三种方案,修改之前的仿慕课网首页
github代码仓库:https://github.com/youzi177/css-layout-lab
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














