前書き
自分はバックエンドエンジニアであるが一度フロントエンドの思考に触れたいと考えていたため、冬休みを利用してUdemyのコースを受講した。本投稿ではその後半(全12章中1−10章)の個人的記録である。後半はVue+Firebaseを使ったアドレス帳アプリケーションの実装とデプロイハンズオンレクチャーである。
環境
特筆すべき点として、動画に合わせてvuetifyは1.5.5を使った。
11. アドレス帳アプリケーションの作成
VuetifyによるUIコンポーネントの作成
-
UIコンポーネントのライブラリはVuetifyを使う(ElementUI, Bootstrapなどが他のvue.jsに特化したライブラリ)。導入はvue cliで
$ vue add vuetify
- ツールバー:
<v-appbar></v-appbar>
を利用(vuetifyより) - サイドメニュー:(vuelidateのNavigationDrawerのスクリプトをコピーして引っ張ってきた)
- ツールバー:
-
コンポーネント間で値を共有するのに使えるのが
Vuexのストア
。VuexとはVue.jsで状態の管理を行うためのモジュール。( 今まで見てきたのは親子間でのデータの共有)。src/store/index.js
で管理する。Vue.user(Vuex)
で有効化。 -
ヘッダー左のmenuボタンで開閉するサイドメニューの作成。Vuexを用いる。まずSideNav.vueで
<v-navigation-drawer v-model="$store.state.drawer" absolute temporary>
このv-modelとはmenuの開閉の状態を表すbooleanだ。-
dispatcherメソッドを使う方法。つまり、App.vueで
<v-app-bar-nav-icon @click="openSideMenu">
し、このopenSideMenuメソッド内でthis.$store.dispatch('toggleSideMenu')
-
mapActionsメソッドを使うよりスマートな方法。App.vueで
import { mapActions } from 'vuex'
し、これをmethods内で分割代入。methods: { ...mapActions(['toggleSideMenu']) }
これをクリック時に<v-app-bar-nav-icon @click="toggleSideMenu">
で呼び出す。
-
dispatcherメソッドを使う方法。つまり、App.vueで
-
連絡先一覧テーブルおよびそのページ作成。Vuetifyの
<v-data-table :headers='headerの配列' :items='データの配列'>
を使う。 -
サイドバーからhomeページ、連絡先一覧ページそれぞれへのリンクを有効にした。
<v-list-item v-for="(item, index) in items" :key="index" :to="item.link">
を使った。 -
連絡先追加ページの作成。連絡先一覧ページからのジャンプボタンと連絡先一覧ページへのキャンセルボタンを設置した。
- このページのpathはrouterにおいて
path: '/addresses/:address_id?/edit'
で指定した。?はこのaddress_idがオピションの値であることを表す。address_idなしへのリンクは<router-link :to="{ name: 'address_edit' }">
で実装可能。 - キャンセルボタンでのページジャンプは
<v-btn @click="$router.push({ name: 'addresses' })">キャンセル</v-btn>
で実装可能。
- このページのpathはrouterにおいて
Firebaseによるユーザ認証(ログイン、ログアウト機能)
下準備
- Firebase指定のスクリプトを
public/index.html
のbodyタグの下部、他のjsを読み込む前にコピペする。 - これをvue.jsの利用に適した形にいじる。すなわち
$npm install firebase
し、src/main.js
でインポートかつ先ほどのスクリプトでInitialize Firebaseする。(public/index.html
のコピペは消す) - FirebaseのwebコンソールのAuthenticationでgoogle認証を有効化する。
ログイン、ログアウトの仕組み
Googleアカウントでログイン認証を実装する。これはloginアクション
が呼ばれたタイミングでgoogleのGoogleの認証ページにリダイレクトされ、認証成功したらまたアプリケーションに自動的に戻ってくる。そこでログインユーザの情報をGoogleからFirebase経由で(?)受け取る。
- AppVueのcreatedメソッド:
firebase.auth().onAuthStateChanged(~~~)
というオブザーバーを置いている。これはfirebaseと通信し、そのuserのログイン/ログアウト状態が切り替わったら発動されるメソッドである。 - vuex-storeの
login(), logout()メソッドはこのfirebaseと通信し、そのユーザのlogin/logoutの状態変更をする。 - vuex-storeのsetLoginUser, setLogoutUserは動作中フロントエンドにおけるstate.login_userの切り替えにつかわれる。
これらを利用して、以下の手順でログイン、ログアウト機能が作動する。
login() ==> firebase.auth().onAuthState()[オブザーバが検知] =>setLoginUserでstate.login_userを設定する。
logout() ==> firebase.auth().onAuthState()[オブザーバが検知] => setLogoutUserでstate.login_userをnullにする。
ログイン状態のルートの制御
- ユーザログイン時に連絡先一覧ページに飛ばす。ログアウト時にはホームのログインのページに飛ばす。
- ログインしている場合のみサイドメニューを表示するためのアイコンを表示する。
Firebaseによるデータの永続化
firebaseのルールは
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId}/addresses/{addressId} {
allow read, update, delete: if request.auth.uid == userId;
allow create: if request.auth.uid != null;
}
}
}
-
ユーザが連絡先を新規登録時にfirebaseのデータベースにデータを保存する。すでにvuex-storeに格納するアクションは用意できているので、そこにcloud firestoreに保存する処理**if (getters.uid) firebase.firestore().collection(`users/${getters.uid}/addresses`).add(address)**を追加する。ここで使うuser-idはログインユーザのオブジェクト内にuidという名前で持っている。
-
ユーザーがログイン時に連絡先一覧データをデータベースから取ってくる。これはvuex-storesの
fetchAddressesアクション:
**firebase.firestore().collection(`users/${getters.uid}/addresses`).get().then(snapshot =>{
snapshot.forEach(doc => commit('addAddress', doc.data()))**を定義し、これをfirebase.auth().onAuthStateChanged()
から呼び出す。 -
個々の連絡先の編集ページを作る。
- まずは個々の連絡先オブジェクトに固有のidを与える。これは
addAdresssアクション
で**if (getters.uid) firebase.firestore().collection(`users/${getters.uid}/addresses`).add(address).then(doc => { commit('addAddress', {id: doc.id, address}) })**かつaddAddressミューテーション
でstate.addressesへのpush前にaddress.id = id
により実現可能。これに伴い、fetchAddressesアクション
でも同様にidを含めた処理を実装する。
- まずは個々の連絡先オブジェクトに固有のidを与える。これは
-
個々の連絡先編集ページへの遷移ボタンを連絡先一覧ページに仕込む。Addresses.vueを改造し、ボタンは
<router-link :to="{ name: 'address_edit', params: { address_id: item.id }}">
でよし。 -
個々の連絡先編集ページではすでに登録されている対応する連絡先のデータをfirebaseから取得し、フォームに反映している状態にする。このために、store/index.jsにおいて
getAddressById: state => id => state.addresses.find(address => address.id === id)
という、連絡先のidに一致する連絡先をstate.addressから取ってこれる関数を返す関数を定義する。これを、AddressFormコンポーネントのcreatedメソッドにおいて呼び出して、フォームにv-modelで反映する()。- 現在のページが示すaddressは
this.$route.params.address_id
で取得する。 - addressesの中に該当するaddressがなかった場合は連絡先一覧ページに遷移する。
- 現在のページが示すaddressは
-
個々の連絡先編集ページはupdate, 新規連絡先追加はaddにする。実装するのは以下。
- stores/index.jsのupdateAddressミューテーション
- stores/index.jsのupdateAddressアクション
- AddressForm.vueでmapActionsを分割代入する。連絡先追加ボタンが押された時、
this.$route.params.address_id
がnullではない場合には、updateAddressミューテーションを呼び出し(引数はアドレスidとaddressオブジェクト)、nullの時はaddAddressミューテーションを呼び出す。
-
連絡先一覧ページに、それぞれの連絡先の削除ボタンを入れる。削除ボタンを押した際には確認のconfirmダイアログが出るようにする。実装は以下。
- stores/index.jsのdeleteAddressミューテーション
- stores/index.jsのdeleteAddressアクション
- Address.vueでmapActionsを分割代入し、deleteAddressミューテーションを呼び出せるようにする(引数はアドレスid)。連絡先削除ボタンが押された時、deleteConfirmメソッドを介して(confirmダイアログでの確認)、これにユーザがYesを押すと、deleteAddressミューテーションを呼び出す。
12: Firebaseのホスティングを利用したアプリのデプロイ
$ npm install -g firebase-tools
$ firebase login
$ firebase init
# 設定(distを公開する設定など)
$ npm run build
$ firebase deploy
# ホスティングを停止するには、、、
$firebase hosting:disable
その他のメモ
cp -a dir newdir
でdirのコピーディレクトリnewdirができる。