答案先放在最前面:用 uni-app 做多端适配时,默认单位应该用 rpx,不建议把 rem 当成跨端单位。rem 依赖根元素的字号,只有 H5 端才走浏览器的解析规则;小程序端和 App 端的 WebView 对它的支持并不一致,一套样式可能在浏览器里刚好,到了手机上就整体放大或缩小。

之所以会纠结,是因为很多人在 Web 端习惯了 rem 加动态根字号的做法,转成 uni-app 工程时想直接复用。问题在于 uni-app 要同时编译到 H5、微信小程序、App 等多个目标,而 rpx 是框架层面专门为此设计的响应式单位,它的基准是屏幕宽度:750rpx 等于屏幕的可视宽度,换算由框架在编译或运行时完成,不需要你自己写任何插值逻辑。
本文的范围限定在使用 vue 语法的 uni-app 工程(HBuilderX 或 CLI 创建均可),讨论三种单位的适用边界、H5 端确实要上 rem 时的配置写法,以及换单位之后最容易复发的三类失败。文中所有命令与配置都基于通用行为,未在真机执行的部分会明确标注。
先看结论
- 默认用 rpx:它以屏幕宽度为基准,由框架换算,是 uni-app 官方在多端场景下推荐的响应式单位。
- rem 只用于 H5:rem 依赖根字号,小程序与 App 端的行为不可控,跨端项目不要指望它统一表现。
- 真要在 H5 用 rem,需要两件东西:一个把 px 转成 rem 的编译插件,一段在启动时计算根字号的逻辑。
- 三个高频坑:单位混用导致的尺寸漂移、动态根字号波及第三方组件、1px 边框在高清屏上丢失。
单位的具体换算规则记不清时,对照 uni-app 中文文档 里的尺寸单位一节最快,那里列的是官方文档口径。
一、先看清三套单位的换算基准
把问题定死:同一个 100 单位的元素,在 iPhone SE 和 iPad 上分别是多大。
rpx 的基准是屏幕宽度。750rpx 恒等于屏幕宽度,在 375pt 宽的设备上 1rpx 约等于 0.5 个逻辑像素,在 768pt 宽的设备上 1rpx 约等于 1.024 个逻辑像素。也就是说元素占屏比例不变,这正是多端适配想要的效果。
px 是框架处理的静态像素。它不做响应式缩放,写 100px 就在各端渲染成大致相同的物理尺寸,屏幕越大占比越小。适合 Logo、头像这类不该被拉伸的元素。
rem 的基准是根元素字号。浏览器里 html 的 font-size 默认是 16px,所以 1rem 等于 16px。它之所以在 Web 端流行,是因为只要改一处根字号,整页按比例缩放。但这个前提在小程序端不成立——小程序不走浏览器的根字号继承,App 端取决于 WebView 的实现版本。
三者对比下来结论很清楚:需要"占屏比例恒定"用 rpx,需要"绝对稳定"用 px,需要"整页缩放"且只在 H5 端发布才考虑 rem。
二、什么时候才真的需要考虑 rem
判断标准只有一条:你的项目是否只在 H5 端发布。
如果是投放落地页、嵌入第三方 App 的活动页,整版等比缩放确实更省事,改一处根字号就能让所有元素同步缩放,这时候用 rem 是合理的。
如果是 H5 加小程序加 App 的多端项目,答案是不要用。因为你会被迫为不同端维护两套单位体系,一旦有一处漏改,线上就是"某几个页面的按钮比别处大一圈"这种难查的问题。
还有一种折中场景:项目主要跑小程序,但 H5 端要与一个已上线的 Web 页保持视觉一致,那个 Web 页原本就是 rem 体系。这种情况下正确的做法是在 H5 端单独启用转换,而不是把整个工程的单位推倒重来。

三、H5 端上 rem 的两步配置
如果你的结论是"只在 H5 用",那需要安装一个编译期插件把样式里的 px 转成 rem,并在启动时算根字号。
第一步,安装并配置转换插件:
npm install postcss-pxtorem --save-dev
第二步,在 postcss.config.js 里写死换算基准:
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 37.5, // 设计稿 375 宽时,1rem = 37.5px
propList: ['*'],
selectorBlackList: [/^html$/, /^\.no-rem/],
minPixelValue: 2 // 小于等于 1px 的值不转换,避免边框丢失
}
}
}
这里的 rootValue 必须与设计稿宽度对应:设计稿 375 写 37.5,设计稿 750 写 75。selectorBlackList 的作用是把 html 和不参与转换的类排除掉,否则根字号自己被转成 rem 会形成循环。
第三步,在入口里根据屏幕宽度设置根字号:
// App.vue 的 onLaunch 中,仅 H5 端生效
// #ifdef H5
function setRemUnit() {
const width = document.documentElement.clientWidth
document.documentElement.style.fontSize = (width / 10) + 'px'
}
setRemUnit()
window.addEventListener('resize', setRemUnit)
// #endif
这段逻辑的含义是把屏幕宽度十等分,1rem 等于十分之一屏宽。写上 #ifdef H5 条件编译非常重要——没有它,这段代码到了小程序端会因为 document 未定义而直接报错。
浏览器 Prefers Color Scheme 之类的细节不影响本流程。CSS 的长度单位体系本身较大,需要系统过一遍可以看 CSS 教程,重点看单位与取值那一章。
四、三类常见失败与它们的适用边界
坑一:单位混用导致的尺寸漂移。 同一个组件里一部分写 rpx、一部分写 px,在窄屏上看起来没问题,换到平板就错位。排查方式是全局搜索样式文件里的单位,同一个组件内部保持单一单位,只有当某种元素明确不该缩放时才破例。
坑二:动态根字号波及第三方组件。 给 html 设了根字号之后,组件库内部那些用 rem 写的尺寸会跟着缩放。如果你的页面里混入了非本工程的 UI 组件,可能出现"弹窗比页面大一号"。解决办法是把第三方组件的根节点加进 selectorBlackList,或者给它们单独包一层用 px 的容器。
坑三:高清屏上的 1px 边框丢失。 px 转 rem 会有精度损失,0.5px 级别的边框换算后可能被舍成 0,表现为边框时有时无。配置 minPixelValue 就是为了避开这个场景;真要实现极细边框,用 transform: scaleY(0.5) 更可靠。
排错时的验证顺序(预期结果,未在本机执行):
- 检查:用 Chrome 设备模拟器切到 iPhone SE,看目标元素的计算宽度是否符合屏幕占比;
- 运行:执行一次完整的
npm run build:h5,确认插件没有把html自身的字号也转换掉; - 修复:发现根字号异常时,先检查
selectorBlackList是否写成了字符串而不是正则; - 预期:同一元素在 375 宽与 768 宽下的屏占比差值不超过百分之一,边框均未消失。

总结
uni-app 的多端适配不需要发明新方案:默认按屏幕宽度走 rpx,绝对尺寸走 px,rem 只在纯 H5 项目里当作整页缩放手段。
带走的四条:
- 先分清有没有多端发布需求,再决定要不要上 rem;
- 上 rem 必须同时配通编译插件与根字号逻辑,缺一样就不会生效;
rootValue与设计稿宽度对齐,这是配置里最容易填错的一个数;- 第三方组件要显式排除,否则会被根字号连带缩放。
下一步建议把单位的取舍写成团队约定,并在代码评审里检查新组件是否混用了单位。
常见问题
Q:uni-app 里rpx 和 px 怎么选?
A:占屏比例要恒定的元素用 rpx,比如卡片宽度、间距;不希望被拉伸的元素用 px,比如头像、分割线。同一个组件内部尽量只用一种。
Q:为什么配置了 postcss-pxtorem 却没有任何变化?
A:先确认两件事:配置是否生效于当前编译目标(部分工程 H5 端使用独立的 postcss 配置),以及书写的值是否小于 minPixelValue。小于阈值的值会被跳过转换。
Q:rem 在微信小程序里为什么没效果?
A:因为 rem 依赖根元素字号,而小程序不使用浏览器的根字号继承体系。小程序端请一律使用 rpx 或 px。
Q:1px 边框在高清屏上消失了怎么修?
A:把这类值排除在转换之外(minPixelValue 或黑名单),再改用 transform: scaleY(0.5) 绘制,避免依赖亚像素渲染。
Q:设计稿是 750 宽,rootValue 应该填多少?
A:填 75。通用算法是 rootValue 等于设计稿宽度除以 10,前提是入口里的根字号也按屏幕宽度十等分来算。
延伸学习
想把这个话题往下沉两级,可以按这个顺序来:
- 想补齐前端基础里的尺寸体系,Web前端入门扫盲课程 可以跟一遍;
- 读一篇相关笔记 探索UniApp:一次开发,多端应用,看别人怎么组织多端工程;
- 想验一段换算逻辑,用 JavaScript 在线运行 跑一遍最快。

TRAE-AI编程



