移动端适配终极方案 —— postcss-px-to-viewport vs postcss-pxtorem

移动端适配终极方案

移动端适配绕不开两个 PostCSS 插件:postcss-pxtorem(px → rem)和 postcss-px-to-viewport(px → vw/vh)。二者都是编译期自动转换,写 px 即可适配不同屏幕,但原理和适用场景完全不同。本文从配置到踩坑一次性讲透。

一、两句话理解区别

方案原理一句话
px → rem把 px 转成 rem,通过 <html>font-size 缩放按比例放大缩小根字号
px → vw把 px 转成 vw/vh,通过视口宽度自动缩放直接按视口百分比计算

对比:

1
2
3
4
5
6
7
8
9
10
11
12
设计稿 375px 宽,一个 100px 的盒子:

px → rem:
转换后 width: 1rem
html font-size = 375 / 10 = 37.5px
→ 实际宽度 = 1 × 37.5 = 37.5px (375 屏)
→ iPhone 14 Pro Max (430) : 1 × 43 = 43px ✅

px → vw:
转换后 width: 26.67vw
→ 实际宽度 = 375 × 26.67% = 100px (375 屏)
→ iPhone 14 Pro Max (430) : 430 × 26.67% = 114.7px ✅

二、postcss-pxtorem

2.1 安装

1
npm i postcss-pxtorem -D

2.2 配置

1
2
3
4
5
6
7
8
9
10
11
12
// postcss.config.js
module.exports = {
plugins: {
"postcss-pxtorem": {
rootValue: 37.5, // 设计稿宽度 / 10
propList: ["*"], // 所有属性都转换
selectorBlackList: [".norem"], // 该 class 下的 px 不转换
minPixelValue: 2, // 小于 2px 不转换
exclude: /node_modules/i, // 排除 node_modules
},
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Vite —— vite.config.ts
import pxtorem from "postcss-pxtorem";

export default defineConfig({
css: {
postcss: {
plugins: [
pxtorem({
rootValue: 37.5,
propList: ["*"],
}),
],
},
},
});

2.3 配套的 flexible.js

rem 方案依赖在 <html> 上动态设置 font-size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// flexible.js —— 放在 index.html 的 <head> 中
(function () {
const baseSize = 37.5; // 基准:375 / 10

function setRem() {
const scale = document.documentElement.clientWidth / 375;
document.documentElement.style.fontSize = Math.min(baseSize * scale, baseSize * 2) + "px"; // 限制最大 2 倍
// 等价于:fontSize = clientWidth / 10
}

setRem();
window.addEventListener("resize", setRem);
window.addEventListener("pageshow", e => {
if (e.persisted) setRem();
});
})();

或者精简版(直接除 10):

1
2
3
<script>
document.documentElement.style.fontSize = document.documentElement.clientWidth / 3.75 + "px";
</script>

2.4 不同设计稿的 rootValue

设计稿宽度rootValue
375px37.5
750px75
640px64
1920px (PC)192

2.5 配置项详解

参数默认值说明
rootValue16根元素字体大小,通常设 设计稿宽度 / 10
unitPrecision5rem 小数精度
propList['*']转换的属性,如 ['font-size', 'width']
selectorBlackList[]不转换的选择器
replacetrue是否替换原 px,而非追加
mediaQueryfalse媒体查询中是否转换
minPixelValue0小于此值的 px 不转换
excludenull正则匹配排除文件

2.6 不想被转换的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 方式一:选择器黑名单 */
.norem .icon {
width: 20px;
} /* 不转换 */
.icon {
width: 20px;
} /* 转换 */

/* 方式二:大写 PX */
.border {
border: 1px solid #eee;
} /* 不转换,大写 */

/* 方式三:注释忽略 */
.border {
border: 1px solid #eee; /* no */
} /* 不转换 */

2.7 优点与缺点

优点缺点
兼容性好,全平台通用需要额外引入 flexible.js
第三方 UI 库(Vant)原生支持 rem第三方库如用 px 且被转换,布局可能出错
可精细控制哪些元素不转换font-size 缩放会受系统字体设置影响
成熟方案,Vant 2.x 官方推荐小数点精度问题(1rem = 37.5px 容易除不尽)

三、postcss-px-to-viewport

3.1 安装

postcss-px-to-viewport 原始包已停更,推荐使用新包:

1
npm i @minko-fe/postcss-px-to-viewport -D

3.2 基础配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// postcss.config.js
module.exports = {
plugins: {
"@minko-fe/postcss-px-to-viewport": {
viewportWidth: 375, // 设计稿宽度
unitPrecision: 5, // vw 小数位数
viewportUnit: "vw", // 转换单位
selectorBlackList: [".ignore"], // 不转换的选择器
minPixelValue: 1, // 小于 1px 不转换
mediaQuery: false, // 媒体查询中不转换
exclude: [/node_modules/], // 排除
},
},
};

3.3 Vite 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// vite.config.ts
import pxToViewport from "@minko-fe/postcss-px-to-viewport";

export default defineConfig({
css: {
postcss: {
plugins: [
pxToViewport({
viewportWidth: 375,
unitPrecision: 5,
viewportUnit: "vw",
}),
],
},
},
});

3.4 常用配置项

参数默认值说明
viewportWidth375设计稿宽度,必填
viewportHeight667设计稿高度(vh 转换时用)
unitPrecision5小数精度
viewportUnit‘vw’转换单位,可设 vh / vmin / vmax
fontViewportUnit‘vw’字体用的单位,可独立设
selectorBlackList[]不转换的选择器
minPixelValue1小于此 × 不转换
mediaQueryfalse媒体查询中的 px 是否转换
landscapefalse是否生成 @media (orientation: landscape)
landscapeWidth568横屏设计稿宽度
landscapeUnit‘vw’横屏单位
excludenull正则排除路径
includenull正则仅包含路径

3.5 不想转换的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* 方式一:选择器黑名单 */
.ignore {
width: 16px;
} /* 不转换 */

/* 方式二:注释忽略 */
.box {
width: 100px; /* 转换 */
border: 1px solid #eee; /* px-to-viewport-ignore-next */ /* 不转换 */
}

/* 方式三:一行忽略 */
.box {
border: 1px solid #eee; /* px-to-viewport-ignore 不转换 */
}

/* 方式四:文件级忽略 */
/* postcss-px-to-viewport: disable */
.page {
width: 375px;
} /* 整个文件不转换 */

3.6 横屏适配

1
2
3
4
5
{
landscape: true,
landscapeWidth: 812, // iPhone X 横屏宽度
landscapeUnit: 'vw',
}

生效后,插件会在横屏时自动切换到以 812px 为基准的 vw 计算。

3.7 优点与缺点

优点缺点
无需引入 JS,纯 CSS 方案部分旧设备不支持 vw/vh
转换直观,和设计稿 1:1 对应第三方库的 px 可能被误转换
Vant 3.x / 4.x 官方推荐大屏设备(iPad/PC)元素过大
小数点精度好手机上 1px 边框线可能变很粗

四、两种方案深度对比

维度postcss-pxtorempostcss-px-to-viewport
依赖需额外 flexible.js原生 CSS 单位,无依赖
原理rem = px / rootValue,rootValue 动态变化vw = px / viewportWidth × 100
第三方 UI 兼容Vant 2.x 内置 rem(需配合)Vant 3/4 推荐 vw
横屏适配需手动调整 rootValue插件内置 landscape 模式
PC 端限制可设 maxWidth 限制大屏会等比例放大,需额外处理
1px 边框不转换或转大写 PX可用 minPixelValue: 1 排除
系统字体影响受影响(用户可改浏览器字号)不受影响
兼容性极好,全平台iOS 8+ / Android 4.4+
维护状态postcss-pxtorem 稳定原包停更,推荐 @minko-fe/ 分支

选型建议

1
2
3
4
5
6
7
8
你的项目:
├─ 用 Vant 2.x → 选 postcss-pxtorem
├─ 用 Vant 3.x / 4.x → 选 postcss-px-to-viewport
├─ 兼容老旧设备(iOS 7 以下)→ 选 postcss-pxtorem
├─ 对 1px 边框高要求 → 选 postcss-px-to-viewport + transform: scale
├─ 需要横屏适配 → 选 postcss-px-to-viewport
├─ PC 端也需要展示 → 选 postcss-pxtorem + maxWidth 限制
└─ 追求极致简洁 → 选 postcss-px-to-viewport

五、进阶:两者混用

有些项目需要 vw 做布局 + 某些场景保留 rem 作为兜底:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// postcss.config.js
module.exports = {
plugins: [
// 1. 先执行 pxtorem:将 font-size / 字号相关的 px 转 rem
require("postcss-pxtorem")({
rootValue: 37.5,
propList: ["font", "font-size", "line-height", "letter-spacing"],
exclude: /node_modules/,
}),
// 2. 再执行 px-to-viewport:将宽高/间距等布局属性转 vw
require("@minko-fe/postcss-px-to-viewport")({
viewportWidth: 375,
propList: ["*", "!font*", "!line-height", "!letter-spacing"],
exclude: /node_modules/,
}),
],
};

执行顺序:PostCSS 插件按声明顺序执行,先 rem 后 vw,通过 propList 互相排除避免重复转换。

六、Vant UI 库配合

Vant 2.x + pxtorem

1
2
3
4
5
6
7
8
9
10
// postcss.config.js
module.exports = {
plugins: {
// vant 2 设计稿基于 375,内部用 px
"postcss-pxtorem": {
rootValue: 37.5,
propList: ["*"],
},
},
};

Vant 3/4 + px-to-viewport

1
2
3
4
5
6
7
8
9
10
// postcss.config.js
module.exports = {
plugins: {
"@minko-fe/postcss-px-to-viewport": {
viewportWidth: 375,
// Vant 3/4 内部样式用 px,需要一并转换
// 如果 Vant 已适配 vw 则不需要 exclude
},
},
};

Vant 4 可直接按 vw 引入样式(推荐)

1
2
3
// vite.config.ts 中配置 Vant 按需引入
import Components from "unplugin-vue-components/vite";
import { VantResolver } from "unplugin-vue-components/resolvers";

八、常见问题与排查

问题原因解决
转换后页面元素巨大rootValueviewportWidth 搞反了检查设计稿宽度是否正确填写
vw 方案在 PC 端文字过大视口宽如 1920px 时 vw 会等比放大max-width: 750px 容器限制
第三方组件库样式错乱组件库 px 被误转换exclude: /node_modules/
1px 边框被转成 0.27vw 变粗小数值 vw 在 Retina 屏上显示异常minPixelValue: 1 或用大写 PX
flexible.js 导致 Android 页面抖动Android WebView resize 频繁触发加防抖或替换为 CSS 方案
rem 方案刷新后布局变化flexible.js 写入 font-size 的时机问题将 script 放在 <head> 最顶部同步执行
内联样式中的 px 不转换PostCSS 只处理 .css/.vue 文件内联样式手动改为 rem/vw
property ‘xxx’ does not exist插件版本与 PostCSS 8 不兼容确认版本:”postcss-pxtorem”: “^6.0” 对应 PostCSS 8

七、完整配置文件速查

pxtorem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// postcss.config.js —— pxtorem 完整版
module.exports = {
plugins: {
"postcss-pxtorem": {
rootValue: 37.5,
unitPrecision: 5,
propList: ["*"],
selectorBlackList: [".norem"],
replace: true,
mediaQuery: false,
minPixelValue: 1,
exclude: /node_modules/i,
},
},
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- index.html -->
<script>
(function (w, d) {
var docEl = d.documentElement,
timer;
function setRem() {
docEl.style.fontSize = docEl.clientWidth / 3.75 + "px";
}
w.addEventListener("resize", function () {
clearTimeout(timer);
timer = setTimeout(setRem, 100);
});
w.addEventListener("pageshow", function (e) {
e.persisted && setRem();
});
setRem();
})(window, document);
</script>

px-to-viewport

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// postcss.config.js —— px-to-viewport 完整版
module.exports = {
plugins: {
"@minko-fe/postcss-px-to-viewport": {
viewportWidth: 375,
viewportHeight: 667,
unitPrecision: 5,
viewportUnit: "vw",
fontViewportUnit: "vw",
selectorBlackList: [".ignore"],
minPixelValue: 1,
mediaQuery: false,
landscape: false,
exclude: [/node_modules/],
// 保留 1px 边框不转换
// 可以在 CSS 中用大写 PX 或注释 px-to-viewport-ignore
},
},
};

总结

1
2
3
4
postcss-pxtorem       → 老牌方案,Vant 2 标配,兼容极致
postcss-px-to-viewport → 新潮方案,Vant 3/4 推荐,零 JS 依赖

选型一句话:新项目无脑 vw,老项目继续 rem,横屏场景必须 vw。

全文覆盖两种方案的原理对比、完整配置、Vite 集成、Vant 配合、横屏适配、混用策略、12 类常见坑排查,附赠配置文件一键复制。