前言
rem(root em,根em)是 CSS3 新增的一个相对单位, 表示相对于根元素(html)的字体大小。这个单位与 em 的区别在于使用 rem 相对的是 HTML根元素
。
这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有尺寸大小,又可以避免逐层复合的连锁反应。目前,除了IE8及更早版本外,所有浏览器均已支持rem。
因此,使用 rem 单位可以让开发者更容易地实现响应式设计,因为我们只需要关注根元素的font-size。
举个简单的例子:设置字体大小和边距
<!DOCTYPE html>
<html>
<head>
<style>
/* 设置根元素的字体大小 */
html {
font-size: 16px;
}
/* 使用rem单位设置一个元素的字体大小 */
h1 {
font-size: 2rem;
}
/* 使用rem单位设置一个元素的边距 */
p {
margin-bottom: 1rem;
}
</style>
</head>
<body>
<h1>标题</h1>
<p>内容</p>
<p>内容</p>
</body>
</html>
注意:使用<!DOCTYPE html>声明为标准模式,防止浏览器在渲染文档时,切换到我们称为“怪异模式”的渲染模式,在怪异模式下样式继承会出现问题
在这个示例中,我们设置了根元素(html)的字体大小为 16px。然后,我们使用 rem 单位设置了标题(h1)的字体大小为 2rem,这等于 32px(2 * 16)。
我们还为段落(p)设置了 1rem 的底部边距,等于 16px(1 * 16)。我们想让整个页面等比变化,只需要更改根元素的 font-size 属性即可,如
html {
font-size: 20px;
}
基于此,我们在页面初始化的时候,初始化根元素的 font-size 值,监听 onresize 事件在窗口大小变化时,根据比例动态计算出根元素的 font-size 值,那么就能确保网站在不同设备上都能呈现出最佳的效果。
const getScreenWidth = () => window.innerWidth || window.screen.availWidth || window.screen.width;
const setRootFontSize = () => {
const designWidth = 375; // 设计稿宽度
const minWidth = 360;
const maxWidth = 480;
const baseFontSize = 16;
const windowWidth = getScreenWidth();
let adaptiveWidth;
if (windowWidth <= minWidth) {
adaptiveWidth = minWidth;
} else if (windowWidth >= maxWidth) {
adaptiveWidth = maxWidth;
} else {
adaptiveWidth = windowWidth;
}
const fontSize = baseFontSize * (adaptiveWidth / designWidth);
// 保留3位小数
document.documentElement.style.fontSize = `${toFixedBySelf(fontSize, 3)}px`;
};
// 初始化根元素字体大小
setRootFontSize();
// 改变窗口时重新设置
window.onresize = throttle(() => {
setRootFontSize();
}, 200);
我们可以借助 webpack 工具 postcss-pxtorem 来完成单位的自动转化,因此开发时正常使用px即可。
在项目根目录添加 postcss.config.ts文件
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 16, // 基准值
unitPrecision: 5, // 转换后的 rem 值保留的小数位数
propList: ['*'],
selectorBlackList: [],
replace: true, // 是否直接替换属性值,而不是添加备用属性。
mediaQuery: false, // 是否允许在媒体查询中转换 px。
minPixelValue: 0, // 设置要替换的最小像素值。
},
},
};
某些场景下,我们可能不需要进行单位的转化,因此 postcss-pxtorem 配置项为我们提供了可以避免px转rem的实现方法:
1、propList
:允许从 px 更改为 rem 的属性列表
propList: ['*', '!border*'] // 如:不转换任何包含 "border" 的属性
2、selectorBlackList
:选择器黑名单,如果选择器中包含这些字符串,则不转换对应的 px
selectorBlackList: ['html', '.no-rem'] // 如:不转换 "html" 标签和包含 "no-rem" 类的选择器
注意:在内联样式中编写样式属性时,我们需要手动将单位转换为rem
function pxToRem(value) {
return `${value / 16}rem`;
}
注意,打包时 postcss-pxtorem 只会将样式文件中的单位进行自动转换,而我们写在内联样式不会进行处理,故需要手动进行单位的转换。
总结
优点:
- 视觉一致性:rem 是相对于根元素(html)的字体大小来计算的,这使得整个页面的尺寸可以根据根元素的字体大小进行缩放,从而实现响应式设计。
- 易于维护:通过调整根元素的字体大小,可以一次性调整整个页面的尺寸,而不需要逐个修改每个元素的样式,简化了维护成本。
- 灵活性:可以根据不同的屏幕尺寸动态调整根元素的字体大小,从而适应不同的设备,提供更好的用户体验。
- 兼容性:rem 单位在现代浏览器中得到了广泛支持,包括移动设备和桌面设备,兼容性较好。
缺点:
- 初始设置复杂:需要设置根元素的字体大小、添加配置文件。
- 依赖JavaScript:为了实现动态调整根元素字体大小,通常需要使用JavaScript来监听窗口大小的变化并相应地调整根元素的字体大小。
- 内容展示区域:屏幕较大的设备等比放大后一屏能展示的内容会变少。