pc和移动页面切换的两种基本方案对比

以下是路由切换方案的完整代码实现,补充了之前省略的部分:

一、完整目录结构

plaintext

bash 复制代码
src/
├── router/
│   ├── index.js           # 主路由配置
│   ├── mobileRoutes.js    # 移动端路由
│   └── pcRoutes.js        # PC端路由
├── views/
│   ├── mobile/            # 移动端视图
│   │   ├── Home.vue
│   │   ├── Product.vue
│   │   └── Cart.vue
│   └── pc/                # PC端视图
│       ├── Home.vue
│       ├── Product.vue
│       └── Cart.vue
├── utils/
│   ├── device.js          # 设备检测工具
│   └── storage.js         # 本地存储工具
├── App.vue
└── main.js                # 入口文件

二、核心文件实现

1. 设备检测工具(utils/device.js

javascript

javascript 复制代码
// 检测设备类型
export function detectDevice() {
  // 方案1: 通过User-Agent检测
  const userAgent = navigator.userAgent.toLowerCase();
  const isMobile = /mobile|android|iphone|ipad|phone/i.test(userAgent);
  
  // 方案2: 通过屏幕尺寸检测(处理平板等特殊情况)
  const isSmallScreen = window.innerWidth < 768;
  
  return isMobile || isSmallScreen ? 'mobile' : 'pc';
}

// 监听屏幕变化
export function setupDeviceWatcher(callback) {
  const updateDevice = () => {
    const device = detectDevice();
    callback(device);
  };

  window.addEventListener('resize', updateDevice);
  updateDevice(); // 初始化检测

  // 返回清理函数
  return () => window.removeEventListener('resize', updateDevice);
}

2. 移动端路由(router/mobileRoutes.js

javascript

javascript 复制代码
export const mobileRoutes = [
  {
    path: '/',
    name: 'MobileHome',
    component: () => import('@/views/mobile/Home.vue'),
    meta: { device: 'mobile' }
  },
  {
    path: '/product/:id',
    name: 'MobileProduct',
    component: () => import('@/views/mobile/Product.vue'),
    meta: { device: 'mobile' }
  },
  {
    path: '/cart',
    name: 'MobileCart',
    component: () => import('@/views/mobile/Cart.vue'),
    meta: { device: 'mobile', requiresAuth: true }
  },
  {
    path: '/login',
    name: 'MobileLogin',
    component: () => import('@/views/mobile/Login.vue'),
    meta: { device: 'mobile' }
  }
];

3. PC 端路由(router/pcRoutes.js

javascript

javascript 复制代码
export const pcRoutes = [
  {
    path: '/',
    name: 'PcHome',
    component: () => import('@/views/pc/Home.vue'),
    meta: { device: 'pc' }
  },
  {
    path: '/product/:id',
    name: 'PcProduct',
    component: () => import('@/views/pc/Product.vue'),
    meta: { device: 'pc' }
  },
  {
    path: '/cart',
    name: 'PcCart',
    component: () => import('@/views/pc/Cart.vue'),
    meta: { device: 'pc', requiresAuth: true }
  },
  {
    path: '/login',
    name: 'PcLogin',
    component: () => import('@/views/pc/Login.vue'),
    meta: { device: 'pc' }
  }
];

4. 主路由配置(router/index.js

javascript

javascript 复制代码
import { createRouter, createWebHistory } from 'vue-router';
import { mobileRoutes } from './mobileRoutes';
import { pcRoutes } from './pcRoutes';
import { detectDevice } from '@/utils/device';

// 创建基础路由(公共路由)
const commonRoutes = [
  {
    path: '/404',
    name: 'NotFound',
    component: () => import('@/views/NotFound.vue')
  },
  {
    path: '/:pathMatch(.*)*',
    redirect: '/404'
  }
];

const router = createRouter({
  history: createWebHistory(),
  routes: commonRoutes // 初始只包含公共路由
});

// 初始化路由
export function initRouter() {
  const device = detectDevice();
  const routes = device === 'mobile' ? mobileRoutes : pcRoutes;
  
  // 清空现有路由(保留commonRoutes)
  router.matcher = createRouter({
    history: createWebHistory(),
    routes: commonRoutes
  }).matcher;
  
  // 添加设备专属路由
  routes.forEach(route => {
    router.addRoute(route);
  });
  
  return device;
}

// 路由守卫:防止设备与路由不匹配
router.beforeEach((to, from, next) => {
  const currentDevice = detectDevice();
  
  // 检查目标路由是否存在且匹配当前设备
  const isRouteExists = router.getRoutes().some(
    route => route.name === to.name
  );
  
  if (!isRouteExists) {
    // 路由不存在,重定向到对应设备的首页
    const homeRoute = currentDevice === 'mobile' 
      ? { name: 'MobileHome' } 
      : { name: 'PcHome' };
    next(homeRoute);
  } else {
    next();
  }
});

export default router;

5. 入口文件(main.js

javascript

ini 复制代码
import { createApp } from 'vue';
import App from './App.vue';
import router, { initRouter } from './router';
import { setupDeviceWatcher } from './utils/device';

// 初始化路由
const currentDevice = initRouter();

const app = createApp(App);
app.use(router);
app.mount('#app');

// 监听设备变化,必要时刷新页面
setupDeviceWatcher(newDevice => {
  if (newDevice !== currentDevice) {
    // 简单方案:刷新页面
    window.location.reload();
    
    // 高级方案:动态替换路由(需更复杂实现)
    // initRouter();
    // router.push({ name: newDevice === 'mobile' ? 'MobileHome' : 'PcHome' });
  }
});

三、关键实现说明

1. 路由动态管理

  • 初始化:在应用启动时根据设备类型加载对应路由
  • 重置机制 :通过替换 router.matcher 彻底清除旧路由
  • 守卫检查:确保用户不会访问与设备不匹配的路由

2. 设备检测策略

  • 双重检测:结合 User-Agent 和屏幕尺寸
  • 响应式监听:监听窗口变化,支持设备旋转时的路由更新

3. 组件实现示例(移动端首页)

vue

xml 复制代码
<!-- views/mobile/Home.vue -->
<template>
  <div class="mobile-home">
    <!-- 移动端专属布局 -->
    <header class="mobile-header">
      <h1>移动版首页</h1>
      <button @click="goToCart">购物车 ({{ cartCount }})</button>
    </header>
    
    <main class="mobile-content">
      <ProductList :products="products" />
    </main>
    
    <footer class="mobile-footer">
      <nav>
        <a href="/">首页</a>
        <a href="/category">分类</a>
        <a href="/cart">购物车</a>
        <a href="/profile">我的</a>
      </nav>
    </footer>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import ProductList from '@/components/mobile/ProductList.vue';

export default {
  components: {
    ProductList
  },
  setup() {
    const cartCount = ref(0);
    const products = ref([]);
    
    onMounted(() => {
      // 获取商品列表和购物车数据
      fetchProducts();
      updateCartCount();
    });
    
    const goToCart = () => {
      router.push({ name: 'MobileCart' });
    };
    
    return {
      cartCount,
      products,
      goToCart
    };
  }
};
</script>

四、补充说明

  1. 错误处理

    • 添加了 404 页面和路由不存在时的重定向逻辑
    • 路由守卫确保用户不会访问与设备不匹配的页面
  2. 性能优化

    • 使用路由懒加载(() => import(...))减少初始加载时间
    • 设备类型检测结果可缓存到 localStorage 避免重复计算
  3. 用户体验

    • 提供了设备旋转时的处理方案(刷新页面或动态更新路由)

    • 路由切换时可添加过渡动画减少白屏感

这个方案实现了严格的设备隔离,确保只有对应端的代码会被加载和执行,适合两端功能差异较大的应用场景。

很好,把组件切换也加进去,两者整理下再输出,做下对比

以下是基于 Vue 的路由切换组件切换两种适配方案的完整对比,包含目录结构、实现代码、优缺点分析:

一、路由切换方案

1. 目录结构

plaintext

bash 复制代码
src/
├── router/
│   ├── index.js           # 主路由配置
│   ├── mobileRoutes.js    # 移动端路由
│   └── pcRoutes.js        # PC端路由
├── views/
│   ├── mobile/            # 移动端视图
│   │   ├── Home.vue
│   │   └── Product.vue
│   └── pc/                # PC端视图
│       ├── Home.vue
│       └── Product.vue
├── utils/
│   ├── device.js          # 设备检测工具
│   └── storage.js         # 本地存储工具
└── main.js                # 入口文件

2. 核心代码

javascript

javascript 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { mobileRoutes } from './mobileRoutes'
import { pcRoutes } from './pcRoutes'
import { detectDevice } from '@/utils/device'

const router = createRouter({
  history: createWebHistory(),
  routes: [] // 动态加载
})

export function initRouter() {
  const device = detectDevice()
  const routes = device === 'mobile' ? mobileRoutes : pcRoutes
  
  // 重置路由
  router.matcher = createRouter({
    history: createWebHistory()
  }).matcher
  
  routes.forEach(route => router.addRoute(route))
  return device
}

// main.js
import { initRouter } from './router'

// 初始化前完成路由设置
initRouter()

createApp(App).use(router).mount('#app')

二、组件切换方案

1. 目录结构

plaintext

bash 复制代码
src/
├── router/
│   └── index.js           # 统一路由配置
├── views/
│   ├── Home.vue           # 统一入口视图
│   └── Product.vue        # 统一入口视图
├── components/
│   ├── DeviceDetector.vue # 设备检测组件
│   ├── mobile/            # 移动端组件
│   └── pc/                # PC端组件
├── utils/
│   └── device.js          # 设备检测工具
└── main.js                # 入口文件

2. 核心代码

javascript

javascript 复制代码
// router/index.js
const routes = [
  {
    path: '/',
    component: () => import('@/views/Home.vue')
  },
  {
    path: '/product/:id',
    component: () => import('@/views/Product.vue')
  }
]

// DeviceDetector.vue
<template>
  <component :is="currentComponent" />
</template>

<script>
import { defineComponent, ref, onMounted } from 'vue'
import { detectDevice } from '@/utils/device'

export default defineComponent({
  props: {
    mobileComponent: { type: Object, required: true },
    pcComponent: { type: Object, required: true }
  },
  setup(props) {
    const currentComponent = ref(null)
    
    onMounted(() => {
      const device = detectDevice()
      currentComponent.value = device === 'mobile' 
        ? props.mobileComponent 
        : props.pcComponent
    })
    
    return { currentComponent }
  }
})
</script>

// views/Home.vue
<template>
  <DeviceDetector
    :mobileComponent="mobileHome"
    :pcComponent="pcHome"
  />
</template>

<script>
import DeviceDetector from '@/components/DeviceDetector.vue'
import MobileHome from '@/components/mobile/Home.vue'
import PcHome from '@/components/pc/Home.vue'

export default {
  components: {
    DeviceDetector
  },
  setup() {
    return {
      mobileHome: MobileHome,
      pcHome: PcHome
    }
  }
}
</script>

三、方案对比

维度 路由切换方案 组件切换方案
核心逻辑 在路由初始化时决定加载哪套路由 在组件渲染时决定显示哪个子组件
切换时机 路由导航前(无组件渲染) 组件挂载后(可能有短暂渲染)
资源加载 仅加载当前设备所需资源 可能同时加载多端资源
用户体验 切换需刷新页面(有白屏) 平滑切换(无感知)
适用场景 两端功能差异大,需严格资源隔离 两端功能相似,仅 UI 布局不同
相关推荐
烛阴27 分钟前
让你的Python并发飞起来:多线程开发实用技巧大全
前端·python
旺代29 分钟前
Vue3中的v-model、computed、watch
前端
excel1 小时前
微信小程序鉴权登录详解 —— 基于 wx.login 与后端 openid 换取流程
前端
Gazer_S1 小时前
【前端隐蔽 Bug 深度剖析:SVG 组件复用中的 ID 冲突陷阱】
前端·bug
蓝婷儿2 小时前
每天一个前端小知识 Day 7 - 现代前端工程化与构建工具体系
前端
mfxcyh3 小时前
npm下载离线依赖包
前端·npm·node.js
waterHBO3 小时前
01 ( chrome 浏览器插件, 立马翻译), 设计
前端·chrome
江城开朗的豌豆3 小时前
Vue组件CSS防污染指南:让你的样式乖乖“宅”在自家地盘!
前端·javascript·vue.js
江城开朗的豌豆3 小时前
Vue组件花式传值:祖孙组件如何愉快地聊天?
前端·javascript·vue.js
浩男孩4 小时前
【🍀新鲜出炉 】十个 “如何”从零搭建 Nuxt3 项目
前端·vue.js·nuxt.js