1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Vue + Viteで始めるSPA開発入門】HTMLをコンポーネントに変換するまで

Last updated at Posted at 2025-05-13

はじめに

この記事ではVueを用いて、静的なHTMLをもとにSPA化および共通部品化を行う手順を示します。

Vueとは

Webアプリのフロントエンドフレームワークで、HTML内にフレームワーク独自のタグやスクリプトを記述した「コンポーネント」を作成することで、部品の再利用を促すことができるものです。
Reactに比べ学習コストが低い(らしい)点を、ChatGPTが推していました。

この記事でやらないこと、説明しないこと

  • CSSなどを用いた画面の装飾
  • アクセシビリティ対応
  • Vueのリアクティビティーを用いた実装
  • SPAの説明

SPA化 & Vue使用前のHTML

今回Vueで実装する画面のモックは以下の通りです。

index.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="next-button" onclick="location.href='./bill.html'">次へ</button>
    </body>
</html>
bill.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での開発着手前に、まずは以下のコマンドでプロジェクトを作成します。

zsh
npm create vite@latest

コマンド実行中に色々問われるので、それぞれ以下を入力・選択します。

  • Ok to proceed? : y
  • Project name : UseVue
  • Package name : usevue
  • Select a framework : Vue
  • Select a variant : JavaScript

実際のコンソールは以下の通り。

zsh
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

依存パッケージのインストール

プロジェクト作成時点では、依存するパッケージがダウンロードされていないので、以下のコマンドを実行します。

zsh
cd UseVue 
npm install

ダウンロード完了後、lsコマンドを実行してnode_modulesディレクトリが作成されていることを確認します。

zsh
ls
index.html		package-lock.json	public			src
node_modules	package.json		README.md		vite.config.js

DRY RUN

正しくプロジェクトに必要な一式が揃ったかを確認するため、以下のコマンドで開発サーバーを起動します。

zsh
npm run dev

コマンド実行後、コンソールに出力されるURLへアクセスし、下図のような画面が表示されることを確認します。
スクリーンショット 2025-05-13 11.17.53.png
確認できたら、コンソールでcontrol + Cを押してサーバーを停止します。

Vue Routerのインストール

VueではSPAのルーティング制御1を行う場合、Vue Routerを利用すると便利なので、以下のコマンドでインストールします。

zsh
npm install vue-router

実装

あとは淡々と実装を進めます。

共通部品作成

まずは、共通利用されている入力要素をコンポーネント化します。<template>タグでHTMLを書き出す点がポイントです。
なお、本記事では触れませんがJavaScriptやスタイルを同一ファイル内に記述する「単一ファイルコンポーネント」も作成できます。

src/components/InputName.vue
<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>
src/components/InputAddress.vue
<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>内でコンポーネントとして設定している点がポイントです。

src/views/Destination.vue
<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>
src/views/Bill.vue
<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の設定を実装します。

src/router.js
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.jsindex.htmlを以下のように変更します。

src/App.vue
<template>
  <router-view/>
</template>

<script>
export default {
  name: 'App'
}
</script>
src/main.js
import { createApp } from 'vue'
import { router } from './router.js'
import App from './App.vue'

const app = createApp(App)
app.use(router)
app.mount('#app')
index.html
<!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>

ファイル構成

実装後のファイル構成は以下のようになります。

zsh
├── index.html
└── src
    ├── App.vue
    ├── assets
    │   └── vue.svg
    ├── components
    │   ├── InputAddress.vue
    │   └── InputName.vue
    ├── main.js
    ├── router.js
    └── views
        ├── Bill.vue
        └── Destination.vue

node_modulesディレクトリなど、一部省略しています。

動作確認

実装が完了したので、再びコマンドラインからアプリを起動します。

zsh
npm run dev

実際の画面

ブラウザで開発サーバーにアクセスします。
image.png
きちんと入力フォームが画面に表示できています。
「次へ」ボタンを押すと...
スクリーンショット 2025-05-13 16.14.18.png
画面とURLの両方が切り替わりました。これでSPA化、コンポーネント化は完了です :exclamation:

終わりに

ざっくりと使ってみた感触では、PHPのIncludeと似た感じに思えました。
ルーティング設定で少しつまずいたところもありましたが、ChatGPTの言っていた通り、「実装するだけであれば」、比較的容易に習得できそうです。
公式手順に従うと、ユニットテストやE2Eテストのライブラリも初回プロジェクト作成時に導入可能に見えるので、それらテストの実装難度は今後改めて確認してみたいところです。

今回実装したコード等

以下GitHubに置いてありますので、興味のある方はこちらもご参考ください。

参考にさせていただいた記事

  1. ブラウザのURLとウェブサイトの表示(コンポーネントやコンテンツ)を対応させる仕組み。 2

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?