Vue3移动端解决方案
Vue3移动端适配方案
移动端开发需要考虑屏幕尺寸、设备像素比、触摸交互等因素。Vue3提供了多种方案来解决移动端适配问题,下面介绍几种主流方案。
视口单位适配
使用vw/vh单位可以很好地实现移动端适配:
/* 基准尺寸375px设计稿 */
html {
font-size: calc(100vw / 3.75);
}
.box {
width: 1rem; /* 相当于设计稿中的37.5px */
height: 0.8rem;
font-size: 0.28rem;
}
配合postcss-px-to-viewport插件可以自动转换px单位:
// postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport': {
viewportWidth: 375,
unitPrecision: 5,
viewportUnit: 'vw',
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: false
}
}
}
弹性布局方案
Flex布局是移动端最常用的布局方式:
<template>
<div class="container">
<div class="header">头部</div>
<div class="content">
<div v-for="item in 5" :key="item" class="item">项目{{item}}</div>
</div>
<div class="footer">底部</div>
</div>
</template>
<style scoped>
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.header, .footer {
flex: 0 0 50px;
}
.content {
flex: 1;
display: flex;
flex-wrap: wrap;
}
.item {
flex: 0 0 50%;
height: 100px;
}
</style>
移动端组件库
Vant是优秀的Vue3移动端组件库:
npm install vant@next
全局引入:
import { createApp } from 'vue'
import Vant from 'vant'
import 'vant/lib/index.css'
const app = createApp(App)
app.use(Vant)
按需引入示例:
<template>
<van-button type="primary">主要按钮</van-button>
<van-cell-group>
<van-field v-model="value" label="文本" placeholder="请输入文本" />
</van-cell-group>
</template>
<script setup>
import { ref } from 'vue'
import { Button as VanButton, CellGroup as VanCellGroup, Field as VanField } from 'vant'
const value = ref('')
</script>
手势处理
Vue3可以使用@vueuse/gesture处理复杂手势:
npm install @vueuse/gesture
示例代码:
<template>
<div
ref="boxRef"
style="width: 100px; height: 100px; background: red"
@tap="onTap"
@pan="onPan"
></div>
</template>
<script setup>
import { ref } from 'vue'
import { useGesture } from '@vueuse/gesture'
const boxRef = ref(null)
const onTap = () => console.log('tap')
const onPan = (state) => {
console.log(state.movement) // [x, y] 移动距离
}
useGesture({
onTap,
onPan
}, {
target: boxRef,
eventOptions: { passive: true }
})
</script>
性能优化
移动端需要特别注意性能优化:
- 图片懒加载
<template>
<img v-lazy="imgUrl" alt="">
</template>
<script setup>
import { Lazyload } from 'vant'
app.use(Lazyload)
</script>
- 虚拟列表优化长列表
<template>
<van-list
v-model:loading="loading"
:finished="finished"
@load="onLoad"
>
<div v-for="item in list" :key="item">{{ item }}</div>
</van-list>
</template>
- 使用keep-alive缓存组件
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
移动端调试
Chrome DevTools远程调试:
- 手机开启USB调试
- Chrome访问chrome://inspect
- 选择设备进行调试
VConsole方案:
npm install vconsole
import VConsole from 'vconsole'
new VConsole()
PWA支持
Vue3项目添加PWA支持:
npm install @vitejs/plugin-pwa
vite配置:
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
VitePWA({
includeAssets: ['favicon.ico'],
manifest: {
name: 'My App',
short_name: 'App',
theme_color: '#ffffff',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
}
]
}
})
]
})
跨端开发方案
使用uni-app跨端开发:
npm install -g @vue/cli
vue create -p dcloudio/uni-preset-vue my-project
示例页面:
<template>
<view class="container">
<text>Hello uni-app</text>
<button @click="onClick">点击</button>
</view>
</template>
<script setup>
const onClick = () => {
uni.showToast({
title: '点击了按钮'
})
}
</script>
<style>
.container {
padding: 20px;
}
</style>
状态管理方案
Pinia在移动端的应用:
npm install pinia
创建store:
// stores/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++
}
}
})
组件中使用:
<template>
<div>{{ counter.count }}</div>
<button @click="counter.increment()">+</button>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
动画处理
Vue3过渡动画在移动端的应用:
<template>
<button @click="show = !show">切换</button>
<transition name="fade">
<div v-if="show" class="box"></div>
</transition>
</template>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
.box {
width: 100px;
height: 100px;
background: red;
}
</style>
使用GSAP实现复杂动画:
npm install gsap
<script setup>
import { ref, onMounted } from 'vue'
import gsap from 'gsap'
const boxRef = ref(null)
onMounted(() => {
gsap.to(boxRef.value, {
x: 100,
duration: 1,
repeat: -1,
yoyo: true
})
})
</script>
移动端路由处理
Vue Router在移动端的特殊处理:
const router = createRouter({
history: createWebHashHistory(), // 移动端推荐hash模式
routes: [
{
path: '/',
component: Home,
meta: {
keepAlive: true // 需要缓存的页面
}
}
],
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
}
})
页面切换动画:
<router-view v-slot="{ Component }">
<transition name="slide">
<component :is="Component" />
</transition>
</router-view>
<style>
.slide-enter-active,
.slide-leave-active {
transition: transform 0.3s ease;
}
.slide-enter-from {
transform: translateX(100%);
}
.slide-leave-to {
transform: translateX(-30%);
}
</style>
移动端安全区域处理
处理iPhone等设备的刘海屏:
.safe-area {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
使用postcss-env插件自动添加:
// postcss.config.js
module.exports = {
plugins: {
'postcss-env': {
browsers: 'last 2 versions'
}
}
}
移动端主题切换
实现暗黑模式:
<template>
<button @click="toggleDark">切换主题</button>
</template>
<script setup>
import { useDark, useToggle } from '@vueuse/core'
const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>
<style>
.dark {
background: #222;
color: white;
}
</style>
移动端本地存储
使用Pinia持久化存储:
npm install pinia-plugin-persistedstate
配置:
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
在store中使用:
export const useUserStore = defineStore('user', {
state: () => ({
token: ''
}),
persist: true
})
移动端API请求封装
封装axios请求:
// utils/request.js
import axios from 'axios'
const service = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000
})
service.interceptors.request.use(config => {
config.headers['X-Token'] = getToken()
return config
})
service.interceptors.response.use(
response => {
return response.data
},
error => {
return Promise.reject(error)
}
)
export default service
组件中使用:
<script setup>
import request from '@/utils/request'
import { ref } from 'vue'
const list = ref([])
request.get('/api/list').then(res => {
list.value = res.data
})
</script>
移动端WebSocket通信
实现实时通信:
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const messages = ref([])
let socket = null
onMounted(() => {
socket = new WebSocket('wss://echo.websocket.org')
socket.onopen = () => {
socket.send('Hello Server!')
}
socket.onmessage = (e) => {
messages.value.push(e.data)
}
})
onUnmounted(() => {
socket?.close()
})
</script>
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Vue3与Electron集成
下一篇:Vue3微前端方案