vue3+vite 实现移动端自适应


在 Vue3 + Vite 项目中实现移动端自适应,可以通过以下方案实现(附完整配置):

方案一:Viewport 视口方案(推荐)

1. 安装核心依赖

npm install postcss-px-to-viewport -D

2. 配置 vite.config.ts

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import postCssPx from "postcss-px-to-viewport";
export default defineConfig({
    plugins: [vue()],
    css: {
        postcss: {
            plugins: [
                postCssPx({
                    unitToConvert: "px", // 要转换的单位
                    viewportWidth: 750, // 设计稿宽度
                    unitPrecision: 5, // 转换精度
                    propList: ["*"], // 需要转换的属性列表
                    viewportUnit: "vw", // 转换单位
                    fontViewportUnit: "vw", // 字体转换单位
                    selectorBlackList: ["ignore-"], // 忽略类名
                    minPixelValue: 1, // 最小转换值
                    mediaQuery: false, // 媒体查询中是否转换
                    replace: true, // 直接替换值
                    exclude: [/node_modules/], // 排除文件
                    landscape: false, // 是否处理横屏
                }),
            ],
        },
    },
});

3. 基础样式重置

src/assets/css/reset.css

/* 清除默认边距 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* 禁止长按链接与图片弹出菜单 */
a,
img {
    -webkit-touch-callout: none;
}

/* 禁用文字缩放 */
html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}

/* 统一字体 */
body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial;
    line-height: 1.5;
}

4. 添加 viewport meta

index.html<head> 中:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />

方案二:REM 弹性布局

1. 安装依赖

npm install lib-flexible postcss-pxtorem -D

2. 配置 vite.config.ts

// vite.config.ts
import postCssPxToRem from "postcss-pxtorem";
export default defineConfig({
    css: {
        postcss: {
            plugins: [
                postCssPxToRem({
                    rootValue: 75, // 设计稿宽度/10(750px设计稿填75)
                    propList: ["*"],
                    selectorBlackList: ["norem"],
                }),
            ],
        },
    },
});

3. 动态设置 REM

src/main.ts

import "lib-flexible/flexible"; //rem转化的工具
// 推荐使用 lib-flexible 的升级版:amfe-flexible
// import "amfe-flexible";

// 监听窗口变化
window.addEventListener("resize", () => {
    if (document.documentElement.clientWidth > 750) {
        document.documentElement.style.fontSize = "75px";
    } else {
        document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + "px";
    }
});

响应式布局增强技巧

1. 媒体查询补充

/* 横屏适配 */
@media screen and (orientation: landscape) {
    .container {
        padding: 0 10vw;
    }
}

/* 超大屏幕限制 */
@media (min-width: 768px) {
    body {
        max-width: 768px;
        margin: 0 auto !important;
    }
}

2. 1px 边框解决方案

使用 PostCSS 插件:

npm install postcss-write-svg -D

配置 vite.config.ts

require("postcss-write-svg")({
    utf8: false,
});

使用示例:

.border-1px {
    border-bottom: 1px solid transparent;
    background: svg(1px-border param(--color #00ff00)) repeat-x bottom;
    background-size: 100% 1px;
}

最佳实践建议

  1. 设计稿基准
  • 二倍图设计稿使用 750px 宽度
  • 单倍图设计稿使用 375px 宽度
  1. 开发策略
<!-- 在组件中直接使用设计稿尺寸 -->
<div class="box" style="width: 200px; height: 100px"></div>
<!-- 编译后自动转为 26.6667vw 13.3333vw(基于375设计稿) -->
  1. 第三方组件库适配(以 Vant 为例):
// postcss-px-to-viewport 配置中添加
selectorBlackList: ["van-"];

设备测试技巧

  1. Chrome 模拟器:
// 按 F12 打开开发者工具 -> 切换设备工具栏
// 建议测试机型:iPhone SE(320px)、iPhone 12 Pro(390px)、Pixel 5(393px)
  1. 真机调试:
npm run build
npm install -g serve
serve -s dist
# 手机访问电脑IP:5000

两种方案各有优势:
Viewport 方案:更适合现代浏览器,计算逻辑更直观,追求零脚本化;设计稿尺寸不规则或需精确视口控制;项目构建工具为 Vite(配置更简洁
REM 方案:兼容性更好,适合需要支持老旧浏览器的项目;项目集成 Vant、Element 等 UI 框架;设计稿为 375px/750px 等标准尺寸

建议根据项目需求选择,通常 Viewport 方案能满足大部分移动端场景。配置完成后,开发时直接使用设计稿像素值即可实现完美自适应。

两种方性能与兼容性

指标 postcss-pxtorem postcss-px-to-viewport
构建耗时 较低(依赖 CSS 计算) 稍高(需解析视口比例)
运行时性能 需 JS 动态计算根字体 纯 CSS 驱动,无运行时开销
兼容性风险 低(支持老旧设备) 需注意 Android 4.4 以下兼容
UI 框架适配 完美兼容 Vant、Element 等 需手动排除第三方库样式
核心优势 兼容性好、UI 框架友好 无脚本依赖、视口精确控制
劣势 需动态计算根字体 老旧设备兼容性差
适用项目 企业后台、兼容性要求高 现代 H5、活动页
配置复杂度

完整实例

1.安装 npm 包

npm install postcss-pxtorem -D
npm install amfe-flexible -D
# 或
npm install postcss-px-to-viewport -D

2.配置 vue.config.ts

// vue.config.ts
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
import postCssPxToRem from "postcss-pxtorem";
import postCssPx from "postcss-px-to-viewport";
// https://vite.dev/config/
export default defineConfig((config) => {
    // 加载 .env 文件中的环境变量
    const env = loadEnv(config.mode, process.cwd());
    return {
        base: "./",
        //设置别名
        resolve: {
            alias: {
                "@": path.resolve(__dirname, "./src"),
            },
        },
        plugins: [vue()],
        esbuild: {
            // 打包之后删除 console debugger
            // drop: ["console", "debugger"],
        },

        css: {
            postcss: {
                plugins: [
                    postCssPxToRem({
                        rootValue: 75,
                        // rootValue: ({ file }) => (file.includes("vant") ? 37.5 : 75), // 适配 Vant
                        propList: ["*"],
                        selectorBlackList: ["norem"],
                    }),
                    // 二选一,若使用vant,则推荐使用 postCssPxToRem
                    // postCssPx({
                    //     unitToConvert: "px", // 要转换的单位
                    //     viewportWidth: 750, // 设计稿宽度(以iPhone6/7/8为基准)
                    //     unitPrecision: 5, // 转换精度
                    //     propList: ["*"], // 需要转换的属性列表
                    //     viewportUnit: "vw", // 转换单位
                    //     fontViewportUnit: "vw", // 字体转换单位
                    //     selectorBlackList: ["ignore-"], // 忽略类名
                    //     minPixelValue: 1, // 最小转换值
                    //     mediaQuery: false, // 媒体查询中是否转换
                    //     replace: true, // 直接替换值
                    //     exclude: [/node_modules/], // 排除文件
                    //     landscape: false, // 是否处理横屏
                    // }),
                ],
            },
        },
        server: {
            proxy: {
                "/api": {
                    target: env.VITE_QAURL, // 目标服务器地址
                    changeOrigin: true,
                    prependPath: true,
                    secure: false,
                    // rewrite: (path) => path.replace(/^\/api/, ''),// 重写路径,将 /api 替换为空字符串
                },
            },
        },
    };
});

3.配置 main.ts

// main.ts

import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";
import "@/assets/css/reset.scss";
import Vant from "vant";
// 引入vant组件样式
import "vant/lib/index.css";
import "amfe-flexible"; //rem转化的工具
const app = createApp(App);
app.use(Vant);
app.use(createPinia());
app.use(router);
app.mount("#app");

4.重置 css 样式

/* reset.scss */

* {
    margin: 0;
    padding: 0;
    list-style: none;
    box-sizing: border-box;
    text-decoration: none;
    outline: none !important;
    user-select: none;
}

p {
    text-align: center;
    font-family: Source Han Sans MicrosoftYaHei-Bold;
    font-weight: normal;
    font-stretch: normal;
}

/* 禁止图片复制 */
img {
    user-select: none !important;
}

5.添加 viewport meta

index.html<head> 中:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />

踩坑

1.vue3 路由模式为 history,部署之后出现的白屏问题

  1. 配置 vue.config.ts 中的 publicPath

vue.config.ts

module.exports = {
    //根目录
    publicPath: process.env.NODE_ENV === "production" ? "./" : "/",
};

2.配置 vue-router

const router = createRouter({
    //根目录
    history: createWebHistory("/"),
    routes,
});

3.配置 nginx 的 server

location / {
  try_files $uri $uri/ /index.html;
}

vue 官网的说明


文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录