【Vue.js基礎】リスト表示・絞り込みフィルター・JSON読み込みをマスターしよう

2019.03.15

Vue.jsの導入方法:CDNと単一ファイルコンポーネント

Vue.jsを導入する方法は、大きく分けて2つあります。HTMLにCDNのリンクを直接記述する方法と、.vueファイルで管理する「単一ファイルコンポーネント(SFC)」を使う方法です。

記述するコードの基本的なルールはどちらも同じです。

CDNで使う 単一ファイルコンポーネントで使う
メリット
  • jQueryのように、またはその代替として簡単に導入できます
  • 小規模な開発や、既存Webサイトへの部分的な導入に最適です
  • HTML・CSS・JavaScriptを1ファイル(.vue)でまとめて管理できます
  • モジュールの管理が簡単です
  • 小規模から大規模な開発まで幅広く対応できます
デメリット
  • コードが複雑になると管理が大変になりがちです
  • 古いブラウザ(IEなど)では対応が必要な場合があります
  • 開発を始めるために環境構築が必要です
  • 「ビルド(コンパイル)」という作業が必要になります

単一ファイルコンポーネントでの開発方法に慣れておくと応用が効くため、今回はこちらの手順で進めていきましょう。

Vue CLIを使った環境構築

単一ファイルコンポーネントで開発を始めるなら、Vue CLIを使うのが最も簡単で一般的な方法です。

Vue CLIは、Vue.jsプロジェクトの土台を素早く作ってくれる、便利なコマンドラインツール(CLI)です。

1. Node.jsのバージョンを確認する

Vue CLI 3以降を使用するには、Node.jsが特定のバージョン以上(v8.9以上、v8.11.0以降推奨)である必要があります。
まずは、以下のコマンドで現在のバージョンを確認してみましょう。

$ node -v

2. Vue CLIをインストール

$ npm install -g @vue/cli

(補足:古い記事では vue-cli と書かれていることもありますが、それはバージョン2系です。3系以降とは別物なので注意してください。もし2系が入っている場合は、アンインストールしてから進めましょう。)

3. 新規Vueプロジェクトを作成

$ vue create filter-list

ここではプロジェクト名を「filter-list」として進めます。
コマンドを実行すると、対話形式でプロジェクトの設定が始まります。

Vue CLI プリセット選択画面

▲プリセット(設定の組み合わせ)を選択します。細かく設定したいので「Manually select features」を選びます(矢印キーで移動し、Enterキーで決定)。

Vue CLI 機能選択画面

▲プロジェクトに必要な機能を選びます。今回はSCSSを使いたいので、標準の「Babel」「Linter」に加え、「CSS Pre-processors」にもチェックを入れましょう(Spaceキーで選択)。

Vue CLI CSSプリプロセッサ選択画面

▲使用するCSSプリプロセッサを選択します。今回は「Sass/SCSS (with node-sass)」を選びます。

Vue CLI ESLint設定選択画面

▲ESLint(コードチェックツール)の設定を選びます。今回はシンプルに「ESLint with error prevention only」(エラー防止のみ)を選択します。

Vue CLI ESLint実行タイミング選択画面

▲ESLintを実行するタイミングを選びます。「Lint on save」(保存時)が便利です。

Vue CLI 設定ファイルの所在選択画面

▲BabelやESLintなどの設定をどこに保存するか選びます。「In dedicated config files」(専用の別ファイル)が管理しやすいのでおすすめです。

Vue CLI プリセット保存確認画面

▲今回選んだ設定を「プリセット」として保存するか聞かれます。今回は「No」で進めます。

Vue CLI パッケージ管理ツール選択画面

▲最後に、使用するパッケージ管理ツール(NPMかYarnか)を選びます。お好みのもので構いません。

Vue CLI インストール完了画面

▲この画面が出れば、プロジェクトの作成は完了です!お疲れ様でした。

$ cd filter-list

作成したプロジェクトのフォルダに移動し、以下のコマンドで開発サーバーを起動してみましょう。終了する時は Control + C です。

$ npm run serve

【基礎編】配列データからリストを表示する (v-for)

Vueプロジェクトのファイル構成図

1. コンポーネントの作成

まず、 /src/components/ フォルダに List.vue という新しいファイルを作成します。

2. コンポーネントの読み込み

最初からある HelloWorld.vue は使わないので、 App.vue が今作成した List.vue を読み込むように書き換えます。

/src/App.vue

<template>
  <div id="app">
    <List />
  </div>
</template>

<script>
import List from './components/List.vue'

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

3. リストを表示する (v-for)

List.vue に、リストのもとになるデータ(配列)と、それを表示するためのHTMLを記述します。
Vueでは v-for というディレクティブ(指示)を使って、配列データを繰り返し描画するのが基本です。

/src/components/List.vue

<template>
  <div>
    <ul>
      <li v-for="item in list" v-bind:key="item.id" v-bind:class="item.label">
        {{ item.date }}:{{ item.title }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'ItemList',
  data() {
    return {
      list: [
        {
          id: 3,
          title: 'タイトル3',
          label: 'paid',
          date: '2019-03-01'
        },
        {
          id: 2,
          title: 'タイトル2',
          label: 'free',
          date: '2019-02-01'
        },
        {
          id: 1,
          title: 'タイトル1',
          label: 'paid',
          date: '2019-01-01'
        }
      ]
    }
  }
}
</script>

【応用編1】リストの絞り込み機能(フィルター)を実装する

次に、表示されているリストをキーワードやラベルで絞り込める(フィルター)機能を実装してみましょう。

1. <script> の修正 (算出プロパティ)

computed(算出プロパティ)を使って、元の list データを加工した結果(絞り込まれたリスト)を動的に生成します。こうすることで、元のデータを汚さずに絞り込みが実現できます。

/src/components/List.vue

<script>
export default {
  name: "ItemList",
  data() {
    return {
      filterWord: "", // キーワード絞り込み用
      labelSet: "all", // 現在選択中のラベル
      list: [
        // (list配列の中身は省略)
      ]
    };
  },
  computed: {
    // タイトル絞り込み
    matchedWord() {
      return this.list.filter(el => {
        if (el.title.indexOf(this.filterWord) > -1) {
          return true;
        }
      }, this);
    },
    // ラベル絞り込み
    matchedLabel() {
      // まずキーワードで絞り込んだ結果(matchedWord)を、さらにラベルで絞り込む
      return this.matchedWord.filter(el => {
        if (this.labelSet !== "all") {
          if (el.label == this.labelSet) {
            return true;
          }
        } else {
          return true;
        }
      }, this);
    }
  }
};
</script>

2. <template> の修正

絞り込み用の入力欄(input)と選択欄(select)を追加します。v-model を使って、入力された値を data と連動させます。

/src/components/List.vue

<template>
  <div>
    <div>キーワード絞り込み:<input type="text" v-model="filterWord" /></div>
    <div>
      ラベル絞り込み:
      <select v-model="labelSet">
        <option value="all">全て</option>
        <option value="free">無料</option>
        <option value="paid">有料</option>
      </select>
    </div>
    <ul>
      <!-- v-forで回す対象を元の「list」から、絞り込み後の「matchedLabel」に変更します -->
      <li v-for="item in matchedLabel" :key="item.id" :class="item.label">
        {{ item.date }}:{{ item.title }}
      </li>
    </ul>
  </div>
</template>

【応用編2】外部のJSONファイルを読み込んで表示する (axios)

最後に、コンポーネント内に直接書いていたリストデータを、外部のJSONファイルから読み込むように変更してみましょう。

1. axiosのインストールと設定

Vueで外部のJSONファイルやAPIと通信する際は、axiosというライブラリを使うのが定番です。まずはインストールしましょう。

$ npm install --save axios

次に、Vueプロジェクト全体でaxiosを使えるように main.js に設定を追記します。

/src/main.js

import Vue from 'vue'
import App from './App.vue'

import axios from 'axios' // ここを追記
Vue.prototype.$http = axios // ここを追記

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

※もしエラーになるようなら、/src/main.jsを以下に変えて試してみてください。

import { createApp } from 'vue'
import App from './App.vue'

import axios from 'axios'

const app = createApp(App)
app.config.globalProperties.$http = axios
app.mount('#app')

2. JSONファイルの作成

先ほど List.vue に直接書いていた配列データを、別のJSONファイルとして保存します。
public フォルダの下に json フォルダを作成し、 list.json という名前で保存します。

/public/json/list.json

[
  {
    "id": 3,
    "title": "タイトル3",
    "label": "paid",
    "date": "2019-03-01"
  },
  {
    "id": 2,
    "title": "タイトル2",
    "label": "free",
    "date": "2019-02-01"
  },
  {
    "id": 1,
    "title": "タイトル1",
    "label": "paid",
    "date": "2019-01-01"
  }
]

3. JSONファイルの読み込み

List.vue を修正し、コンポーネントが作成されたタイミングでJSONを読み込むようにします。
created() フック(コンポーネントが作成された直後に実行される処理)内で、axiosを使ってJSONファイルを非同期で読み込み、その結果を list データに入れます。

/src/components/List.vue

<script>
export default {
  name: "ItemList",
  data() {
    return {
      filterWord: "", // キーワード絞り込み
      labelSet: "all", // 現在選択中のラベル
      list: [] // listは空の配列として初期化しておきます
    };
  },
  created() {
    // publicフォルダが基準(ドキュメントルート)になるので、パスは `/json/list.json` となります
    this.$http.get('/json/list.json') 
    .then((res) => {
      this.list = res.data
    })
  },
  computed: {
    // (computed部分は省略)
  }
};
</script>

よくある質問(FAQ)

Q1: Vue CLIのインストールやプロジェクト作成でエラーが出ます。

A1: いくつか原因が考えられます。まずはNode.jsとnpm(またはYarn)のバージョンが古い可能性がないか確認してください。node -v コマンドでバージョンを確認し、必要であればアップデートしましょう。また、npmのキャッシュが古い場合もあるので、npm cache clean --force を試してみるのも一つの手です。それでも解決しない場合は、表示されたエラーメッセージで検索すると、同様の事例が見つかることが多いです。

Q2: v-forv-bind:key はなぜ必ず必要なのですか?

A2: key は、Vueが各要素を「これは〇〇さんのデータだ」と識別するための目印(名札)のようなものです。リストの順番が入れ替わったり、一部が削除されたりした時に、Vueはこの key を頼りに、どの要素がどう変化したのかを効率的に追跡します。key がないと、Vueはリスト全体を最初から作り直そうとしてしまい、パフォーマンスが低下する原因になります。そのため、v-for を使う際は、item.id のような一意(ユニーク)な値を key に指定することが推奨されています。

Q3: 絞り込み処理を computed(算出プロパティ)ではなく methods で実装しても良いですか?

A3: 実装自体は可能ですが、computed を使う方が推奨されます。computed には「キャッシュ機能」があるためです。computed は、依存しているデータ(今回の例では listfilterWord)が変更された時にだけ再計算されます。一方、methods は呼び出されるたびに毎回処理が実行されます。絞り込みのような処理は、データが変わらない限り結果も変わらないため、computed を使って計算結果をキャッシュ(一時保存)しておく方が、無駄な処理が減りパフォーマンス的に有利になります。

参考資料

CONTACT

webサイト制作、デザインに関するご相談、御見積のご依頼など、弊社へのお問い合わせはこちら