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 布局不同
相关推荐
excel16 分钟前
Vue3 中的双向链表依赖管理详解与示例
前端
谢尔登16 分钟前
【Nest】基本概念
javascript·node.js·express
老华带你飞34 分钟前
机电公司管理小程序|基于微信小程序的机电公司管理小程序设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·微信小程序·小程序·机电公司管理小程序
前端小白从0开始1 小时前
Chrome DevTools高级用法:性能面板内存泄漏排查
前端·chrome·chrome devtools
EveryPossible1 小时前
带有渐变光晕
前端·javascript·css
jojo是只猫1 小时前
Vue 3 开发的 HLS 视频流播放组件+异常处理
前端·javascript·vue.js
卓码软件测评1 小时前
第三方软件登记测试机构:【软件登记测试机构HTML5测试技术】
前端·功能测试·测试工具·html·测试用例·html5
CS Beginner1 小时前
【html】canvas实现一个时钟
前端·html
林烈涛2 小时前
js判断变量是数组还是对象
开发语言·前端·javascript
Komorebi_99992 小时前
Unocss
开发语言·前端