视频加载失败

rem和vw布局

2358 字
12 分钟
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-sizeTabbar 高度
320px4.267px40.96px
375px5px48px
390px5.2px49.92px
414px5.52px52.99px
750px10px96px

完整实现

index.html
<!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 视口下 1rem10px75px
96px 设计尺寸9.6rem1.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-size1rem
320px42.67px42.67px
375px50px50px
414px55.2px55.2px
750px100px100px

例如设计稿中的底部导航栏宽度为 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>

在不同设备下:

视口宽度导航栏宽度导航栏高度
320px320px40.96px
375px375px48px
414px414px52.99px
750px750px96px

最终实现:视口宽度发生变化时,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 方案
320px42.67px4.27px
375px50px5px
750px100px10px

这里的 1rem = 100px 和 1rem = 10px 都是指设计稿对应的 750px 视口宽度下的值,并不是在所有屏幕下固定不变。

通用计算公式

例如设计稿宽度为 750px:

除数=设计稿宽度 / 设计稿设定的1rem像素值

设定的 1rem计算过程CSS
100px750 ÷ 100 = 7.5calc(100vw / 7.5)
50px750 ÷ 50 = 15calc(100vw / 15)
10px750 ÷ 10 = 75calc(100vw / 75)

vw布局方案#

原理#

vw 是 CSS 的视口相对单位,1vw 等于视口宽度的 1%。

vw 布局方案的核心原理:将设计稿中的 px 单位换算为 vw,让元素尺寸随着视口宽度变化而等比例缩放。

例如:

视口宽度1vw10vw
320px3.2px32px
375px3.75px37.5px
750px7.5px75px

换算公式

[ vw = \frac{设计稿中元素的尺寸}{设计稿宽度}\times100 ]

假设设计稿宽度为 750px,其中一个元素的高度为 96px。

[ \frac{96}{750}\times100=12.8vw ]

.tabbar {
height: 12.8vw;
}

实际效果:

视口宽度元素高度
320px40.96px
375px48px
750px96px

因此,元素能够根据视口宽度进行等比例缩放。

实现#

<!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

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

rem和vw布局
https://f.youzi177.cn/posts/2026101501rem-he-vw-bu-ju/
作者
小封鸭
发布于
2026-10-15
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
小封鸭
小楼一夜听春雨,深巷明朝卖杏花
公告
欢迎来到我的博客。这里主要记录前端全栈和日常内容
分类
标签
最新动态
站点统计
文章
60
分类
3
标签
12
总字数
107,734
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
EdgeOne Pages
博客版本
Firefly v6.16.8
文章许可
CC BY-NC-SA 4.0
文章目录