はじめに
この記事ではVueを用いて、静的なHTMLをもとにSPA化および共通部品化を行う手順を示します。
Vueとは
Webアプリのフロントエンドフレームワークで、HTML内にフレームワーク独自のタグやスクリプトを記述した「コンポーネント」を作成することで、部品の再利用を促すことができるものです。
Reactに比べ学習コストが低い(らしい)点を、ChatGPTが推していました。
この記事でやらないこと、説明しないこと
- CSSなどを用いた画面の装飾
- アクセシビリティ対応
- Vueのリアクティビティーを用いた実装
- SPAの説明
SPA化 & Vue使用前のHTML
今回Vueで実装する画面のモックは以下の通りです。
<html>
<head>
<meta charset="UTF-8">
<title>Sample</title>
</head>
<body>
<h1>お届け先</h1>
<div>
<label>お名前</label>
<div>
<label for="family-name">姓</label>
<input type="text" id="family-name" name="family-name" autocomplete="family-name" placeholder="姓">
<label for="given-name">名</label>
<input type="text" id="given-name" name="given-name" autocomplete="given-name" placeholder="名">
</div>
</div>
<br/>
<div>
<label>お届け先住所</label>
<div>
<div>
<label for="postal-code">郵便番号</label>
<input type="text" id="postal-code" name="postal-code" autocomplete="postal-code" placeholder="郵便番号">
</div>
<div>
<label for="address-line1">都道府県</label>
<input type="text" id="address-line1" name="address-line1" autocomplete="address-line1" placeholder="都道府県">
</div>
<div>
<label for="address-line2">市区町村</label>
<input type="text" id="address-line2" name="address-line2" autocomplete="address-line2" placeholder="市区町村">
</div>
<div>
<label for="address-line3">番地</label>
<input type="text" id="address-line3" name="address-line3" autocomplete="address-line3" placeholder="番地">
</div>
</div>
</div>
<br/>
<button type="button" id="next-button" onclick="location.href='./bill.html'">次へ</button>
</body>
</html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample</title>
</head>
<body>
<h1>請求先</h1>
<div>
<label>お名前</label>
<div>
<label for="family-name">姓</label>
<input type="text" id="family-name" name="family-name" autocomplete="family-name" placeholder="姓">
<label for="given-name">名</label>
<input type="text" id="given-name" name="given-name" autocomplete="given-name" placeholder="名">
</div>
</div>
<br/>
<div>
<label>請求書送付先住所</label>
<div>
<div>
<label for="postal-code">郵便番号</label>
<input type="text" id="postal-code" name="postal-code" autocomplete="postal-code" placeholder="郵便番号">
</div>
<div>
<label for="address-line1">都道府県</label>
<input type="text" id="address-line1" name="address-line1" autocomplete="address-line1" placeholder="都道府県">
</div>
<div>
<label for="address-line2">市区町村</label>
<input type="text" id="address-line2" name="address-line2" autocomplete="address-line2" placeholder="市区町村">
</div>
<div>
<label for="address-line3">番地</label>
<input type="text" id="address-line3" name="address-line3" autocomplete="address-line3" placeholder="番地">
</div>
</div>
</div>
<br/>
<button type="button" id="back-button" onclick="history.back()">戻る</button>
<button type="button" id="submit-button">登録</button>
</body>
</html>
同一の入力フォームを持つ2画面を、Vueでコンポーネント化していきます。
実行環境情報
項目 | バージョン |
---|---|
OS | macOS Sequoia 15.4.1 |
Node.js | v23.11.0 |
npm | 10.9.2 |
Vite | 6.3.5 |
Vue | 3.5.13 |
Vue Router | 4.5.1 |
前提条件
- Node.jsをインストール済みであること
Vue使用前の事前準備
公式のクイックスタートではnpm create vue@latest
コマンドでプロジェクトを作成するよう記載されていますが、公式でも謳われている通り「Vite」の存在が基盤にあることを明示的に理解するため、あえて公式手順とは異なる方法で進行します。
Viteとは
フロントエンド開発体験を向上させることを目的とした、高速で動作するローカル開発サーバーです。
ソースの変更を監視して即時ビルドするため、デバッグやコード修正時にリアルタイムで画面を見ながら動作を確認できます。
本記事ではVueを使用しますが、Reactでも使えるライブラリです。
Viteプロジェクト作成
Vueでの開発着手前に、まずは以下のコマンドでプロジェクトを作成します。
npm create vite@latest
コマンド実行中に色々問われるので、それぞれ以下を入力・選択します。
- Ok to proceed? : y
- Project name : UseVue
- Package name : usevue
- Select a framework : Vue
- Select a variant : JavaScript
実際のコンソールは以下の通り。
Need to install the following packages:
create-vite@6.5.0
Ok to proceed? (y) y
> npx
> create-vite
│
◇ Project name:
│ UseVue
│
◇ Package name:
│ usevue
│
◇ Select a framework:
│ Vue
│
◇ Select a variant:
│ JavaScript
│
◇ Scaffolding project in /Users/yoda//UseVue...
│
└ Done. Now run:
cd UseVue
npm install
npm run dev
npm notice
npm notice New major version of npm available! 10.9.2 -> 11.3.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.3.0
npm notice To update run: npm install -g npm@11.3.0
npm notice
コマンド実行後のファイル構成は以下のようになります。
.
├── NotUseVue
│ ├── bill.html
│ └── index.html
└── UseVue
├── index.html
├── package.json
├── public
│ └── vite.svg
├── README.md
├── src
│ ├── App.vue
│ ├── assets
│ │ └── vue.svg
│ ├── components
│ │ └── HelloWorld.vue
│ ├── main.js
│ └── style.css
└── vite.config.js
7 directories, 12 files
依存パッケージのインストール
プロジェクト作成時点では、依存するパッケージがダウンロードされていないので、以下のコマンドを実行します。
cd UseVue
npm install
ダウンロード完了後、ls
コマンドを実行してnode_modules
ディレクトリが作成されていることを確認します。
ls
index.html package-lock.json public src
node_modules package.json README.md vite.config.js
DRY RUN
正しくプロジェクトに必要な一式が揃ったかを確認するため、以下のコマンドで開発サーバーを起動します。
npm run dev
コマンド実行後、コンソールに出力されるURLへアクセスし、下図のような画面が表示されることを確認します。
確認できたら、コンソールでcontrol + C
を押してサーバーを停止します。
Vue Routerのインストール
VueではSPAのルーティング制御1を行う場合、Vue Routerを利用すると便利なので、以下のコマンドでインストールします。
npm install vue-router
実装
あとは淡々と実装を進めます。
共通部品作成
まずは、共通利用されている入力要素をコンポーネント化します。<template>
タグでHTMLを書き出す点がポイントです。
なお、本記事では触れませんがJavaScriptやスタイルを同一ファイル内に記述する「単一ファイルコンポーネント」も作成できます。
<template>
<div>
<label>お名前</label>
<div>
<label for="family-name">姓</label>
<input type="text" id="family-name" name="family-name" autocomplete="family-name" placeholder="姓">
<label for="given-name">名</label>
<input type="text" id="given-name" name="given-name" autocomplete="given-name" placeholder="名">
</div>
</div>
</template>
<template>
<div>
<div>
<label for="postal-code">郵便番号</label>
<input type="text" id="postal-code" name="postal-code" autocomplete="postal-code" placeholder="郵便番号">
</div>
<div>
<label for="address-line1">都道府県</label>
<input type="text" id="address-line1" name="address-line1" autocomplete="address-line1" placeholder="都道府県">
</div>
<div>
<label for="address-line2">市区町村</label>
<input type="text" id="address-line2" name="address-line2" autocomplete="address-line2" placeholder="市区町村">
</div>
<div>
<label for="address-line3">番地</label>
<input type="text" id="address-line3" name="address-line3" autocomplete="address-line3" placeholder="番地">
</div>
</div>
</template>
画面作成
続いて、親となる画面を作成します。これも<template>
タグで書き出すところは先ほどのコンポーネントと変わりありませんが、独自の<inputName>
タグを用い、<script>
内でコンポーネントとして設定している点がポイントです。
<template>
<h1>お届け先</h1>
<inputName></inputName>
<br/>
<div>
<label>お届け先住所</label>
<inputAddress></inputAddress>
</div>
<br/>
<button type="button" id="next-button" @click="() => $router.push({path: './Bill'})">次へ</button>
</template>
<script>
import inputName from '../components/InputName.vue'
import inputAddress from '../components/InputAddress.vue'
export default {
components: {
inputName,
inputAddress
}
}
</script>
<template>
<h1>請求先</h1>
<inputName></inputName>
<br/>
<label>請求書送付先住所</label>
<inputAddress></inputAddress>
<br/>
<button type="button" id="back-button" @click="() => $router.push({path: './'})">戻る</button>
<button type="button" id="submit-button">登録</button>
</template>
<script>
import inputName from '../components/InputName.vue'
import inputAddress from '../components/InputAddress.vue'
export default {
components: {
inputName,
inputAddress
}
}
</script>
@click
属性はイベントハンドラの省略記法です。
本記事ではボタンクリック時に実行するスクリプトをインラインで実装しています。
ルーティング設定追加
次に、ルーティング1の設定を実装します。
import { createRouter, createWebHistory } from 'vue-router'
import Bill from './views/Bill.vue'
import Destination from './views/Destination.vue'
export const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
component: Destination
},
{
path: '/bill',
component: Bill
}
]
})
メイン実装修正
プロジェクト作成時、自動的に作成されるApp.vue
と、main.js
、index.html
を以下のように変更します。
<template>
<router-view/>
</template>
<script>
export default {
name: 'App'
}
</script>
import { createApp } from 'vue'
import { router } from './router.js'
import App from './App.vue'
const app = createApp(App)
app.use(router)
app.mount('#app')
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vue Sample</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
ファイル構成
実装後のファイル構成は以下のようになります。
├── index.html
└── src
├── App.vue
├── assets
│ └── vue.svg
├── components
│ ├── InputAddress.vue
│ └── InputName.vue
├── main.js
├── router.js
└── views
├── Bill.vue
└── Destination.vue
node_modulesディレクトリなど、一部省略しています。
動作確認
実装が完了したので、再びコマンドラインからアプリを起動します。
npm run dev
実際の画面
ブラウザで開発サーバーにアクセスします。
きちんと入力フォームが画面に表示できています。
「次へ」ボタンを押すと...
画面とURLの両方が切り替わりました。これでSPA化、コンポーネント化は完了です
終わりに
ざっくりと使ってみた感触では、PHPのIncludeと似た感じに思えました。
ルーティング設定で少しつまずいたところもありましたが、ChatGPTの言っていた通り、「実装するだけであれば」、比較的容易に習得できそうです。
公式手順に従うと、ユニットテストやE2Eテストのライブラリも初回プロジェクト作成時に導入可能に見えるので、それらテストの実装難度は今後改めて確認してみたいところです。
今回実装したコード等
以下GitHubに置いてありますので、興味のある方はこちらもご参考ください。
参考にさせていただいた記事