⚡ パフォーマンス最適化テクニック - あなたのアプリを稲妻のように速く!
🚀 あなたの Vant アプリをウサギよりも速く走らせたいですか?このパフォーマンス最適化の秘訣があなたの加速装置になります!バンドルサイズの削減からファーストビューの瞬時表示、滑らかなスクロールからインテリジェントなモニタリングまで、究極のユーザー体験を創り出すお手伝いをします。準備はできていますか?一緒にパフォーマンス最適化の素晴らしい旅に出かけましょう!
📦 バンドルサイズ最適化
按需引入组件
自动按需引入(推荐)
bash
# 安装依赖
npm install unplugin-vue-components -Djavascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [VantResolver()]
})
]
})vue
<template>
<!-- 手動でのインポートは不要、自動的に必要なものだけがロードされます -->
<van-button type="primary">ボタン</van-button>
<van-cell title="セル" />
</template>手动按需引入
javascript
// main.js
import { createApp } from 'vue'
import { Button, Cell, Toast } from 'vant'
import 'vant/es/button/style'
import 'vant/es/cell/style'
import 'vant/es/toast/style'
const app = createApp()
app.use(Button)
app.use(Cell)
app.use(Toast)代码分割
路由级代码分割
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue')
},
{
path: '/products',
name: 'Products',
component: () => import('../views/Products.vue')
},
{
path: '/profile',
name: 'Profile',
// 懒加载 + 预加载
component: () => import(/* webpackChunkName: "profile" */ '../views/Profile.vue')
}
]
export default createRouter({
history: createWebHistory(),
routes
})组件级代码分割
vue
<script setup>
import { defineAsyncComponent } from 'vue'
// 异步组件
const HeavyComponent = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
)
// 带加载状态的异步组件
const AsyncComponent = defineAsyncComponent({
loader: () => import('./components/AsyncComponent.vue'),
loadingComponent: LoadingComponent,
errorComponent: ErrorComponent,
delay: 200,
timeout: 3000
})
</script>
<template>
<div>
<HeavyComponent />
<AsyncComponent />
</div>
</template>构建优化配置
Vite 配置优化
javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
// 启用CSS代码分割
cssCodeSplit: true,
// 构建后是否生成source map文件
sourcemap: false,
// chunk大小警告的限制
chunkSizeWarningLimit: 2000,
rollupOptions: {
output: {
// 手动分包
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'vant-vendor': ['vant'],
'utils-vendor': ['lodash-es', 'dayjs']
}
}
},
// 压缩配置
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
}
}
},
// 开发服务器优化
server: {
warmup: {
clientFiles: ['./src/components/*.vue']
}
}
})⚡ ランタイムパフォーマンス最適化
画像最適化戦略
遅延読み込みの実装
vue
<script setup>
import { ref, onMounted } from 'vue'
import { Lazyload } from 'vant'
// 画像の遅延読み込み設定
const lazyloadOptions = {
preLoad: 1.3,
error: '/error.png',
loading: '/loading.gif',
attempt: 1
}
onMounted(() => {
app.use(Lazyload, lazyloadOptions)
})
</script>
<template>
<!-- 画像の遅延読み込み -->
<img v-lazy="imageUrl" alt="遅延読み込み画像" />
<!-- 背景画像の遅延読み込み -->
<div v-lazy:background-image="bgImageUrl" class="bg-container"></div>
<!-- Vant 画像コンポーネントの遅延読み込み -->
<van-image
lazy-load
:src="imageUrl"
width="100"
height="100"
fit="cover"
/>
</template>レスポンシブ画像
html
<!-- デバイスのピクセル比に応じて異なる解像度の画像をロード -->
<img
srcset="image@1x.jpg 1x, image@2x.jpg 2x, image@3x.jpg 3x"
src="image@1x.jpg"
alt="レスポンシブ画像"
>
<!-- picture要素を使用して複数の形式をサポート -->
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="画像">
</picture>仮想スクロール
vue
<script setup>
import { ref, computed, onMounted } from 'vue'
import { List } from 'vant'
const list = ref([])
const loading = ref(false)
const finished = ref(false)
// 仮想スクロール設定
const itemHeight = 60
const visibleCount = Math.ceil(window.innerHeight / itemHeight) + 2
const startIndex = ref(0)
const visibleItems = computed(() => {
const start = startIndex.value
const end = start + visibleCount
return list.value.slice(start, end)
})
const onLoad = async () => {
loading.value = true
// データロードのシミュレーション
const newItems = Array.from({ length: 20 }, (_, i) => ({
id: list.value.length + i,
title: `アイテム ${list.value.length + i + 1}`
}))
list.value.push(...newItems)
loading.value = false
if (list.value.length >= 100) {
finished.value = true
}
}
const onScroll = (event) => {
const scrollTop = event.target.scrollTop
startIndex.value = Math.floor(scrollTop / itemHeight)
}
</script>
<template>
<div class="virtual-list" @scroll="onScroll">
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="これ以上ありません"
@load="onLoad"
>
<van-cell
v-for="item in visibleItems"
:key="item.id"
:title="item.title"
:style="{ height: itemHeight + 'px' }"
/>
</van-list>
</div>
</template>
<style scoped>
.virtual-list {
height: 400px;
overflow-y: auto;
}
</style>🚀 ファーストビューロードの最適化
重要リソースの最適化
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- DNSプリフェッチ -->
<link rel="dns-prefetch" href="//cdn.example.com">
<link rel="dns-prefetch" href="//api.example.com">
<!-- 重要リソースのプリロード -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/css/critical.css" as="style">
<!-- 重要なサードパーティのプリコネクト -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<!-- クリティカルCSSのインライン -->
<style>
/* ファーストビューの重要スタイル */
.loading-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #1989fa;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<title>Vant移动端UI</title>
</head>
<body>
<div id="app">
<!-- ファーストビューロードアニメーション -->
<div class="loading-screen">
<div class="loading-spinner"></div>
</div>
</div>
</body>
</html>スケルトンスクリーンの実装
vue
<script setup>
import { ref, onMounted } from 'vue'
import { Skeleton } from 'vant'
const loading = ref(true)
const data = ref([])
const fetchData = async () => {
// APIリクエストのシミュレーション
return new Promise(resolve => {
setTimeout(() => {
resolve([
{ id: 1, title: 'タイトル1', value: 'コンテンツ1' },
{ id: 2, title: 'タイトル2', value: 'コンテンツ2' },
{ id: 3, title: 'タイトル3', value: 'コンテンツ3' }
])
}, 2000)
})
}
onMounted(async () => {
try {
data.value = await fetchData()
} finally {
loading.value = false
}
})
</script>
<template>
<div class="page-container">
<!-- スケルトンスクリーン -->
<van-skeleton
:loading="loading"
:row="3"
:row-width="['100%', '60%', '80%']"
avatar
title
>
<!-- 実際のコンテンツ -->
<div class="content">
<van-cell-group>
<van-cell
v-for="item in data"
:key="item.id"
:title="item.title"
:value="item.value"
/>
</van-cell-group>
</div>
</van-skeleton>
</div>
</template>
<style scoped>
.page-container {
padding: 16px;
}
</style>📱 モバイル端末のパフォーマンス最適化
タッチ操作の最適化
css
/* タッチ操作の最適化 */
.touch-element {
/* 300msのタッチ遅延を排除 */
touch-action: manipulation;
/* タッチのハイライトを無効にする */
-webkit-tap-highlight-color: transparent;
/* 長押しメニューを無効にする */
-webkit-touch-callout: none;
/* テキスト選択を無効にする */
-webkit-user-select: none;
user-select: none;
}
/* スクロールの最適化 */
.scroll-container {
/* ハードウェアアクセラレーションによるスクロールを有効にする */
-webkit-overflow-scrolling: touch;
overflow-scrolling: touch;
/* スクロールパフォーマンスを最適化 */
will-change: scroll-position;
/* スクロールの貫通を防止 */
overscroll-behavior: contain;
}
/* 長いリストの最適化 */
.list-item {
/* GPUアクセラレーションを有効にする */
transform: translateZ(0);
/* 再描画を最適化 */
will-change: transform;
}メモリの最適化
vue
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
const data = ref([])
const observer = ref(null)
// タイマーのクリーンアップ
const timers = []
const addTimer = (callback, delay) => {
const timer = setTimeout(callback, delay)
timers.push(timer)
return timer
}
// イベントリスナーのクリーンアップ
const eventListeners = []
const addEventListener = (element, event, handler) => {
element.addEventListener(event, handler)
eventListeners.push({ element, event, handler })
}
onMounted(() => {
// Intersection Observerを使用してスクロールパフォーマンスを最適化
observer.value = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 要素がビューポートに入ったときの処理
entry.target.classList.add('visible')
}
})
})
// すべてのリストアイテムを観察
nextTick(() => {
document.querySelectorAll('.list-item').forEach(item => {
observer.value.observe(item)
})
})
})
onUnmounted(() => {
// タイマーのクリーンアップ
timers.forEach(timer => clearTimeout(timer))
// イベントリスナーのクリーンアップ
eventListeners.forEach(({ element, event, handler }) => {
element.removeEventListener(event, handler)
})
// Intersection Observerのクリーンアップ
if (observer.value) {
observer.value.disconnect()
}
})
</script>📊 パフォーマンスモニタリング
パフォーマンス指標のモニタリング
javascript
// パフォーマンスモニタリングツール
class PerformanceMonitor {
constructor() {
this.metrics = {}
this.init()
}
init() {
this.observePageLoad()
this.observeUserInteraction()
this.observeResourceLoad()
}
// ページロードパフォーマンス
observePageLoad() {
window.addEventListener('load', () => {
const navigation = performance.getEntriesByType('navigation')[0]
this.metrics.pageLoad = {
dns: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp: navigation.connectEnd - navigation.connectStart,
request: navigation.responseStart - navigation.requestStart,
response: navigation.responseEnd - navigation.responseStart,
dom: navigation.domContentLoadedEventEnd - navigation.responseEnd,
load: navigation.loadEventEnd - navigation.loadEventStart,
total: navigation.loadEventEnd - navigation.navigationStart
}
this.reportMetrics()
})
}
// ユーザーインタラクションパフォーマンス
observeUserInteraction() {
// FID(First Input Delay)のモニタリング
if ('PerformanceObserver' in window) {
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-input') {
this.metrics.fid = entry.processingStart - entry.startTime
}
}
}).observe({ type: 'first-input', buffered: true })
// LCP(Largest Contentful Paint)のモニタリング
new PerformanceObserver((list) => {
const entries = list.getEntries()
const lastEntry = entries[entries.length - 1]
this.metrics.lcp = lastEntry.startTime
}).observe({ type: 'largest-contentful-paint', buffered: true })
}
}
// リソースロードパフォーマンス
observeResourceLoad() {
if ('PerformanceObserver' in window) {
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.initiatorType === 'img') {
this.metrics.imageLoad = entry.duration
}
}
}).observe({ type: 'resource', buffered: true })
}
}
// パフォーマンスデータの送信
reportMetrics() {
// 分析サービスに送信
if (navigator.sendBeacon) {
navigator.sendBeacon('/api/performance', JSON.stringify(this.metrics))
} else {
fetch('/api/performance', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.metrics)
}).catch(err => console.error('Performance report failed:', err))
}
}
}
// パフォーマンスモニタリングの初期化
if (typeof window !== 'undefined') {
new PerformanceMonitor()
}Web Vitalsのモニタリング
javascript
// web-vitalsのインストール
// npm install web-vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'
// コアWebパフォーマンス指標のモニタリング
function sendToAnalytics(metric) {
// 分析サービスに送信
fetch('/api/analytics', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(metric)
})
}
getCLS(sendToAnalytics) // 累積レイアウトシフト
getFID(sendToAnalytics) // 初回入力遅延
getFCP(sendToAnalytics) // 初回コンテンツペイント
getLCP(sendToAnalytics) // 最大コンテンツペイント
getTTFB(sendToAnalytics) // 最初のバイト時間🛠️ デバッグツール
パフォーマンス分析ツールの推奨
Chrome DevTools
- Performanceパネル:実行時パフォーマンスの分析
- Networkパネル:リソースロードの分析
- Lighthouse:総合的なパフォーマンス評価
Bundle Analyzer
bash# インストール npm install --save-dev webpack-bundle-analyzer # バンドルサイズの分析 npm run build -- --analyzeパフォーマンスモニタリングサービス
- Google Analytics
- Sentry Performance
- New Relic Browser
パフォーマンス最適化チェックリスト
- [ ] 必要なものだけをインポートし、バンドルサイズを削減
- [ ] コード分割を設定し、ロード速度を最適化
- [ ] 画像の遅延読み込みと圧縮を実装
- [ ] スケルトンスクリーンを使用してユーザー体験を向上
- [ ] クリティカルレンダリングパスを最適化
- [ ] リソースの圧縮とキャッシュを有効にする
- [ ] コアパフォーマンス指標をモニタリング
- [ ] 定期的にパフォーマンステストを実行
💡 ベストプラクティスまとめ - パフォーマンス最適化の黄金律
バンドルサイズの最適化 📦
- 必要なものだけをインポートし、コード分割を使用(必要なものだけ)
- 適切なビルド最適化を設定(極限まで圧縮)
ファーストビュー最適化 🚀
- スケルトンスクリーンと重要リソースのプリロードを実装(迅速な応答)
- クリティカルCSSをインライン化し、非重要リソースを遅延(優先度管理)
実行時最適化 ⚡
- 仮想スクロールと画像の遅延読み込みを使用(必要に応じてレンダリング)
- アニメーションとインタラクションのパフォーマンスを最適化(スムーズな体験)
モバイル端末最適化 📱
- タッチ体験とスクロールパフォーマンスを最適化(モバイルファースト)
- メモリとリソースを合理的に管理(細かな計画)
パフォーマンスモニタリング 📊
- 完全なパフォーマンスモニタリングシステムを構築(データで話す)
- コアWeb Vitals指標に注目(ユーザー体験)
継続的な最適化 🔄
- 定期的にパフォーマンスのボトルネックを分析・最適化(止まらない)
- パフォーマンス予算とモニタリングメカニズムを確立(未然に防ぐ)
📚 関連コンテンツ
パフォーマンス最適化の詳細な技巧を深く理解したいですか?これらのリソースは見逃せません:
- 🚀 クイックスタート - Vantの旅を始めよう
- 💎 ベストプラクティス - エンタープライズレベルの開発経験
- 🌟 Vue3 インテグレーション - 最新の開発体験
- 🎨 テーマカスタマイズ - アプリを個性化
- 📱 モバイル対応 - 様々なデバイスに完全対応
- 📝 TypeScript ガイド - 型安全な開発
- 📖 デザインガイドライン - デザイン言語とガイドライン
- 🎯 Vant 4 - 最新バージョンの機能
- 📱 Vant Weapp - ミニプログラムバージョン
- ❓ FAQ - よくある質問
- 📞 お問い合わせ - さらに支援を受ける
上記の最適化戦略により、Vantアプリは大幅なパフォーマンス向上を実現し、ユーザーに卓越した使用体験を提供します。パフォーマンス最適化は継続的なプロセスであり、細部の改善ごとにユーザーはあなたの気配りを感じるでしょう!🎯✨