Vue.js + axios + Vue Routerで作る!シンプルなSPAの作り方

SPA

この記事では、SPAがはじめてという方向けに、”Vue.js”と”Vue Router”、”axios”を使った、SPAの作り方を紹介します。

そのため、「SPAのアプリケーションを作ってみたいけれど、どこから手をつければ良いかわからない」という方向けの内容となっています。

今回作成するアプリケーションは、「Ajaxで自分のIPアドレスを取得し、表示する」ことを目標としています。

実装する機能としてはシンプルですが、「画面遷移」や「Ajaxによるデータ通信」といった、アプリケーションに欠かせない処理を含んでいます。

この記事を参考にSPAを作ってみて、Vue.jsでSPAを作ることが、どういった感じかを掴んでいただければと思います。

作成するアプリケーションのイメージを下記に記載します。

  • ホーム画面

  • IPを表示する画面

スポンサー

SPAとは何か?

SPAは、”Single Page Application(シングルページアプリケーション)”の略で、Webアプリケーションの設計方法の一つです。

SPAの特徴は、アクセス時に読み込むHTMLファイルは一つだけという点です。

最初にHTMLファイルを読み込んだ後は、Ajaxを使ってデータを取得し、画面内の要素を更新します。

通常のWebアプリケーションの場合、リクエストごとに画面全体をロードしなければなりません。

一方SPAでは、画面内で更新が必要な要素のみを置き換えます。

画面全体をロードする必要がないため、画面遷移中の表示を自由に変更したり、データの通信量を減らせたりする、というメリットがあります。

環境の説明

今回使用した環境は下記の通りです。

  • システムのバージョン: macOS 10.13.4
  • npmのバージョン: 6.2.0
  • Vue CLIのバージョン: 3.0.1

Vue.jsプロジェクトの作成

本記事中の、$(ドルマーク)で始まる内容は、CLIコマンドを意味します。
“ターミナル”で実行してください。

まずはSPAプロジェクトを作成します。

“vue init”を実行すると、質問が表示されるので、下記を参考に選択してください。

$ vue init webpack hello-spa

===============================================

? Project name hello-spa // EnterでOK
? Project description A Vue.js project // EnterでOK
? Author // EnterでOK
? Vue build // EnterでOK。「Runtime + Compiler」
? Install vue-router? No // のちほどバージョンを指定してインストールするため、「n」を入力
? Use ESLint to lint your code? No // ESLintは扱わないため、「n」を入力
? Set up unit tests No // テストは扱わないため、「n」を入力
? Setup e2e tests with Nightwatch? No // E2Eテストは扱わないため、「n」を入力
? Should we run `npm install` for you after the project has been created? (recommended) npm // npmを選択

プロジェクトが作成できたら、”hello-spaディレクトリ”に移動します。

$ cd hello-spa

ローカルサーバを起動します。

$ npm run dev

“アプリケーションの起動に成功した”というメッセージが出るので、表示されたURLにアクセスします。

I  Your application is running here: http://localhost:8080

次の画面が表示されていれば、プロジェクトのセットアップはOKです。

確認ができたら、でローカルサーバを停止しておきましょう。

画面遷移 – Vue Router

“Vue Router”は、”Vue.js”のプラグインで、ルーティングを管理するために使用します。

Vue Router をインストールする

使用するVue Rouerのバージョンは”3.0.1″です。

npmでインストールするバージョンを指定する場合は、”@”のうしろにバージョン番号を指定します。

$ npm install vue-router@3.0.1

ページ用コンポーネントを作成する

今回作成するアプリケーションは、”ホーム画面”と”自分のIPを表示する画面”の2画面構成です。

各画面に対応するコンポーネントを用意します。

まずは、コンポーネントファイルを格納する、”pages”ディレクトリを作成します。

$ mkdir src/pages
src
├── App.vue
├── assets
├── components
├── main.js
└── pages

src/pages配下に ホーム画面用の”Home.vue”と、IP表示画面用の”SearchIp.vue”を作成します。

src/pages
├── Home.vue
└── SearchIp.vue

“Home.vue”と”SearchIp.vue”は、それぞれ下記のコードを入力してください。

“Home.vue”

<template>
  <div id="home">
    <div>
      <h2>Vue.jsではじめるSPA</h2>
    </div>
  </div>
</template>

“SearchIp.vue”

<template>
  <div id="search-ip">
    <div>
      <p>ここにIPが表示されます</p>
      <div>
        <input type="button" value="IPを取得する">
      </div>
    </div>
  </div>
</template>

Vue Router の設定ファイルを用意する

次は、”Vue Router”の設定ファイルを用意します。

“srcディレクトリ”の直下に”router.js”を作成してください。

“router.js”の設定内容は下記の通りです。

“src/router.js”

import Vue from 'vue';
import VueRouter from 'vue-router';

import Home from '@/pages/Home.vue';
import SearchIp from '@/pages/SearchIp.vue';

Vue.use(VueRouter);

var router = new VueRouter({
  routes: [
    {
      path: '/',
      component: Home
    },
    {
      path: '/search_ip',
      component: SearchIp
    }
  ]
});

export default router;

“routes”キーの配列にオブジェクトを追加しました。

このオブジェクト内の”path”と”component”で、URLとコンポーネントが紐付けられます。

作成した”router.js”は、”src/main.js”で読み込みます。

次のコード中に、「この行を追加」とコメントしている処理を追加しましょう。

“src/main.js”

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router.js' // この行を追加

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router: router, // この行を追加
  components: { App },
  template: '<App/>'
})

まだ追加したコンポーネントを表示することはできません。

“router-link”タグとrouter-view”タグを使って、画面遷移用のリンクとコンポーネントを表示できるようにしましょう。

そのために、”src/App.vue”を次のように書き換えます。

<template>
  <div id="app">
    <router-link to="/">Home</router-link>
    <router-link to="/search_ip">Search IP</router-link>

    <router-view />

  </div>
</template>

<script>
export default {
  name: 'App',
  components: {
  }
}
</script>
  • ホーム画面
    “router-link”タグの”to”属性で遷移先のURLを指定しています。

  • IPの表示画面
    “router-view”タグは、画面用のコンポーネントに置換されます。

画面遷移の動作確認

以上で、ルーティングの設定が完了しました。

ローカルサーバを起動して、コンポーネントを表示できるかどうか確認します。

$ npm run dev

“http://localhost:8080” にアクセスして、”Home.vue”の内容が見えていればOKです。

画面上部の”Home”と”Search IP”のリンクをクリックすると、それぞれの画面が表示されます。

  • ホーム画面

  • IPの表示画面

Ajaxリクエスト – axios

画面の遷移ができるようになったので、次は「Ajaxを使ってデータの取得する」機能を実装してみましょう。

IPを表示する

本例では、”httpbin.org”にリクエストを送信し、返ってきたレスポンスを取得します。

その中から、自分のIPアドレスを取得して、画面に表示します。

httpbin.org

「IPを取得」ボタンが押されたときに呼び出されるメソッドを定義します。

“SearchIp.vue”を下記の実装に変更してください。

<template>
  <div id="search-ip">
    <div>
      <p v-text="ip">ここにIPが表示されます</p>
      <div>
        <input @click="getIp" type="button" value="IPを取得">
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'SearchIp',
    data() {
      return {
        ip: ''
      }
    },
    methods: {
      getIp() {
        this.ip = '111.111.111.111';
      }
    }
  }
</script>

“@click”で、ボタンのonClickと”getIp()”メソッドを紐づけています。

メソッドでは、”this.ip”を変更するようになっています。

“dataのip”は”v-text”を設定したpタグに表示されるため、「IPを取得」ボタンをクリックすると、”ここにIPが表示されます”が”111.111.111.111″に切り替わります。

これで、「IPを取得」ボタンに対応するメソッドが用意できました。

次はリクエストを送信して、自分のIPを取得する処理を追加します。

HTTPリクエストの送信には、“axios”を使用します。

まずは”npm install”で、”axios”をインストールしましょう。

$ npm install axios@0.18.0

axiosは”main.js”でインポートすることで、コンポーネント内で使用できるようになります。

“src/main.js”にaxiosのimport文を追加します。

“Vue.prototype.$axios”にインポートしたaxiosを設定します。

コンポーネント内では、”this.$axios”という書き方でaxiosを呼び出せるようになります。

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router.js'
import axios from 'axios' // この行を追加

Vue.config.productionTip = false

Vue.prototype.$axios = axios; // この行を追加

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router: router,
  components: { App },
  template: '<App/>'
})

“SearchIp.vue”に、axiosを使ったリクエストの送信処理を実装します。

実装内容は下記を参照ください。

<template>
  <div id="search-ip">
    <div>
      <p v-text="ip">ここにIPが表示されます</p>
      <div>
        <input @click="getIp" type="button" value="IPを取得">
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'SearchIp',
    data() {
      return {
        ip: 'ここにIPが表示されます'
      }
    },
    methods: {
      getIp() {
        this.ip = 'IPを取得しています';
        this.$axios.get('https://httpbin.org/get')
          .then((response) => {
            this.ip = response.data.origin;
          })
          .catch((reason) => {
            this.ip = 'IPの取得に失敗しました';
          });
      }
    }
  }
</script>

書き換えができたら、「IPを取得」ボタンを押してください。

“ここにIPが表示されます”のメッセージが、自分のIPのアドレスに更新されます。

今回の目標であった、”シンプルなSPA”の作成はここまで完了です。

ただ、このままだと見た目が寂しいので、デザインを変更します。

デザイン変更

ライブラリをインストールする

デザインの調整には、SASS(SCSS)を使います。

scssファイルをコンパイルするためには、ライブラリを追加する必要があります。

下記のnpmコマンドを実行して、インストールしてください。

$ npm install sass-loader node-sass --save-dev

CSSのリセットは、Bootstrapの”Reboot.css”を用います。

$ npm install bootstrap@4.1.3

アプリケーションに”Reboot.css”を読み込ませるために、scssファイルを用意します。

“src/assets/scss/styles.scss”を作成してください。

“styles.scss”には、”Reboot.css”のインポートを記述します。

@import "bootstrap/dist/css/bootstrap-reboot.min.css";

“src/main.js”で、”styles.scss”を読み込みます。

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router.js'
import axios from 'axios'
import './assets/scss/styles.scss'; // この行を追加

Vue.config.productionTip = false

Vue.prototype.$axios = axios;

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router: router,
  components: { App },
  template: '<App/>'
})

デザインを変更する

背景画像を設定します。

お好みの画像を、”src/assetsディレクトリ”に保存してください。

本例では、”src/assets/background.jpg”を使用します。

$ tree src/assets

==================
src/assets
├── background.jpg
├── logo.png
└── scss
    └── styles.scss

“App.vue”を下記の内容に変更します。

<template>
  <div id="app">
    <div class="bg-image">
      <ul class="navigation">
        <li class="navigation-link">
          <router-link to="/">Home</router-link>
        </li>
        <li class="navigation-link">
          <router-link to="/search_ip">Search IP</router-link>
        </li>
      </ul>

      <router-view class="main"/>

    </div>

  </div>
</template>

<script>
  export default {
    name: 'App',
    components: {}
  }
</script>

<style lang="scss">
  #app {
    height: 100vh;
  }

  .navigation {
    padding-top: 5%;
    display: flex;
    list-style: none;
    justify-content: flex-end;
  }

  .navigation-link {
    width: 100px;
    text-align: center;
    margin-right: 2rem;

    a {
      padding: 8px;
      color: #ffffff;
      text-decoration: none;
      display: block;
      font-size: 0.9rem;
      text-transform: uppercase;

      &.router-link-exact-active {
        border-bottom: 2px solid #e67e22;
      }

      &:hover {

        transition: border-bottom 0.2s;
        border-bottom: 2px solid #e67e22;

      }

    }

  }

  .bg-image {
    background-image: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url("assets/background.jpg");
    background-position: center;
    background-size: cover;
    height: 100%;
  }

  .main {
    height: 85%;
  }
</style>

“Home.vue”は次の内容にしてください。

<template>
  <div id="home">
    <div class="hero-text">
      <h2>Vue.jsではじめるSPA</h2>
    </div>
  </div>
</template>

<style lang="scss">
  #home {
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .hero-text {
    font-size: 2rem;
    color: #ffffff;
  }
</style>

最後に”SearchIp.vue”を更新します。

<template>
  <div id="search-ip">
    <div>
      <p v-text="ip" class="ip-text"></p>
      <div class="btn-area">
        <input @click="getIp"
               class="btn"
               type="button"
               value="IPを取得する">
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'SearchIp',
    data() {
      return {
        ip: 'ここにIPが表示されます'
      }
    },
    methods: {
      getIp() {
        this.ip = 'IPを取得しています';
        this.$axios.get('https://httpbin.org/get')
          .then((response) => {
            this.ip = response.data.origin;
          })
          .catch((reason) => {
            this.ip = 'IPの取得に失敗しました';
          });
      }
    }
  }
</script>

<style lang="scss">

  #search-ip {
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .btn-area {
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .ip-text {
    font-size: 2rem;
    color: #ffffff;
  }

  .btn {
    background-color: #ffffff;
    border-color: #ffffff;
    color: #e67e22;
    padding: 10px 20px;
    border-radius: .3em;
    cursor: pointer;
    display: block;

    &:hover {
      background-color: #e67e22;
      border-color: #e67e22;
      color: #ffffff;
    }

    &:focus {
      outline: 0;
    }
  }
</style>

デザインを調整した結果が、下記の画像です。

  • ホーム画面

  • IPの表示画面 表示前

  • IPの表示画面 表示後

まとめ

Vue.jsを使った、SPAの作り方について紹介しました。

今回作ったアプリが、SPAの作り方を理解する助けになれば幸いです。

まだ物足りない方は、APIを自作したり、画面遷移時にローディングを表示したり、思いつく変更を実装してみてください。

作成したアプリは、Firebaseで公開することができます。詳細については、次の記事を参照ください。

【Firebase】Vue.jsで作ったWebアプリを公開する方法を解説【初心者向け】
この記事ではFirebaseを使うメリットについて紹介します。また、Firebaseでどのくらい簡単にWebアプリを公開できるかの一例として、Vue.jsで作ったアプリをFirebaseのHostingで公開する手順について解説します。...

最後までお読みいただき、ありがとうございます。

もしこの記事が役に立ったと感じたら、はてなブックマークボタン等を押していただけると励みになりますので、ぜひお願いします。