概要
vueとfirebaseを用いてログインを実装。
- ログイン
- ルーティング
ログインが確認できたら特定のページにルーティング。
その際、ユーザーをセットするまでに時間がかかりルーティングに待ちが生じる。
この待ち時間にローディング画面を入れたい。
ライブラリ
- firebase
- vuex
- vue-router
- vue-loading
※それぞれ細かい解説は省く。
ログイン
ログインはGoogleの認証を使用。
認証画面に自動で飛ぶため、ログインユーザーを選択。
※vuexより抜粋
// vuex/actions
login() {
const provider = new firebase.auth.GoogleAuthProvider();
// 自動でグーグルの認証画面
firebase.auth().signInWithRedirect(provider);
},
firebaseのonAuthStateChangedメソッドで現在ログインしているユーザーを取得。
ログインユーザーをstate(vuex)にセット。
// App.vue
created() {
// ログインログアウトを検知
// ログイン時とログアウト時にユーザーオブジェクトが入る
firebase.auth().onAuthStateChanged(user => {
if (user) {
//ユーザーをセット
this.setLoginUser(user);
// ログアウト時
} else {
//セットしたユーザーを削除
this.deleteLoginUser();
}
});
},
一応セット用のコード
// vuex/actions
setLoginUser({ commit }, user) {
//mutationsでstateのlogin_userでuserをセット
commit("setLoginUser", user);
},
ルーティング
ユーザーがログインしたのち特定のページへルーティング。
先ほどのコードに少し加えるだけ。
// App.vue
created() {
// ログインログアウトを検知
// ログイン時とログアウト時にユーザーオブジェクトが入る
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setLoginUser(user);
// ルーティング
if (this.$router.currentRoute.name === "home") {
this.$router.push({ name: "input" });
}
// ログアウトした時
} else {
this.deleteLoginUser();
}
});
},
この処理を実行した時inputへページ遷移はできるもののユーザーのセットに時間がかかりルーティングまでに待ちが生じる。
ローディング画面
vue-loadingを使用。
導入方法などは【Vue.js】ローディング画面の実装方法(サンプルコード付き)を参照。
// vuex/state
const state = {
loading: false
};
loadingの値によってローディング画面をトグル。
<template>
<div id="app">
<Loading v-show="loading"></Loading>
<div class="container" v-show="!loading">
<Header></Header>
<div class="content">
<router-view></router-view>
</div>
</div>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed(){
...mapState("loading", ["loading"])
}
</script>
あとはactionsでloadingの値をトグルするだけだが、どこで実装すればいいかわからなかった。
色々試した結果行き着いたのがナビゲーションガードによるルーティング処理。
試したこと
loadingの初期値はfalseなため、loadingを発動させるにはtrueにする必要がある。
したがってルーティングする前にloadingの値を変えれば良い。
userオブジェクトの確認後セットされるタイミングと考えた。
ここで詰まったため動かなかったコードを先に紹介。
// App.vue
created() {
// ログインログアウトを検知
// ログイン時とログアウト時にユーザーオブジェクトが入る
firebase.auth().onAuthStateChanged(user => {
if (user) {
//ユーザーをセット
this.setLoginUser(user);
// actionsによりloadingの値を変更
this.setLoading(true);
// ルーティング
if (this.$router.currentRoute.name === "home") {
this.$router.push({ name: "input" });
}
// ログアウト時
} else {
//セットしたユーザーを削除
this.deleteLoginUser();
}
});
},
次にloadingの値をfalseにしてloading画面を終わらせる必要がある。
こちらも試行錯誤したが、最終的になんてことなかったので正解のコードを紹介。
// input.vue
// loading画面を無効
beforeRouteEnter(to, from, next) {
next(vm => {
vm.setLoading(false);
});
},
こちらの2セットで動くと考えたがどうやらローディング画面が一瞬で終わってしまう。
動きとしては,
1. ログインユーザーを選択
2. homeコンポーネントの画面で固まる
3. 一瞬ローディングが入ってinputコンポーネントの画面に移る
結果
なぜこのような動きになってしまうのか、あくまで推測だがuserオブジェクトの処理に時間がかかっていたからである。
この処理が実行された後loadingの値を変更していたため、本来潰したかった待ちの時間が埋まらない結果に。
したがってuserオブジェクトを処理する前にloadingの値を変更する必要がある。
次に試してみてとりあえず動いたコード
// App.vue
created() {
// actionsによりloadingの値を変更
this.setLoading(true);
// ログインログアウトを検知
// ログイン時とログアウト時にユーザーオブジェクトが入る
firebase.auth().onAuthStateChanged(user => {
if (user) {
//ユーザーをセット
this.setLoginUser(user);
// ルーティング
if (this.$router.currentRoute.name === "home") {
this.$router.push({ name: "input" });
}
// ログアウト時
} else {
//セットしたユーザーを削除
this.deleteLoginUser();
}
});
},
userが入ってくる前にトリガーしているため、確かに思い通りに動く。
しかし一つだけ大きな問題がある。
初期描画
ログインユーザーが存在しない状態でアプリを開いたとする。
するとログインできないままloadingがtrueになってしまい、無限ローディングが始まってしまう。
解決策
なんとかゴリ押しで解決できた。
// App.vue
created() {
// actionsによりloadingの値を変更
this.setLoading(true);
setTimeout(() => {
this.setLoading(false);
}, 2100);
// ログインログアウトを検知
// ログイン時とログアウト時にユーザーオブジェクトが入る
firebase.auth().onAuthStateChanged(user => {
if (user) {
//ユーザーをセット
this.setLoginUser(user);
// ルーティング
if (this.$router.currentRoute.name === "home") {
this.$router.push({ name: "input" });
}
// ログアウト時
} else {
//セットしたユーザーを削除
this.deleteLoginUser();
}
});
},
初期描画の段階でローディングが入るものの、2.1秒後にloadingをfalseにする処理を入れることによってローディングを止めることができた。
あまりにパワープレイでダサいため気に入ってないが、私にはこれが限界である。
他にいい案があれば教えていただきたい。
気付き
nextの処理
// input.vue
// loading画面を無効
beforeRouteEnter(to, from, next) {
next(vm => {
vm.setLoading(false);
});
},
この beforeRouteEnter ガードは this へのアクセスはできないです。なぜならば、ナビゲーションが確立する前にガードが呼び出されるからです。したがって、新しく入ってくるコンポーネントはまだ作られていないです。
しかしながら、 next にコールバックを渡すことでインスタンスにアクセスすることができます。このコールバックはナビゲーションが確立した時に呼ばれ、コンポーネントインスタンスはそのコールバックの引数として渡されます。
書いてるままだが、beforeRouteEnterを使った場合でもvmを使うことによってインスタンスにアクセスできる。
以上。