🚀 Vue 3 統合ガイド - モダン開発の完璧なパートナー!
Vue 3 + Vant の素晴らしい世界へようこそ!🎉 ここには最も包括的な統合ガイドがあり、Vue 3 の強力な機能を深く探求し、Vant コンポーネントライブラリと完璧に融合させ、驚くべきモバイルアプリケーションを作り上げる方法をご案内します。
Composition API から TypeScript サポート、パフォーマンス最適化からテーマカスタマイズまで、モダンなフロントエンド開発のエッセンスを段階的に習得できるよう導きます。このエキサイティングな開発の旅を一緒に始めましょう!✨
🚀 Vue 3 新機能サポート
Composition API 統合
Vant 4.x は Vue 3 の Composition API を完全にサポートしており、コンポーネントロジックをより明確で再利用可能にします。
基本的な使用例
vue
<script setup>
import { ref, computed } from 'vue'
import { showToast, showDialog } from 'vant'
// リアクティブデータ
const count = ref(0)
const loading = ref(false)
const userInfo = ref({
name: '田中',
age: 25,
city: '東京'
})
// 算出プロパティ
const displayInfo = computed(() => {
return `${userInfo.value.name} (${userInfo.value.age}歳) - ${userInfo.value.city}`
})
// メソッド
const handleIncrement = async () => {
loading.value = true
// 非同期操作のシミュレーション
await new Promise(resolve => setTimeout(resolve, 1000))
count.value++
loading.value = false
showToast(`現在のカウント: ${count.value}`)
}
const handleReset = () => {
showDialog({
title: 'リセット確認',
message: 'カウンターをリセットしますか?',
}).then(() => {
count.value = 0
showToast('リセットしました')
}).catch(() => {
showToast('キャンセルしました')
})
}
}
</script>
<template>
<div class="demo-container">
<!-- ユーザー情報の表示 -->
<van-cell-group title="ユーザー情報">
<van-cell title="基本情報" :value="displayInfo" />
<van-cell title="現在のカウント" :value="count" />
</van-cell-group>
<!-- 操作ボタン -->
<div class="button-group">
<van-button
type="primary"
:loading="loading"
@click="handleIncrement"
block
>
カウントを増やす
</van-button>
<van-button
type="danger"
:disabled="count === 0"
@click="handleReset"
block
>
カウントをリセット
</van-button>
</div>
</div>
</template>
<style scoped>
.demo-container {
padding: 16px;
}
.button-group {
margin-top: 16px;
display: flex;
flex-direction: column;
gap: 12px;
}
</style>反応式システムの深い統合
reactive を使用して複雑な状態を管理
vue
<script setup>
import { reactive, computed, watch } from 'vue'
import { showNotify } from 'vant'
// 複雑な状態管理
const state = reactive({
user: {
name: '',
email: '',
phone: ''
},
form: {
loading: false,
errors: {}
},
settings: {
notifications: true,
darkMode: false
}
})
// フォーム検証
const isFormValid = computed(() => {
return state.user.name &&
state.user.email &&
state.user.phone
})
// 設定変更の監視
watch(() => state.settings.darkMode, (newValue) => {
document.documentElement.classList.toggle('dark', newValue)
showNotify({
type: 'success',
message: `モードを${newValue ? 'ダーク' : 'ライト'}に切り替えました`
})
})
// フォームの送信
const handleSubmit = async () => {
if (!isFormValid.value) {
showNotify({ type: 'warning', message: '全ての情報を入力してください' })
return
}
state.form.loading = true
try {
// 非同期操作のシミュレーション
await new Promise(resolve => setTimeout(resolve, 2000))
showNotify({ type: 'success', message: '保存成功しました' })
} catch (error) {
showNotify({ type: 'danger', message: '保存に失敗しました' })
} finally {
state.form.loading = false
}
}
</script>
<template>
<div class="form-container">
<van-form @submit="handleSubmit">
<van-cell-group title="個人情報">
<van-field
v-model="state.user.name"
name="name"
label="名前"
placeholder="名前を入力してください"
:rules="[{ required: true, message: '名前を入力してください' }]"
/>
<van-field
v-model="state.user.email"
name="email"
label="メール"
placeholder="メールアドレスを入力してください"
:rules="[
{ required: true, message: 'メールアドレスを入力してください' },
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'メールアドレスの形式が正しくありません' }
]"
/>
<van-field
v-model="state.user.phone"
name="phone"
label="電話番号"
placeholder="電話番号を入力してください"
:rules="[
{ required: true, message: '電話番号を入力してください' },
{ pattern: /^1[3-9]\d{9}$/, message: '電話番号の形式が正しくありません' }
]"
/>
</van-cell-group>
<van-cell-group title="設定">
<van-cell title="通知プッシュ">
<template #right-icon>
<van-switch v-model="state.settings.notifications" />
</template>
</van-cell>
<van-cell title="ダークモード">
<template #right-icon>
<van-switch v-model="state.settings.darkMode" />
</template>
</van-cell>
</van-cell-group>
<div class="submit-section">
<van-button
type="primary"
native-type="submit"
:loading="state.form.loading"
:disabled="!isFormValid"
block
>
情報を保存
</van-button>
</div>
</van-form>
</div>
</template>
<style scoped>
.form-container {
padding: 16px;
}
.submit-section {
margin-top: 24px;
}
</style>🎯 TypeScript 完全サポート
コンポーネント型定義
typescript
// types/user.ts
export interface User {
id: number
name: string
email: string
avatar?: string
phone?: string
status: 'active' | 'inactive' | 'pending'
}
export interface UserFormData {
name: string
email: string
phone: string
}
// types/components.ts
export interface ButtonProps {
type?: 'primary' | 'success' | 'warning' | 'danger' | 'default'
size?: 'large' | 'normal' | 'small' | 'mini'
loading?: boolean
disabled?: boolean
block?: boolean
}TypeScript コンポーネントの例
vue
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { User, UserFormData } from '@/types/user'
// Props インターフェースの定義
interface Props {
user?: User
readonly?: boolean
showAvatar?: boolean
}
// 默认値の設定
const props = withDefaults(defineProps<Props>(), {
readonly: false,
showAvatar: true
})
// Emits 型の定義
interface Emits {
(e: 'update', data: UserFormData): void
(e: 'delete', id: number): void
(e: 'save', user: User): void
}
const emit = defineEmits<Emits>()
// 响应式データの設定
const formData = ref<UserFormData>({
name: props.user?.name || '',
email: props.user?.email || '',
phone: props.user?.phone || ''
})
const loading = ref<boolean>(false)
// 計算プロパティの設定
const isFormValid = computed((): boolean => {
return !!(formData.value.name &&
formData.value.email &&
formData.value.phone)
})
const avatarUrl = computed((): string => {
return props.user?.avatar || '/default-avatar.png'
})
// メソッドの設定
const handleSave = async (): Promise<void> => {
if (!isFormValid.value) return
loading.value = true
try {
const updatedUser: User = {
id: props.user?.id || Date.now(),
...formData.value,
status: 'active'
}
emit('save', updatedUser)
} finally {
loading.value = false
}
}
const handleDelete = (): void => {
if (props.user?.id) {
emit('delete', props.user.id)
}
}
// 公開メソッドの設定
defineExpose({
validate: () => isFormValid.value,
reset: () => {
formData.value = {
name: '',
email: '',
phone: ''
}
}
})
</script>
<template>
<div class="user-form">
<!-- アバター表示 -->
<div v-if="showAvatar" class="avatar-section">
<van-image
:src="avatarUrl"
width="80"
height="80"
round
fit="cover"
/>
</div>
<!-- フォーム内容 -->
<van-form @submit="handleSave">
<van-field
v-model="formData.name"
label="名前"
placeholder="名前を入力してください"
:readonly="readonly"
:rules="[{ required: true, message: '名前を入力してください' }]"
/>
<van-field
v-model="formData.email"
label="メール"
placeholder="メールアドレスを入力してください"
:readonly="readonly"
:rules="[
{ required: true, message: 'メールアドレスを入力してください' },
{ pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'メールアドレスの形式が正しくありません' }
]"
/>
<van-field
v-model="formData.phone"
label="電話番号"
placeholder="電話番号を入力してください"
:readonly="readonly"
:rules="[
{ required: true, message: '電話番号を入力してください' },
{ pattern: /^1[3-9]\d{9}$/, message: '電話番号の形式が正しくありません' }
]"
/>
<!-- 操作ボタン -->
<div v-if="!readonly" class="action-buttons">
<van-button
type="primary"
:loading="loading"
:disabled="!isFormValid"
@click="handleSave"
block
>
情報を保存
</van-button>
<van-button
v-if="user?.id"
type="danger"
plain
@click="handleDelete"
block
>
ユーザーを 削除
</van-button>
</div>
</van-form>
</div>
</template>
<style scoped>
.user-form {
padding: 16px;
}
.avatar-section {
display: flex;
justify-content: center;
margin-bottom: 24px;
}
.action-buttons {
margin-top: 24px;
display: flex;
flex-direction: column;
gap: 12px;
}
</style>🔧 パフォーマンス最適化戦略
按需引入設定
自動按需引入(推奨)
javascript
// 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()],
// 生成型定義ファイル
dts: true,
// カスタムコンポーネントディレクトリ
dirs: ['src/components'],
// 包含のファイル拡張子
extensions: ['vue', 'ts'],
// 子ディレクトリを再帰的に検索
deep: true
})
],
// ビルド最適化
build: {
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'vant-vendor': ['vant'],
'utils-vendor': ['lodash-es', 'dayjs']
}
}
}
}
})手動按需引入
javascript
// main.js
import { createApp } from 'vue'
import {
Button,
Cell,
CellGroup,
Field,
Form,
Toast,
Dialog,
Notify
} from 'vant'
// 引入对应样式
import 'vant/es/button/style'
import 'vant/es/cell/style'
import 'vant/es/cell-group/style'
import 'vant/es/field/style'
import 'vant/es/form/style'
import 'vant/es/toast/style'
import 'vant/es/dialog/style'
import 'vant/es/notify/style'
const app = createApp(App)
app.use(Button)
app.use(Cell)
app.use(CellGroup)
app.use(Field)
app.use(Form)
app.use(Toast)
app.use(Dialog)
app.use(Notify)
app.mount('#app')コンポーネント遅延ロード
vue
<script setup>
import { defineAsyncComponent } from 'vue'
// 遅延ロードコンポーネント
const HeavyComponent = defineAsyncComponent(() =>
import('./components/HeavyComponent.vue')
)
// ロード中状態の遅延ロードコンポーネント
const AsyncUserList = defineAsyncComponent({
loader: () => import('./components/UserList.vue'),
loadingComponent: () => h('van-loading', { type: 'spinner' }),
errorComponent: () => h('van-empty', { description: 'ロードに失敗しました' }),
delay: 200,
timeout: 3000
})
</script>
<template>
<div>
<Suspense>
<template #default>
<HeavyComponent />
<AsyncUserList />
</template>
<template #fallback>
<van-skeleton :row="3" />
</template>
</Suspense>
</div>
</template>🎨 テーマカスタマイズとスタイル
CSS 変数カスタマイズ
css
/* グローバルテーマ変数 */
:root {
/* メインカラー */
--van-primary-color: #1989fa;
--van-primary-color-dark: #0960bd;
--van-primary-color-light: #66b1ff;
/* 機能色 */
--van-success-color: #07c160;
--van-warning-color: #ff976a;
--van-danger-color: #ee0a24;
/* テキストカラー */
--van-text-color: #323233;
--van-text-color-2: #646566;
--van-text-color-3: #969799;
/* 背景色 */
--van-background-color: #f7f8fa;
--van-background-color-light: #fafafa;
/* 境界 */
--van-border-color: #ebedf0;
--van-border-width: 1px;
/* フォントサイズ */
--van-font-size-xs: 10px;
--van-font-size-sm: 12px;
--van-font-size-md: 14px;
--van-font-size-lg: 16px;
/* パディング */
--van-padding-base: 4px;
--van-padding-xs: 8px;
--van-padding-sm: 12px;
--van-padding-md: 16px;
--van-padding-lg: 24px;
/* ボーダーラジオ */
--van-border-radius-sm: 2px;
--van-border-radius-md: 4px;
--van-border-radius-lg: 8px;
}
/* 深色モード */
@media (prefers-color-scheme: dark) {
:root {
--van-primary-color: #4fc3f7;
--van-text-color: #ffffff;
--van-text-color-2: #cccccc;
--van-background-color: #1a1a1a;
--van-background-color-light: #2d2d2d;
--van-border-color: #333333;
}
}コンポーネントレベルのスタイルカスタマイズ
vue
<script setup>
import { ref } from 'vue'
const theme = ref('light')
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
document.documentElement.setAttribute('data-theme', theme.value)
}
</script>
<template>
<div class="themed-container" :data-theme="theme">
<!-- カスタムボタンスタイル -->
<van-button class="custom-button" type="primary">
カスタムボタン
</van-button>
<!-- カスタムカードスタイル -->
<van-card class="custom-card" title="カスタムカード">
<template #footer>
<van-button size="small" @click="toggleTheme">
テーマを切り替える
</van-button>
</template>
</van-card>
</div>
</template>
<style scoped>
.themed-container {
padding: 16px;
}
/* カスタムボタンスタイル */
.custom-button {
--van-button-primary-background-color: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--van-button-primary-border-color: transparent;
--van-button-border-radius: 20px;
font-weight: 600;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
transition: all 0.3s ease;
}
.custom-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
/* カスタムカードスタイル */
.custom-card {
--van-card-background-color: var(--van-background-color-light);
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
/* 深色モード適応 */
[data-theme="dark"] .custom-card {
--van-card-background-color: #2d2d2d;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
}
</style>🚀 ベストプラクティス要約
🎯 開発規約
コンポーネント設計原則 ⭐
- 単一責任原則:各コンポーネントは1つの機能だけを担当
- 再利用性:propsとslotsを活用して柔軟性を提供
- 型安全:TypeScriptを使用して型安全を保証
- パフォーマンス最適化:キャッシュと遅延ロードを適用
コード組織 📁
- Composition APIを使用してロジックを組織
- composablesを適切に分割
- 型定義を一元管理
- クリアなフォルダ構造を維持
パフォーマンス最適化 ⚡
- 按需引入コンポーネントとスタイル
- 使用非同期コンポーネントと Suspense
- 合理使用 v-memo と v-once
- 不必要な反応式データを避ける
📂 プロジェクト構造推奨
src/
├── components/ # 公共コンポーネント
│ ├── base/ # 基本コンポーネント
│ ├── business/ # ビジネスコンポーネント
│ └── layout/ # レイアウトコンポーネント
├── composables/ # 組み合わせ関数
├── types/ # 型定義
├── utils/ # ユーティリティ関数
├── styles/ # スタイルファイル
│ ├── variables.css # CSS 変数
│ ├── mixins.scss # Sass 混入
│ └── themes/ # テーマファイル
├── views/ # ページコンポーネント
└── router/ # ルーター設定💡 開発技巧
- 善用 Vue DevTools 🔧:Composition API と反応式データのデバッグ
- 型提示 📝:TypeScript のスマートヒントを活用
- パフォーマンスモニタリング 📊:Vue DevTools のパフォーマンスパネルを使用
- コンポーネントテスト 🧪:コンポーネントの品質を保証するユニットテストを書く
- ドキュメント保守 📖:コンポーネントのドキュメントを及时に更新
🎉 まとめ
通じて、Vue 3 + Vant の開発には多くのツールとテクニックが必要です。以上のガイドを参考に、Vue 3 の Composition API を活用して、Vant のコンポーネントを効果的に使用してください。
型安全を保証する TypeScript を使用し、パフォーマンスを最適化するために、Vue DevTools を活用してください。さらに、コンポーネントの品質を保証するユニットテストを書き、ドキュメントを及时に更新してください。
以上のベストプラクティスを遵守することで、Vue 3 + Vant の開発を効率的かつ品質の高いものにすることができます。
📚 関連コンテンツ
さらに詳しく知りたいですか?これらのドキュメントがあなたの開発の旅をより便利にします:
- 📖 クイックスタート - ゼロからプロジェクトを構築
- 🏆 ベストプラクティス - 経験のまとめとテクニック共有
- 📝 TypeScript ガイド - 型安全な開発体験
- 🎨 テーマカスタマイズ - 独自のビジュアルスタイルを作り上げる
- ⚡ パフォーマンス最適化 - アプリを高速化し、ユーザー体験を向上させる
- 📱 モバイル適応 - 様々なデバイスに完璧に適応
- 🎯 デザインガイド - 統一されたデザイン言語
- 🚀 Vant 4 - 最新機能の紹介
- 📦 Vant Weapp - ミニプログラム開発ガイド
- ❓ よくある質問 - よくある質問と回答
- 💬 お問い合わせ - 支援を受ける