Vue.js

Vue.js入門

HTMLに近いテンプレートと、変化を自動で画面へ反映するリアクティビティを、実際にコードを動かしながら学びましょう。

Vue 3系、Composition API、<script setup>、Viteを対象にしています。

Vue.js入門の講座一覧

Node.jsの導入

Node.js公式ダウンロードページからLTS版を導入し、node --versionとnpm --versionを確認してください。その後、下のVite作成コマンドを実行します。

01

Vue.jsとは

Vue.jsは、Webページの画面を部品に分け、データの変化をUIへ反映しやすくするJavaScriptフレームワークです。HTMLに近いテンプレート、コンポーネント、リアクティビティを組み合わせて、入力フォームや管理画面、シングルページアプリケーションを作れます。

この講座の学習順序
  1. ViteでVueプロジェクトを起動する
  2. テンプレートとイベントで小さなUIを作る
  3. ref・computed・watchで状態を扱う
  4. コンポーネント、フォーム、API通信へ進む
  5. Router・Pinia・テストでアプリを整理する

Vueでは、状態から画面が決まる関係を宣言的に書きます。DOMを一つずつ探して書き換えるのではなく、データを変更するとVueが必要な部分を更新します。

02

環境構築

現在のVue公式ガイドに合わせ、Viteでプロジェクトを作成します。Node.jsをインストールしたターミナルで次のコマンドを実行してください。

プロジェクト作成SHELL
npm create vue@latest vue-practice
cd vue-practice
npm install
npm run dev
表示される例OUTPUT
Local: http://localhost:5173/

質問画面では、初めてならTypeScript、Router、Pinia、テストは一度Noにしても構いません。この講座の後半では、それぞれを使う理由と導入方法を扱います。最初はsrc/App.vueを編集し、ブラウザーを更新して結果を確認します。

うまく起動しないとき
node --versionとnpm --versionでNode.jsが認識されるか、コマンドを実行したフォルダーが正しいかを確認します。エラーの最後の数行を読み、依存関係のインストールを省略していないか確認してください。
03

テンプレートと基本文法

Vueのテンプレートでは、二重中括弧で値を表示し、v-bindやv-onなどのディレクティブで属性やイベントを指定します。短縮記法もありますが、最初は意味を理解してから使いましょう。

src/App.vueVUE
<script setup>
import { ref } from 'vue'

const name = ref('Mika')
const imageUrl = 'https://example.com/avatar.png'
</script>

<template>
  <main>
    <h1>こんにちは、{{ name }}さん</h1>
    <img :src="imageUrl" alt="プロフィール画像">
  </main>
</template>
画面に表示される内容OUTPUT
こんにちは、Mikaさん
画像が指定したURLから読み込まれる

:srcはv-bind:src、@clickはv-on:clickの短縮記法です。HTML属性へ文字列を固定で書くか、JavaScriptの値を結び付けるかを意識してください。

条件・繰り返し・クリックVUE
<script setup>
import { ref } from 'vue'
const visible = ref(true)
const fruits = ref(['りんご', 'みかん', 'ぶどう'])
function toggle() { visible.value = !visible.value }
</script>

<template>
  <button @click="toggle">表示を切り替える</button>
  <p v-if="visible">表示中です</p>
  <ul><li v-for="fruit in fruits" :key="fruit">{{ fruit }}</li></ul>
</template>

v-ifは条件に応じて要素を作ったり外したりします。v-showは要素を残したまま表示・非表示を切り替えます。v-forでは、並べ替えや削除に対応できる安定した:keyを指定します。

04

リアクティビティと状態

Vueのリアクティビティは、状態を読み取った場所を追跡し、状態が変更されたときに必要な処理を実行する仕組みです。プリミティブ値にはref、複数のプロパティを持つオブジェクトにはreactiveを使います。

refとcomputedVUE
<script setup>
import { ref, computed } from 'vue'

const price = ref(1200)
const quantity = ref(2)
const total = computed(() => price.value * quantity.value)
</script>

<template>
  <input v-model.number="quantity" type="number" min="1">
  <p>合計: {{ total }}円</p>
</template>
操作結果OUTPUT
初期表示: 合計: 2400円
数量を3に変更: 合計: 3600円

テンプレート中ではrefの.valueが自動的に展開されますが、JavaScriptの処理中ではquantity.valueと書きます。計算結果はcomputedにし、ボタン操作や通信などの副作用を入れないのが基本です。

computedとwatchの使い分け
別の状態から値を計算するならcomputed、状態の変化をきっかけに通信・localStorage・ログ出力など外部へ作用させるならwatchを使います。
watchとライフサイクルVUE
import { ref, watch, onMounted } from 'vue'

const keyword = ref('')
watch(keyword, (newKeyword, oldKeyword) => {
  console.log(`${oldKeyword} から ${newKeyword} へ変更`)
})
onMounted(() => {
  console.log('画面がDOMへ追加されました')
})

onMountedは初回描画後、onUnmountedはコンポーネントが破棄されるときに実行します。タイマーやイベント購読を開始したら、終了時に解除することも忘れないでください。

05

コンポーネントとProps

画面を再利用できる部品へ分けると、ファイルが読みやすくなり、テストもしやすくなります。親から子へ値を渡すのがProps、子から親へ出来事を伝えるのがイベントです。

TodoItem.vueVUE
<script setup>
defineProps({
  todo: { type: Object, required: true }
})
const emit = defineEmits(['remove'])
</script>

<template>
  <li>
    {{ todo.text }}
    <button @click="emit('remove', todo.id)">削除</button>
  </li>
</template>
親から使うVUE
<TodoItem
  v-for="todo in todos"
  :key="todo.id"
  :todo="todo"
  @remove="removeTodo"
/>

子コンポーネントがPropsを直接書き換えるのではなく、イベントで親へ依頼します。データの所有者を一つに決めると、どこで値が変わったのか追跡しやすくなります。さらに共通の外枠を差し替えたい場合は<slot>を使います。

06

フォームとAPI通信

v-modelは入力値と状態を同期します。v-model.trimやv-model.numberで入力値を整えられますが、送信時にも必ず検証してください。

入力検証VUE
const title = ref('')
const message = ref('')

function submit() {
  const value = title.value.trim()
  if (value === '') {
    message.value = 'タイトルを入力してください'
    return
  }
  message.value = `「${value}」を登録しました`
}
実行結果OUTPUT
空欄で送信: タイトルを入力してください
Vue入門で送信: 「Vue入門」を登録しました

API通信では、読み込み中・成功・失敗を別の状態として扱います。HTTPステータスを確認し、コンポーネントが破棄されたときに通信を中断できるようにすると、より安全です。

fetchの基本形VUE
const items = ref([])
const status = ref('idle')
const errorMessage = ref('')

async function loadItems() {
  status.value = 'loading'
  try {
    const response = await fetch('/api/items')
    if (response.ok === false) throw new Error('HTTPエラー')
    items.value = await response.json()
    status.value = 'success'
  } catch (error) {
    errorMessage.value = error.message
    status.value = 'error'
  }
}
07

Router・Pinia・テスト

複数画面のSPAには公式ルーターのVue Router、複数コンポーネントで共有する状態にはPiniaを使います。どちらも最初から導入するのではなく、ページ間の移動や共有状態が必要になった段階で追加します。

Vue Routerの最小例VUE
import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'

export default createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
    { path: '/about', component: About }
  ]
})
テストの考え方VITEST
import { expect, test } from 'vitest'

function total(price, quantity) {
  return price * quantity
}

test('合計金額を計算する', () => {
  expect(total(1200, 2)).toBe(2400)
})

Piniaでは状態・getter・actionをストアにまとめます。コンポーネントの表示、ユーザー操作、API通信の責務を分け、純粋な計算は小さな関数としてテストすると、変更の影響を確認しやすくなります。

08

次に学ぶこと

各講座で作ったカウンター、Todo、フォームを組み合わせ、入力・一覧・詳細表示のある小さなアプリを作ってみましょう。その後、TypeScriptでPropsやAPIデータの型を付け、アクセシビリティ、認証、デプロイへ進みます。