Skip to content

Vue 门户网站 PC 与移动端适配方案

本文梳理了 Vue 项目中实现 PC 与移动端适配的两套主流方案——CSS 媒体查询响应式双视图动态切换,涵盖设备检测、状态管理、路由分发、UI 组件库选择及构建工具配置,适用于需要同时支持桌面端和移动端的门户网站项目。

一、核心技术方案概述

针对Vue门户网站PC和移动端适配,目前主流有两种方案:

方案适用场景优点缺点
一套代码 + CSS媒体查询两端布局差异不大,仅样式/位置/显隐不同维护成本低,部署简单交互逻辑差异大时代码臃肿
两套视图 + 动态切换两端UI/交互差异巨大,如导航结构、功能模块完全不同可针对性深度优化,代码隔离清晰开发量相对较大

对于"移动端和PC端的导航目录展示不一样"这一典型需求,当两者导航结构差异不大时,推荐媒体查询;当差异巨大时,推荐动态切换方案。

二、方案一:一套代码 + CSS媒体查询

2.1 核心实现:媒体查询 + 弹性布局

通过CSS媒体查询,根据屏幕宽度改变导航栏的布局和展示方式,实现差异化效果。

vue
<template>
  <div id="app">
    <!-- 顶部导航栏(仅桌面端显示) -->
    <header class="pc-header" v-if="!isMobile">
      <nav class="top-nav">
        <ul class="menu-list">
          <li v-for="item in menuItems" :key="item.id">
            <a :href="item.link">{{ item.title }}</a>
          </li>
        </ul>
      </nav>
    </header>

    <!-- 菜单按钮(仅移动端显示) -->
    <header class="mobile-header" v-if="isMobile">
      <button class="menu-button" @click="toggleDrawer">☰</button>
    </header>

    <!-- 移动端抽屉式菜单 -->
    <div class="drawer" :class="{ open: isDrawerOpen }">
      <div class="drawer-header">
        <span>导航菜单</span>
        <button class="close-button" @click="toggleDrawer">×</button>
      </div>
      <ul class="mobile-menu-list">
        <li v-for="item in menuItems" :key="item.id">
          <a :href="item.link">{{ item.title }}</a>
        </li>
      </ul>
    </div>

    <!-- 遮罩层 -->
    <div
      v-if="isMobile && isDrawerOpen"
      class="overlay"
      @click="closeDrawer"
    ></div>
  </div>
</template>

<script setup>
import { ref } from "vue";
import { useMediaQuery } from "@vueuse/core";

const isMobile = useMediaQuery("(max-width: 768px)");
const isDrawerOpen = ref(false);

const toggleDrawer = () => {
  isDrawerOpen.value = !isDrawerOpen.value;
};
const closeDrawer = () => {
  isDrawerOpen.value = false;
};
</script>

2.2 CSS媒体查询实现布局切换

vue
<style scoped>
/* 移动端优先:默认移动端样式 */
.menu-button {
  display: block;
  background: none;
  border: none;
  font-size: 24px;
  cursor: pointer;
}
.drawer {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  width: 250px;
  background: #fff;
  transform: translateX(-100%);
  transition: transform 0.3s ease;
  z-index: 1000;
}
.drawer.open {
  transform: translateX(0);
}
.overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  z-index: 999;
}
/* PC端调整:通过媒体查询覆盖移动端样式 */
@media (min-width: 769px) {
  .menu-button,
  .drawer,
  .overlay {
    display: none; /* 移动端专属元素全部隐藏 */
  }
  .pc-header {
    display: block;
  }
  .top-nav .menu-list {
    display: flex;
    gap: 24px;
  }
}
</style>

核心思路:移动端优先策略,小屏幕默认垂直单列布局,通过min-width媒体查询重构成PC端多列/水平布局。

三、方案二:两套视图 + 动态切换

适用场景

当PC端和移动端的UI设计差异较大时,可以采用"一套代码库,两套独立视图"的模式,在根组件或路由入口根据设备类型动态选择渲染哪套组件。

想象一下这个场景:产品经理给你看两个设计稿,一个是 PC 端的后台管理系统,布局复杂,侧边导航、顶部工具栏、数据表格、各种图表卡片一应俱全,信息密度极高。另一个是移动端的 H5 页面,设计简洁,以卡片流和列表为主,交互方式也完全不同,比如有大量的滑动操作和底部弹出层。这两个页面的信息架构、交互逻辑和视觉风格天差地别。这时候如果你硬要用一套 HTML 结构和 CSS 媒体查询去 “响应”,代码会变得极其臃肿和难以维护。你可能需要写大量的 display: none 来隐藏 PC 端的复杂元素,又得用 JavaScript 去动态改变交互行为,最后搞出来一个 “四不像”,在 PC 上看着简陋,在手机上又显得笨重。 所以,当 PC 端和移动端的页面功能、交互和视觉设计差异巨大时,强行使用响应式就是给自己挖坑。更合理的做法是:在同一个 Vue 项目里,为 PC 端和移动端分别开发两套独立的页面组件。用户访问同一个 URL(比如 /search),我们根据他使用的设备(是电脑还是手机),动态地加载并渲染对应的那套组件。这样做的好处非常明显:两套代码逻辑清晰,互不干扰,可以针对各自平台的特性进行深度优化。PC 端你可以放心使用 Element Plus 这类 桌面端组件库,构建复杂的交互;移动端则可以引入 Vant 这类轻量级移动端 UI 库,专注于触屏体验。 这种模式特别适合一些 “重 PC、轻移动” 或者 “两端功能侧重点不同” 的项目。比如一个电商后台,PC 端是功能完整的运营台,而移动端可能只是一个简单的订单查询或数据概览页面。它们的核心业务虽然相通,但展现形式和操作路径完全可以独立设计。接下来,我们就聊聊怎么在 Vue 项目里优雅地实现这个方案。

3.1 推荐的项目目录结构

src/
├── views/
│   ├── pc/                    # PC端所有页面组件
│   │   ├── Home/
│   │   ├── About/
│   │   └── index.vue          # PC端根布局
│   ├── mobile/                # 移动端所有页面组件
│   │   ├── Home/
│   │   ├── About/
│   │   └── index.vue          # 移动端根布局
│   └── index.vue              # 入口:设备判断+分发
├── router/
│   ├── pc-routes.ts
│   └── mobile-routes.ts
├── stores/
│   └── device.ts              # Pinia设备状态管理
└── components/
    ├── common/                # 跨端复用组件
    ├── pc/
    └── mobile/

3.2 Pinia设备状态管理

typescript
// stores/device.ts
import { defineStore } from "pinia";
import { computed } from "vue";
import { useMediaQuery } from "@vueuse/core";

export type DeviceType = "pc" | "mobile";

export const useDeviceStore = defineStore("device", () => {
  const isMobile = useMediaQuery("(max-width: 768px)");
  const deviceType = computed<DeviceType>(() =>
    isMobile.value ? "mobile" : "pc",
  );

  return { deviceType };
});

3.3 智能分发入口

vue
<!-- views/index.vue -->
<script setup lang="ts">
import { useDeviceStore } from "@/stores/device";
import PcLayout from "./pc/index.vue";
import MobileLayout from "./mobile/index.vue";

const deviceStore = useDeviceStore();
</script>

<template>
  <div>
    <PcLayout v-if="deviceStore.deviceType === 'pc'" />
    <MobileLayout v-else />
  </div>
</template>

3.4 路由分发(进阶)

typescript
// router/index.ts
import { createRouter, createWebHistory } from "vue-router";
import { pcRoutes } from "./pc-routes";
import { mobileRoutes } from "./mobile-routes";

const router = createRouter({
  history: createWebHistory(),
  routes: [],
});

router.beforeEach(async (to, from, next) => {
  const deviceStore = useDeviceStore();
  const isMobile = deviceStore.deviceType === "mobile";
  // 根据设备动态添加对应路由
  const targetRoutes = isMobile ? mobileRoutes : pcRoutes;
  targetRoutes.forEach((route) => router.addRoute(route));
  next();
});

四、辅助适配工具与UI组件库选择

4.1 视口单位转换工具

推荐使用 postcss-px-to-viewport 插件,在Vite配置中直接使用px进行开发,构建时自动转换为vw单位:

ts
// vite.config.ts
import { defineConfig } from "vite";
import postcssPxToViewport from "postcss-px-to-viewport";

export default defineConfig({
  css: {
    postcss: {
      plugins: [
        postcssPxToViewport({
          unitToConvert: "px",
          viewportWidth: 1920, // PC设计稿宽度
          unitPrecision: 6,
          propList: ["*"],
          viewportUnit: "vw",
          minPixelValue: 1,
        }),
      ],
    },
  },
});

4.2 UI组件库搭配建议

设备端推荐组件库说明
PC端Element Plus / Ant Design Vue成熟的桌面端组件,支持键盘/鼠标事件
移动端Vant轻量级移动端组件库,支持touch手势

若移动端导航需使用Vant且在PC端测试,需引入触摸模拟器:

javascript
import "@vant/touch-emulator"; // 将PC mouse事件转为touch事件

4.3 断点阈值参考

设备类型宽度阈值说明
PC端> 768px桌面端完整布局
平板端768px - 1024px响应式二级断点可选适配
移动端≤ 768px移动端精简布局

五、综合建议与架构参考

5.1 技术选型对比

考量维度媒体查询方案动态切换方案
两端差异程度仅样式/显隐/位置UI/交互/功能完全不同
维护成本中等
开发效率中高
代码复用率中(业务逻辑可抽离复用)
典型场景响应式门户展示页电商后台+H5官网

5.2 双视图动态切换项目完整流程

  1. 设备检测层:使用 useMediaQuery 获得deviceType
  2. 路由分发层:根组件v-if或路由守卫动态加载端特定组件
  3. UI组件层:PC端Element Plus,移动端Vant,common抽离共享逻辑
  4. 适配工具层:postcss-px-to-viewport + flex/Grid弹性布局
  5. 样式层:PC和移动端独立样式文件,基础全局样式跨端共享

5.3 注意事项

  • SSR闪屏处理:设备检测需在根组件beforeMountsetup阶段执行,避免先渲染错误端再切换
  • resize节流:若使用 addEventListener 手动监听 resize,需加入防抖处理,避免频繁触发;若使用 VueUse 的 useMediaQuery,已内置优化,无需额外处理。
  • 移动端viewport设置<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">