PC端和H5端适配 [Vue3]

Marimo_z
2025-04-27 / 0 评论 / 15 阅读 / 正在检测是否收录...

一、核心适配方案

1. 响应式布局体系

推荐方案:使用 postcss-px-to-viewport + 媒体查询 + Flex/Grid 布局

// postcss.config.js
module.exports = {
  plugins: {
    'postcss-px-to-viewport': {
      viewportWidth: 375, // 移动端基准
      unitPrecision: 5,
      viewportUnit: 'vw',
      selectorBlackList: ['.ignore-', '.pc-'], // PC端样式黑名单
      minPixelValue: 1,
      mediaQuery: false,
      exclude: [/node_modules/]
    }
  }
}

媒体查询基准点

/* 移动端优先样式 */
.container {
  padding: 10px;
}

/* PC端适配 */
@media screen and (min-width: 1200px) {
  .container {
    width: 1200px;
    margin: 0 auto;
  }
  
  /* 禁用vw转换 */
  .pc-disable-vw {
    font-size: 16px !important; /* 使用固定单位 */
  }
}

2. 多入口架构

// vue.config.js
module.exports = {
  pages: {
    mobile: {
      entry: 'src/mobile/main.js',
      template: 'public/mobile.html',
      filename: 'mobile.html'
    },
    web: {
      entry: 'src/web/main.js',
      template: 'public/web.html',
      filename: 'web.html'
    }
  },
  chainWebpack: config => {
    // 移动端打包配置
    config.plugin('define').tap(args => {
      args[0]['process.env'].PLATFORM = '"mobile"'
      return args
    })
  }
}

3. 动态组件加载系统

// components/AdaptiveComponent.vue
<script setup>
import { computed } from 'vue'
import { useDevice } from '@/composables/useDevice'

const { isMobile } = useDevice()

const componentMap = {
  header: {
    mobile: () => import('@/components/mobile/Header.vue'),
    web: () => import('@/components/web/Header.vue')
  }
}

const props = defineProps({
  componentName: String
})

const dynamicComponent = computed(() => {
  return componentMap[props.componentName][isMobile.value ? 'mobile' : 'web']
})
</script>

<template>
  <component :is="dynamicComponent" />
</template>

二、设备检测与路由系统

1. 精准设备检测

// composables/useDevice.js
import { ref, onMounted } from 'vue'

export function useDevice() {
  const isMobile = ref(false)
  
  const checkDevice = () => {
    const ua = navigator.userAgent
    const rules = [
      /Android/i,
      /webOS/i,
      /iPhone/i,
      /iPad/i,
      /iPod/i,
      /BlackBerry/i,
      /Windows Phone/i
    ]
    isMobile.value = rules.some(rule => ua.match(rule))
    
    // 窗口尺寸二次验证
    if (window.innerWidth >= 1200) isMobile.value = false
  }

  onMounted(() => {
    checkDevice()
    window.addEventListener('resize', checkDevice)
  })

  return { isMobile }
}

2. 智能路由系统

// router/index.js
import { createRouter } from 'vue-router'

const createPlatformRouter = (isMobile) => {
  return createRouter({
    routes: isMobile ? [
      { path: '/', component: () => import('@/views/mobile/Home.vue') },
      { path: '/detail', component: () => import('@/views/mobile/Detail.vue') }
    ] : [
      { path: '/', component: () => import('@/views/web/Home.vue') },
      { path: '/detail', component: () => import('@/views/web/Detail.vue') }
    ]
  })
}

export const router = createPlatformRouter(useDevice().isMobile)

三、UI 框架混合方案

1. 按需加载策略

// src/plugins/element.js (PC端)
import { ElButton, ElInput } from 'element-plus'

export default {
  install(app) {
    app.use(ElButton)
    app.use(ElInput)
  }
}

// src/plugins/vant.js (移动端)
import { Button, Field } from 'vant'

export default {
  install(app) {
    app.use(Button)
    app.use(Field)
  }
}

2. 动态框架加载

// main.js
import { createApp } from 'vue'
import { useDevice } from './composables/useDevice'

const { isMobile } = useDevice()
const app = createApp(App)

if (isMobile.value) {
  import('./plugins/vant').then(module => {
    module.default.install(app)
  })
} else {
  import('./plugins/element').then(module => {
    module.default.install(app)
  })
}

四、高级适配技巧

1. REM 动态计算(备用方案)

// utils/rem.js
const setRem = () => {
  const baseSize = 37.5 // 设计稿375宽时,1rem=37.5px
  const scale = document.documentElement.clientWidth / 375
  document.documentElement.style.fontSize = 
    `${Math.min(scale, 2) * baseSize}px`
}

window.addEventListener('resize', setRem)
setRem()

2. 触摸事件优化

// directives/touch.js
export const touchDirective = {
  mounted(el, binding) {
    let startX, startY
    const threshold = 10

    el.addEventListener('touchstart', e => {
      startX = e.touches[0].clientX
      startY = e.touches[0].clientY
    })

    el.addEventListener('touchend', e => {
      const diffX = e.changedTouches[0].clientX - startX
      const diffY = e.changedTouches[0].clientY - startY
      
      if (Math.abs(diffX) > threshold || Math.abs(diffY) > threshold) {
        binding.value({ diffX, diffY })
      }
    })
  }
}

五、构建优化策略

1. 条件编译指令

// babel.config.js
module.exports = {
  plugins: [
    ['@babel/plugin-proposal-decorators', { legacy: true }],
    process.env.PLATFORM === 'mobile' 
      ? ['import', { libraryName: 'vant', style: true }]
      : ['import', { libraryName: 'element-plus', style: true }]
  ]
}

2. 资源按需加载

// webpack 配置示例(vue.config.js)
configureWebpack: {
  optimization: {
    splitChunks: {
      cacheGroups: {
        mobile: {
          test: /[\\/]src[\\/]mobile[\\/]/,
          chunks: 'all',
          priority: 20
        },
        web: {
          test: /[\\/]src[\\/]web[\\/]/,
          chunks: 'all',
          priority: 20
        }
      }
    }
  }
}

六、调试与验证方案

1. 多设备联调

// 在 Chrome DevTools 中配置自定义设备
const devicePresets = [
  {
    name: 'Mobile Large',
    width: 414,
    height: 896,
    dpr: 3,
    type: 'phone'
  },
  {
    name: 'Desktop HD',
    width: 1920,
    height: 1080,
    dpr: 1,
    type: 'desktop'
  }
]

2. 自动化测试脚本

// cypress/integration/layout.spec.js
describe('响应式测试', () => {
  const viewports = [
    [375, 812], // iPhone X
    [1920, 1080] // Desktop
  ]

  viewports.forEach(([width, height]) => {
    it(`应正确适配 ${width}x${height}`, () => {
      cy.viewport(width, height)
      cy.visit('/')
      cy.get('.container').should(($el) => {
        expect($el.width()).to.be.closeTo(width > 800 ? 1200 : width, 10)
      })
    })
  })
})

最佳实践建议

  1. 设计规范先行

    • 制定 PC 端 1440px / 移动端 375px 双基准设计规范
    • 使用 8pt 栅格系统,保证两端布局一致性
  2. 性能监控

    // 性能埋点示例
    window.addEventListener('load', () => {
      const timing = performance.timing
      const loadTime = timing.loadEventEnd - timing.navigationStart
      analytics.send('page_load', { 
        platform: isMobile ? 'mobile' : 'web',
        loadTime 
      })
    })
  3. 渐进增强策略

    /* 基础样式 */
    .button {
      padding: 8px 16px;
      border-radius: 4px;
    }
    
    /* PC增强 */
    @media (hover: hover) {
      .button:hover {
        box-shadow: 0 2px 8px rgba(0,0,0,0.1);
      }
    }
    
    /* 移动端增强 */
    @media (pointer: coarse) {
      .button {
        min-width: 44px;
        min-height: 44px;
      }
    }

通过以上系统化方案,可以实现:

  • 同一代码库维护多端适配
  • 自动化的设备检测与适配
  • 按需加载的资源优化
  • 完善的跨端测试体系
  • 像素级精准的响应式控制

实际项目应根据具体需求选择组合方案,推荐优先采用多入口架构配合动态组件加载,在保证性能的同时实现最佳的跨端体验。

0

评论 (0)

取消