Vue.js + VuexでQiitaの投稿を検索/一覧表示するWebアプリを作る

ここ最近、Vue.jsやVuexを学習しています。
そのなかでいくつか習作を作っているので、この記事ではとある制作物とその作り方を簡単に解説します。

作ったもの

Qiitaのビューワーアプリです。
https://qiita-viewer.netlify.com/

トップページの検索窓にキーワードを入力してEnterすると、そのキーワードのタグを含むQiitaの投稿を取得しリンクで一覧表示します。検索結果の下部にある「トップへ戻る」をクリックすると、再度初期表示に戻ります。URLは基本ひとつで、要素の表示/非表示で画面遷移っぽくしてます。

超シンプルなアプリですが、Vue.js + Vuex初心者にとっては、

  • Vuexのstateで検索状態を管理する
  • 外部APIを叩いて情報を取得 → 表示する

という、VueでWebアプリを作るうえでの基礎が地味に詰まってると思います。ゼロから作ると、特にVuexの理解が進むので、ぜひ興味がある方は以下の解説に沿って作ってみてください。

作り方の解説

早速、作り方を解説していきます。

vue-cliでプロジェクトを作成する

vue-cliをインストールして、任意のディレクティブプロジェクトを作ってください。このあたりについては、以下の記事などを参考に作ってみてください。

なお、ディレクトリ構成は基本デフォルトのままで大丈夫です。

Vue.js を vue-cli を使ってシンプルにはじめてみる – Qiita

見た目のマークアップ

ロジックの前にまずは見た目のマークアップをしていきます。

まずはトップの検索画面から。簡単のために、styleの表記は省略しています。
headerは使い回すかわかりませんが、拡張性を考慮しとりあえずコンポーネント化しておきます。

こちらはheaderコンポーネント。styleがないとめちゃくちゃシンプルですね。

ひとまず、トップの検索画面はできました。

次は、検索結果を表示するリストをマークアップしていきましょう。こちらはApp.vueに直接追加していきます。
また、初期表示ではヘッダおよびリストは非表示にしたいので、v-ifディレクティブを使って非表示にしておきましょう。

47〜49行目を見ると、isSearchShow や isHeaderShow というコンポーネント側に記述したデータを記載しています。この条件についてtrue、falseで切り替えることで要素の表示/非表示を制御します。

条件付きレンダリング — Vue.js 

v-ifディレクティブで要素の表示/非表示を制限するのは、いわゆるWebサイトを作る上での使えるテクニックなので覚えておくといいでしょう。

イベントを用意し、画面遷移っぽい振る舞いにする

今は検索キーワードを入力してEnterしても何も起こりませんが、Enterにイベントハンドラを用意し画面が切り替わる(ように見える)ようにしましょう。

そこで、inputタグに@keyup.enterのイベントを、「トップへ戻る」要素に@clickのイベントをそれぞれ用意します。

61〜73行目のmethodsに、各イベント発火時の処理を書いておきます。主に要素の表示/非表示を切り替えています。
「トップへ戻る」を押した時、入力フィールドのテキストを空にしておくのも忘れずにおこないます。

また、liタグは検索して返ってきた数に応じて表示したいので、v-forでレンダリングするようにしておきます。

store.jsの用意

見た目のコーディングができたので、ロジックを作っていきます。

アプリの状態管理に関する記述をしていくため、Vuexでの状態管理に欠かせないストアを作っていきます。具体的には、src/store.js内に新たにストアオブジェクトを作っていきます。

Vuex 入門 | Vuex

ストアオブジェクト内にstate、actions、getters、mutationsというオブジェクトがありますが、順次ここに状態管理のための記述をしていきます。

初期状態として、stateを登録しておきます。stateは「信頼できる唯一の情報源」とされており、アプリ全体で保持しておきたい状態を設定できます。

このアプリでは、どんどん入れ替わっていく検索キーワード検索結果を保持し参照したいので、それぞれkeyword(テキスト)とarticles(オブジェクト)としてstateに登録しておきます。初期状態は空にしておきます。

QiitaのAPIを叩いて記事情報を取得する関数を用意

状態管理のための記述の前に、QiitaのAPIを叩いて記事を取得する処理を関数にまとめ、store.js内に記述しておきます。

7〜19行目に、Qiitaから記事を取得するための関数getContentsを追加しました。引数queryで検索キーワードを渡し、そのキーワードに基づいてAPIを叩きます。

取得した結果は return res.data としてJSON形式で返します。

外部APIから呼び出し後続の処理へと進めていくため、async/awaitを使っています。また、呼び出しにはaxiosを使っているので、事前に yarn add axios などでインストールしておいてください。

async/awaitやaxiosの使い方についてはVue.jsの本筋とは逸れるので気になったら以下の記事などを読んでみてください。

async/await 入門(JavaScript) – Qiita
[axios] axios の導入と簡単な使い方 – Qiita

注意
今回はQiitaのAPIを叩く際に、認証情報を持たせていないので1時間あたり60回のリクエストを超えるとエラーになりますのでご注意ください。

Mutationをmutation-type.jsに記述する

次にMutationを作っていきます。
Mutationとは、ストア内のstateを実行するための唯一の方法です。このアプリでは、Mutationをコミットすることで、stateのキーワードや検索結果をどんどん更新していきます。

Mutationはメンテナンス性を考慮し、mutation-type.jsという外部ファイルに記述し、exportして使います。

‘SEARCH’ や ‘UPDATE_KEYWORD’という、Mutationと同名の定数を作ります。この定数を呼び出すことでミューテーションをコミットします。

また、mutation-type.jsはstore.jsや各コンポーネント内でimportする必要があるので、忘れないでください。

inputタグからEnterされたら関数を実行し、stateに保持する

関数が用意できたところで、inputタグのEnterイベントにこの関数を実行するトリガーを用意し、APIを叩いて記事取得を実行、さらにその情報をstateに保存していきます。

MuttionをコミットするにはActionsを用意する必要があります。
ActionはMutationと少し似てますが役割は異なっていて、Mutationをコミットする働きがあります。また、任意の非同期処理を含めることができるので、外部APIと通信する際などに必要となります。

28〜38行目にactionsを記述しました。[UPDATE_KEYWORD]や[SEARCH]はMutasionを定義するときに使いましたね。commit の記述からも分かるように、actionsはこれらのMutationsを実行するためのものです。

また、[SEARCH]の記述に注目すると、getContents関数が呼ばれ、引数として state.keywordが渡されていることがわかります。要は、stateに新たに入った新キーワードをこの関数に渡して、それをもとに検索をおこなうことを示しています。

actionsが定義できたので、次はmutationsの定義を書いていきます。

42〜49行目に追記しました。

[UPDATE_KEYWORD]ではstateのkeywordに新たなkeywordを、[SEARCH]ではstateのarticlesに新たなarticlesを代入していきます。このように、検索を繰り返すことでどんどんstateを更新していくというわけです。これが状態管理の肝ですね。

次に、コンポーネント側にこれらが実行されるようにinputタグにメソッドを追加していきます。

65〜66行目のmethodsに注目してください。

mapActions という記述で[UPDATE_KYWORD]、[SEARCH]を呼び出しています。 mapActionsはヘルパー関数と呼ばれ、コンポーネントのメソッドをstore.dispatchにマッピングすることができます。

this.$store.dispatch('xxx') のように書くのが基本ですが、mapActionsを使うと短く楽に記述できるんですね。使用する際には、コンポーネントにmapActionsを読み込んでおく必要があるので注意してください。この部分の説明を結構端折っているので、Vuexの公式ドキュメントを読んでみてください。

アクション | Vuex 

取得した記事情報をコンポーネント側で表示する

無事APIを叩いて記事が取得できたら、コンポーネントに表示していきましょう。

68〜70行目を見てください。

算出プロパティに、...mapGetters という記述があります。これは、ストアのGetterをコンポーネントで使用するための記述です。コンポーネント側にこのようにマッピングしておくと、keywordの値や、searchの値を使用できるようになります。

ゲッター | Vuex

コンポーネント側でmapGettersを使うために、store.jsにgettersを定義しておきます。

こうしておくことで、コンポーネント側で{{article.title}} や{{ keyord }}などのように、各stateの値をデータバインディングして表示することができます。基本的にストアはリアクティブなので、stateの値が更新される度に、ここに描画される値も更新されていきます。

というわけで完成です。

まとめ

超駆け足で解説していきました。僕もまだ理解しながら作っている段階なので、曖昧な表記だったり適当な部分があるかもしれませんがご容赦ください。

なお、Vueは公式ドキュメントが豊富なことはよく知られてますが、サンプルが載った本もとても参考になるのでこれからVueを勉強しようと思ってる方には以下の本をおすすめします。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)