3. rem适配方案:从原理到实战
说到移动端适配,rem方案绝对是个绕不开的话题。我记得刚入行那会儿,还在用px写死尺寸,结果换个手机就崩得不成样子。后来接触了rem,才算是真正找到了移动端适配的「钥匙」。
今天我就带大家把rem方案彻底搞明白。从原理到实战,咱们一步步来。
3.1 rem单位原理:它到底是个啥?
rem,全称是「root em」。说白了,它就是相对于根元素(html)的字体大小来计算的。
举个例子:
/* 如果 html 的 font-size 是 16px */
html { font-size: 16px; }
/* 那么 1rem = 16px */
.box { width: 10rem; } /* 实际宽度 = 160px */
嗯,就这么简单。但为什么它能解决适配问题呢?
你想想看:如果我们让html的字体大小随着屏幕宽度变化,那所有用rem写的尺寸不就跟着一起变了吗?
这就是rem适配的核心思想——等比缩放。
核心要点:rem不是固定值,它依赖于根字体大小。根字体变了,所有rem单位的值都会跟着变。
3.2 根字体大小设置:怎么定这个基准?
我个人习惯用设计稿宽度来反推。比如设计稿是750px宽,那我们就希望在这个宽度下,1rem等于某个方便计算的数值。
常见的做法有两种:
| 方案 | 设计稿宽度 | 根字体大小 | 换算关系 |
|---|---|---|---|
| 方案一 | 750px | 75px | 1rem = 75px |
| 方案二 | 750px | 37.5px | 1rem = 37.5px |
方案一的好处是:设计稿上100px,直接写1.333rem(100/75),计算简单。但有个坑——很多浏览器对根字体大小有限制,最小不能低于12px,最大不能超过某个值。
方案二更稳妥,但计算时得除以37.5,稍微麻烦点。
我的建议:新手先用方案二,配合postcss-pxtorem这类工具,写px自动转rem,省心。
3.3 动态计算rem:让适配活起来
根字体大小不能写死,得根据屏幕宽度动态算。怎么做?
核心公式就一句话:
根字体大小 = 屏幕宽度 / 设计稿宽度 × 基准值
举个例子,设计稿750px,基准值75px:
// 屏幕宽度 375px(iPhone 6/7/8)
根字体大小 = 375 / 750 × 75 = 37.5px
// 屏幕宽度 414px(iPhone 6/7/8 Plus)
根字体大小 = 414 / 750 × 75 = 41.4px
代码实现也很简单:
// 动态设置根字体大小
(function() {
var docEl = document.documentElement;
var designWidth = 750; // 设计稿宽度
var baseFontSize = 75; // 基准字体大小
function setRem() {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
// 限制最大宽度,防止在PC上过大
if (clientWidth > 750) {
clientWidth = 750;
}
docEl.style.fontSize = (clientWidth / designWidth) * baseFontSize + 'px';
}
setRem();
window.addEventListener('resize', setRem);
})();
这段代码我用了好多年,基本没出过问题。但要注意一点——一定要在页面加载前执行,不然会出现闪屏。
避坑指南:我曾经把这段代码放在window.onload里,结果页面先渲染了默认字体大小,然后才调整,导致所有元素「跳」了一下。后来改成内联脚本放在head里,问题就解决了。
3.4 lib-flexible库的使用:偷懒的正确姿势
说实话,上面那段代码虽然能用,但还不够完善。比如横竖屏切换、dpr适配等问题,自己写容易漏。
这时候就该lib-flexible出场了。它是淘宝前端团队出品的一个库,专门解决rem适配问题。
使用方式极其简单:
<!-- 引入 lib-flexible -->
<script src="https://cdn.jsdelivr.net/npm/amfe-flexible@2.2.1/index.min.js"></script>
引入之后,它会自动做三件事:
- 动态设置根字体大小——根据屏幕宽度自动计算
- 处理dpr——给html加上data-dpr属性,方便做高清适配
- 监听窗口变化——横竖屏切换时自动重新计算
配合px转rem的工具,写起来特别爽:
// 以750px设计稿为例
// 直接写设计稿上的px值
.box {
width: 750px; /* 自动转成 10rem */
height: 100px; /* 自动转成 1.333rem */
font-size: 28px; /* 自动转成 0.373rem */
}
我用的是postcss-pxtorem,配置也很简单:
// postcss.config.js
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 75, // 对应lib-flexible的基准值
propList: ['*'], // 所有属性都转换
selectorBlackList: ['.norem'] // 排除某些类
}
}
}
小技巧:如果某个属性不想转rem,可以加注释 /* no */。比如:border: 1px /* no */;,它就会保持px不变。
3.5 实战中的几个坑
做rem适配这么多年,我踩过的坑还真不少。挑几个典型的说说:
- 字体大小别用rem——字体用rem会导致在小屏上字太小,大屏上字太大。我一般用px配合媒体查询,或者用vw。
- 1px边框问题——rem缩放后,1px可能会变成0.5px或2px。解决方案是用伪元素+transform缩放,或者用0.5px的边框。
- 第三方组件库冲突——有些UI库自己带了rem适配,和lib-flexible一起用会打架。解决办法是统一用一个方案。
我曾经踩过的坑:有次项目用了vant组件库,它默认是基于375px设计稿的。而我用了750px的lib-flexible,结果所有组件都大了一倍。后来发现vant支持自定义rootValue,改成75就好了。
3.6 总结一下
rem适配方案,说白了就是三步:
- 定基准——确定设计稿宽度和根字体大小
- 动态算——根据屏幕宽度实时计算根字体
- 写rem——开发时用rem单位,或者用工具自动转换
lib-flexible帮我们搞定了第二步,剩下的就是写代码了。我个人建议新手直接上lib-flexible+postcss-pxtorem,等熟悉了原理再考虑自己实现。
下一章我会讲vw/vh方案,到时候咱们对比一下,看看哪个更适合你的项目。
公众号:蓝海资料掘金营,微信deep3321