Vue.js入門 - 進化し続けるプログレッシブフレームワーク
はじめに
Vue.js(ビュー)は、ユーザーインターフェース構築のためのプログレッシブJavaScriptフレームワークです。2014年にEvan You氏によって開発され、現在はGitHubで52,000以上のスターを獲得する人気プロジェクトに成長しました。
「プログレッシブ」という名前が示すとおり、Vue.jsは小規模なライブラリとして始めて、必要に応じてフルスタックフレームワークまでスケールアップできる柔軟性を持っています。最新バージョンはVue 3.5.25(2025年11月リリース)で、TypeScriptを96.6%採用した堅牢なコードベースを誇ります。
特徴・メリット
1. 親しみやすい(Approachable)
Vue.jsは標準的なHTML、CSS、JavaScriptの知識があれば、すぐに始められます。
- 直感的なAPI: 学習曲線が緩やかで、初心者にも優しい
- 充実したドキュメント: 日本語を含む多言語対応の公式ドキュメント
- シングルファイルコンポーネント(SFC): HTML、CSS、JavaScriptを1つのファイルにまとめて管理
2. 高性能(Performant)
- 真のリアクティブシステム: データの変更が自動的にUIに反映
- コンパイラ最適化: 仮想DOMの差分計算を最小化
- 軽量: コアライブラリは約33KB(gzip圧縮時)
3. 多機能(Versatile)
- 段階的な採用: 既存プロジェクトへの部分的な導入が可能
- 豊富なエコシステム: Vue Router、Pinia、Vuetifyなど公式・サードパーティライブラリが充実
- SSR/SSG対応: Nuxt.jsを使ったサーバーサイドレンダリングにも対応
4. TypeScriptファースト
Vue 3はTypeScriptで書かれており、型安全な開発が標準でサポートされています。
インストール方法
CDNを使った簡単な導入
最も手軽にVue.jsを試す方法です。
<!DOCTYPE html>
<html>
<head>
<title>Vue.js Quick Start</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">{{ message }}</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
message: 'Hello Vue!'
}
}
}).mount('#app')
</script>
</body>
</html>
npm/yarnを使った本格的なプロジェクト
推奨される方法はcreate-vueを使うことです。
# npmの場合
npm create vue@latest
# yarnの場合
yarn create vue
# pnpmの場合
pnpm create vue
対話形式でプロジェクト設定を選択できます:
✔ Project name: my-vue-app
✔ Add TypeScript? Yes
✔ Add JSX Support? No
✔ Add Vue Router? Yes
✔ Add Pinia for state management? Yes
✔ Add Vitest for Unit Testing? Yes
✔ Add ESLint for code quality? Yes
プロジェクト作成後:
cd my-vue-app
npm install
npm run dev
基本的な使い方
Composition APIの基本
Vue 3で推奨されるComposition APIを使った例です。
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
// リアクティブな状態
const count = ref(0)
const name = ref('Vue')
// 算出プロパティ
const doubleCount = computed(() => count.value * 2)
// メソッド
const increment = () => {
count.value++
}
// ライフサイクルフック
onMounted(() => {
console.log('コンポーネントがマウントされました')
})
</script>
<template>
<div class="counter">
<h1>Hello, {{ name }}!</h1>
<p>カウント: {{ count }}</p>
<p>2倍: {{ doubleCount }}</p>
<button @click="increment">+1</button>
</div>
</template>
<style scoped>
.counter {
padding: 20px;
text-align: center;
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
</style>
コンポーネント間のデータ受け渡し
親コンポーネント(Parent.vue)
<script setup lang="ts">
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
const parentMessage = ref('親からのメッセージ')
const handleChildEvent = (payload: string) => {
console.log('子から受信:', payload)
}
</script>
<template>
<div>
<ChildComponent
:message="parentMessage"
@custom-event="handleChildEvent"
/>
</div>
</template>
子コンポーネント(ChildComponent.vue)
<script setup lang="ts">
// Propsの定義
const props = defineProps<{
message: string
}>()
// Emitの定義
const emit = defineEmits<{
(e: 'custom-event', payload: string): void
}>()
const sendToParent = () => {
emit('custom-event', '子からのデータ')
}
</script>
<template>
<div>
<p>{{ message }}</p>
<button @click="sendToParent">親に送信</button>
</div>
</template>
条件分岐とリスト表示
<script setup lang="ts">
import { ref } from 'vue'
interface Todo {
id: number
text: string
done: boolean
}
const todos = ref<Todo[]>([
{ id: 1, text: 'Vue.jsを学ぶ', done: true },
{ id: 2, text: 'プロジェクトを作る', done: false },
{ id: 3, text: 'デプロイする', done: false }
])
const showCompleted = ref(true)
</script>
<template>
<div>
<label>
<input type="checkbox" v-model="showCompleted" />
完了済みを表示
</label>
<ul>
<li
v-for="todo in todos"
:key="todo.id"
v-show="showCompleted || !todo.done"
>
<span :class="{ done: todo.done }">{{ todo.text }}</span>
</li>
</ul>
</div>
</template>
<style scoped>
.done {
text-decoration: line-through;
color: #999;
}
</style>
実践的なユースケース
1. Piniaを使った状態管理
// stores/counter.ts
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0,
name: 'Vue'
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
async fetchData() {
const response = await fetch('/api/data')
// ...
}
}
})
<!-- コンポーネントでの使用 -->
<script setup lang="ts">
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>
<p>{{ counter.count }}</p>
<button @click="counter.increment">+1</button>
</div>
</template>
2. Vue Routerを使ったルーティング
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
// 遅延ロード
component: () => import('@/views/About.vue')
},
{
path: '/user/:id',
name: 'user',
component: () => import('@/views/User.vue'),
props: true
}
]
})
export default router
3. APIデータの取得とローディング状態
<script setup lang="ts">
import { ref, onMounted } from 'vue'
interface User {
id: number
name: string
email: string
}
const users = ref<User[]>([])
const loading = ref(true)
const error = ref<string | null>(null)
onMounted(async () => {
try {
const response = await fetch('https://api.example.com/users')
if (!response.ok) throw new Error('データの取得に失敗しました')
users.value = await response.json()
} catch (e) {
error.value = e instanceof Error ? e.message : '不明なエラー'
} finally {
loading.value = false
}
})
</script>
<template>
<div>
<div v-if="loading" class="loading">読み込み中...</div>
<div v-else-if="error" class="error">{{ error }}</div>
<ul v-else>
<li v-for="user in users" :key="user.id">
{{ user.name }} ({{ user.email }})
</li>
</ul>
</div>
</template>
まとめ
Vue.jsは、その親しみやすさと高い性能を両立させた優れたフレームワークです。主なポイントをまとめると:
- 学習しやすい: HTMLベースのテンプレート構文で、直感的に理解できる
- 柔軟なスケーラビリティ: 小さなウィジェットから大規模SPAまで対応
- TypeScript対応: 型安全な開発が標準でサポート
- 豊富なエコシステム: Vue Router、Pinia、Nuxt.jsなど公式ツールが充実
- 活発なコミュニティ: 566人以上のコントリビューターによる継続的な開発
これからフロントエンド開発を始める方にも、React/Angularから移行を検討している方にも、Vue.jsは魅力的な選択肢です。まずは公式のインタラクティブチュートリアルから始めてみてはいかがでしょうか。