1、问题现象
在微信小程序开发中,你可能会遇到这样一个奇怪的现象:
/* ❌ 不生效 */
@media (min-width: 400px) {
.some-element {
top: 920rpx;
}
}
/* ✅ 生效 */
@media (min-width: 400px) {
.some-element {
top: 460px;
}
}
两段代码唯一的区别是单位——rpx 和 px。而在普通 CSS 规则里,rpx 明明是可以正常使用的,为什么放进 @media 块里就失效了?
2、根本原因:rpx 是运行时单位,不是原生 CSS 单位
理解这个问题,需要先弄清楚 rpx 的本质。
2.1、rpx 的工作机制
rpx(responsive pixel)是微信小程序独创的相对单位,规定:
750rpx = 屏幕总宽度
以 iPhone 6/7/8(375px 逻辑宽)为基准:
- 1rpx = 375 / 750 = 0.5px
- 750rpx = 375px(满屏宽)
这个转换不是由 WebView 的 CSS 引擎完成的,而是由微信小程序的运行时(Runtime)在样式加载前预处理的。微信会扫描 WXSS 文件,把所有 rpx 值按当前设备屏幕宽度换算成 px,再交给 WebView 渲染。
2.2、@media 块为什么是例外
@media 查询由 WebView 的原生 CSS 引擎直接解析和执行,这一层在微信运行时的 rpx 预处理之外。
执行顺序如下:
WXSS 文件
↓
微信运行时预处理(rpx → px 转换)
↓ ↑ 只处理普通规则
↓ × 不处理 @media 块内的属性值
↓
WebView 原生 CSS 引擎
↓
渲染到界面
当 CSS 引擎解析到 @media 块内的 top: 920rpx 时,rpx 已经不在它的认知范围内——它是一个未知单位,会被直接忽略,属性不生效。
3、数值对照验证
以标准 375px 宽设备为例,验证一下:
| rpx 值 | 换算公式 | 等价 px |
|---|---|---|
| 920rpx | 920 × 0.5 | 460px |
| 750rpx | 750 × 0.5 | 375px |
| 100rpx | 100 × 0.5 | 50px |
这也解释了为什么用 460px 可以生效——它正是 920rpx 在 375px 屏上的真实像素值。
4、解决方案
方案一:直接换算成 px(适合固定值场景)
@media (min-width: 400px) {
.some-element {
top: 460px; /* 原 920rpx,按 375px 基准换算 */
}
}
缺点:固定 px 值在宽屏设备上不会等比缩放,失去了 rpx 的响应式特性。
方案二:改用 vw 单位(推荐)
vw(viewport width)同样基于屏幕宽度,与 rpx 的设计理念一致,且是 WebView 原生支持的 CSS 单位,在 @media 块里完全可用。
换算公式:
N rpx = N / 750 * 100 vw
示例:
@media (min-width: 400px) {
.some-element {
top: 122.67vw; /* 920 / 750 * 100 ≈ 122.67vw */
}
}
这样在不同宽度的设备上,top 值会随屏幕宽度等比变化,行为和 rpx 一致。
方案三:使用 CSS 变量 + calc 兜底
如果需要在媒体查询外也保持一致性,可以借助 CSS 变量:
page {
--tip-top: 122.67vw; /* 全局用 vw 替代 rpx */
}
.some-element {
top: var(--tip-top);
}
@media (min-width: 500px) {
.some-element {
top: 460px; /* 宽屏下固定值 */
}
}
5、总结
| 场景 | rpx | px | vw |
|---|---|---|---|
| 普通 CSS 规则 | ✅ | ✅ | ✅ |
| @media 属性值 | ❌ | ✅ | ✅ |
| 随屏幕等比缩放 | ✅ | ❌ | ✅ |
核心结论:
@media块内的属性值,不要使用rpx,推荐使用vw(保持响应式)或px(固定值场景)。
这不是 bug,而是微信小程序运行时架构决定的:rpx 的预处理发生在 WXSS 加载阶段,早于 WebView 的 CSS 解析,因此无法覆盖到 @media 内部的属性值。
参考:微信小程序官方文档 · WXSS · 尺寸单位