移动端视口适配(px → vw)
大约 3 分钟
移动端视口适配(px → vw)
方案:
postcss-mobile-forever(设计宽 375 →vw),不依赖html基准字号 / rem。
设计稿:本项目按 375 × 812(宽 375 参与换算;高 812 仅作布局参考)。
用途:日常开发约定 + 下个项目复用清单。与 rem 方案(如 LegacyAppamfe-flexible)二选一,勿混用。
1. 原理(怎么对上设计稿)
开发时按设计稿标注写 px;构建期插件把 #app 内样式的 px 转成 vw;真机按屏宽缩放。
设计稿量到 44px
→ 源码写 height: 44px
→ 构建后 ≈ height: 11.733vw (44 / 375 × 100)
→ 屏宽 375 时 ≈ 44px(对稿)
→ 屏宽 414 时略放大
→ 屏宽 > 600 时按 maxDisplayWidth 封顶,不再继续涨
公式:
vw值 = 设计稿px / viewportWidth × 100
真机 CSS 像素 ≈ (vw值 / 100) × min(屏宽, maxDisplayWidth)
为何不用基准字号:本方案缩放单位是 视口宽度(vw),不是 rem。html { font-size: 14px } 只是正文默认字号,同样会被转成 vw,不是 rem 根基准。
2. 本仓库关键文件
| 文件 | 职责 |
|---|---|
postcss.config.ts | viewportWidth: 375、maxDisplayWidth: 600、fixed 选择器、exclude |
src/utils/inline-px-to-vw.ts | 行内 :style 用(PostCSS 转不到动态 style) |
uno.config.ts | UnoCSS rem → px(createRemToPxProcessor),再交给 PostCSS 转 vw |
index.html | viewport meta(device-width + 禁缩放 + viewport-fit=cover) |
src/styles/app.less | 全局重置;默认字号写 px |
src/config/layout.ts | 顶栏 / TabBar 高度常量(源码仍为设计稿 px) |
依赖:postcss-mobile-forever、autoprefixer(见 package.json)。
3. 日常怎么写(约定)
| 场景 | 做法 |
|---|---|
Less / CSS / SFC <style> | 直接写设计稿 px(如 width: 120px) |
| UnoCSS 原子类 | 正常使用;由 rem→px→vw 链路处理 |
:style / JS 动态尺寸 | 使用 vw(n),n 为设计稿 px |
| 第三方固定布局(如 VForm) | postcss.config.ts → exclude |
| 1px 发丝线 | 尽量交给插件 border: true;特殊情况单独处理 |
禁止:
- 同时接入
amfe-flexible/postcss-pxtorem(与 vw 方案冲突) - 在
html上设动态fontSize = 屏宽/10当 rem 基准 - 行内样式手写死
px却期望随屏缩放(须走vw()) - 设计稿是 750 宽时在源码写 750 而不 ÷2(本仓约定写 375 标注)
4. 行内 vw() 口径
与 PostCSS 同口径(改一处必须改另一处):
// src/utils/inline-px-to-vw.ts
const idealWidth = 375 // = postcss viewportWidth
const maxWidth = 600 // = postcss maxDisplayWidth
// 输出示例:min(3.2vw, 19.2px)
export default function vw(n: number) { /* ... */ }
<div :style="{ marginTop: vw(12) }" />
5. fixed / 弹层
position: fixed 的包含块有时不是 #app。新增全屏浮层、自定义底栏时,把选择器加入 postcss.config.ts 的 rootContainingBlockSelectorList,然后 重启 dev(该文件不支持热更新)。
当前已登记:.van-tabbar、.van-popup 及四向弹出修饰类。
6. 安全区(与 vw 无关,App 必做)
- viewport 含
viewport-fit=cover - 顶栏:
padding-top: env(safe-area-inset-top)(见App.vue) - 底栏:Vant
safe-area-inset-bottom或env(safe-area-inset-bottom)
7. 自测清单
- 写
width: 375px块 → 真机接近 整屏宽(未超 max) - 写
height: 44px→ 375 宽设备上量约 44 - 桌面拉宽到 > 600 → 尺寸不再继续放大(封顶)
- 改
viewportWidth/maxDisplayWidth后 重启,再验 1~3 - 行内
:style用vw()与样式表尺寸观感一致
8. 下个项目复用步骤
- 定设计宽:仅一个数(本方案推荐 375;若稿为 750,标注 ÷2 再写代码)
- 安装:
pnpm add -D postcss-mobile-forever autoprefixer - 复制
postcss.config.ts:改viewportWidth/maxDisplayWidth/exclude/ fixed 列表 - viewport meta:
width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover - 复制
inline-px-to-vw.ts:idealWidth/maxWidth与 PostCSS 对齐 - (可选)UnoCSS:
createRemToPxProcessor,避免原子类 rem 与 vw 打架 - 安全区 + 业务写 px;动态尺寸走
vw(n) - 按 §7 自测
最小文件清单:postcss.config.* + viewport meta + vw() 工具(数字对齐)。
9. 与 rem 方案对照(选型)
| 本方案 px→vw | rem(flexible + pxtorem) | |
|---|---|---|
| 根字号 | 不需要 | 必须动态设 html.fontSize |
| 设计稿 | 375 写 375 | 常 750 + 双 rootValue(业务 / Vant) |
| 大屏 | maxDisplayWidth 封顶 | 需自行限制 rem |
| 行内 | vw() | 自行转 rem |
| 适用 | 375 稿、Vant 4、本模板 | 历史 750 稿项目 |
本仓库设计稿为 375×812 → 固定采用本方案,不迁移 rem。
变更记录
- 2026-07-17:成文;明确保留
postcss-mobile-forever,不以 LegacyApp rem 替换;补充复用步骤与自测