移动端适配别再死磕 rem 了,2026 年这套方案更务实

移动端适配别再死磕 rem 了,2026 年这套方案更务实

导语

如果你还在项目里维护一套 lib-flexible + rem + px2rem 的适配方案,先别急着加新功能。我最近把手里的移动端项目统一换了一套思路,代码更少、横竖屏切换也更自然,今天把踩过的坑和最终的方案拆给你看。

从 rem 说起:它到底解决了什么问题

rem 方案的核心逻辑是:JS 根据视口宽度动态设置 htmlfont-size,CSS 里统一用 rem 写尺寸,从而达到”一套代码自适应多屏幕”的目的。

这套方案在 2016–2020 年确实帮了大忙,尤其是淘宝 lib-flexible 那个年代。但 rem 有 3 个至今没解决好的问题:

  • 只能响应视口,不能响应容器。 同一个商品卡片组件,在首页大屏里该横排,在侧边栏窄容器里该竖排,rem 做不到,因为 rem 只跟屏幕宽度挂钩。
  • 横竖屏切换会有”缩放感”。 竖屏下 font-size 是 100px,横屏后视口变宽,字体被重新计算放大,用户感觉整页突然”变大”了。
  • 维护成本高。 需要 JS 运行时计算、需要统一设计稿基准宽度、需要处理 1px 边框和 dpr 兼容,多一个人进来都要重新理解这套约定。

viewport 单位:少了一层 JS,但问题没根治

后来社区开始推 vw/vh/vmin/vmax,本质是把 rem 的那层 JS 计算交给浏览器原生处理。思路是对的,但又出现两个现实问题:

  • 字体大小用 vw 会随视口线性缩放,在大屏手机上字可能过大或过小,缺少弹性。
  • 组件级响应还是做不到。 和 rem 一样,vw 依然绑定视口,不是组件自己说了算。

换句话说,从 rem 到 vw,只是”把 JS 能做的事交给浏览器”,但适配的参照系仍然是视口,而不是组件所在容器。

容器查询:组件终于能”自己决定怎么排版”

CSS Container Queries 是近两年真正值得认真对待的新能力。它的核心变化只有一句话:响应式判断的参照系,从页面视口变成了父容器。

用代码理解最快:


/* 1. 先声明这是一个查询容器 */
.card-list {
  container-type: inline-size;
  container-name: cardList;
}

/* 2. 组件根据自身容器宽度响应 */
.card {
  display: grid;
  gap: 1rem;
}

/* 容器宽度大于 480px 时横排 */
@container cardList (min-width: 480px) {
  .card {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* 容器宽度大于 720px 时三列 */
@container cardList (min-width: 720px) {
  .card {
    grid-template-columns: repeat(3, 1fr);
  }
}

这里 .card 组件完全不 care 页面是手机还是桌面,它只 care 自己住在一个多宽的容器里。放在侧边栏就自动一列,放在首页宽区域就自动三列。这就是组件级响应。

浏览器支持方面,Container Queries 在 Chrome 105+、Safari 16+、Firefox 110+ 已经可用,覆盖了绝大多数主流环境。

2026 年的务实组合:Container Queries + clamp() + 少量 vw

完全放弃 rem/vw 也不现实,我的建议是分层处理:

场景 推荐方案 原因
组件内部排版 Container Queries 组件自己决定几列、间距多少
字体大小弹性缩放 `clamp(min, preferred, max)` 不用 JS,纯 CSS 做字体的”弹性区间”
页面级布局 少量 vw + 常规媒体查询 针对整体导航、侧边栏显隐等页面级判断
需要像素级控制 保持 `px`,不做单位转换 边框、阴影等不该参与缩放

clamp() 是这几年最被低估的特性之一:


.title {
  /* 最小 1rem,首选 5vw,最大 2rem */
  font-size: clamp(1rem, 5vw, 2rem);
}

字体随视口自然呼吸,但又不会太小或太大,不再需要 JS 在 DOMContentLoaded 里重算一次根字体。

我实际替换掉的代码

之前一个典型移动端入口文件,用 lib-flexible 的写法大概是这样的:


// main.js
import 'lib-flexible/flexible'

/* 设计稿 750px,所有尺寸用 rem */
.header {
  height: 1rem;       /* 实际 100px 设计稿 */
  padding: 0.3rem;     /* 实际 30px */
  font-size: 0.28rem;  /* 实际 28px */
}

替换后:


/* 容器查询驱动组件响应 */
.product-grid {
  container-type: inline-size;
  container-name: productGrid;
  display: grid;
  gap: clamp(0.75rem, 2vw, 1.5rem);
}

@container productGrid (min-width: 480px) {
  .product-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

@container productGrid (min-width: 720px) {
  .product-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* 字体用 clamp,不再用 rem 做弹性 */
.card-title {
  font-size: clamp(0.9rem, 4vw + 0.1rem, 1.25rem);
}

JS 入口文件里不再引入 lib-flexible,少了一层运行时依赖。

结论

rem 不是不好,它在那个浏览器能力有限的年代是正确解;但 2026 年的今天,Container Queries + clamp() + 少量 vw 是更务实的组合。组件自己响应自己,字体有弹性区间,页面级判断留少量媒体查询,整体心智负担更低。

如果你还在维护 lib-flexible,下一步建议:挑一个卡片列表组件先试点容器查询,跑通再逐步替换,不要一刀切。

评论区

0 条评论

登录后可评论。

查看完整榜单
查看完整榜单
查看完整榜单