Vue Routerのナビゲーションガードについて

Vue.jsとVue Routerを使って最近作業しております。
そんな中で、
「ログイン出来ていない状態で、メニュー画面を参照した時にログイン画面へリダイレクトする」機能を実装しておりました。

当初は、ページが遷移した後にcreatedで状況判定後を行っていたのですが、エンジニアの先輩からナビゲーションガードを使うのもおすすめとお聞きして早速調べて作成しました。

ナビゲーションガードについて使ってみたいという方はデータをgithubに公開しております。
純粋にナビゲーションガードの確認ができるような設定になっておりますので、お気軽にご利用ください。
[ここから](https://github.com/yoshiblog-space/check_navgaurd)


環境としてはVue-cliを利用しております。
 Vue/cli : 4.5.9
 Vue-Router
 Vue

###ナビゲーションガードとは？
リダイレクトもしくはキャンセルによって遷移をガードするために主に使用されます。

簡単に言えば、次のページに移動する間にチェック機能が入るといったイメージです。

ルートナビゲーション処理は
 ・グローバル
 ・ルート単位
 ・コンポーネント内（コンポーネントについては[こちら](https://qiita.com/yoshiblog-space/items/7ea39f0b890e46566582))
の３つで使用が可能です。

全てのポイントに入れて作成したコードが下記になります。 


```JavaScript:/store/index.js
import Vue from "vue";
import VueRouter from "vue-router";
import Home from "../views/Home.vue";

Vue.use(VueRouter);
const TestComponent = { // アクセスさせない画面を持つコンポーネント
  render: function (h) {
    return h('div', 'hello ' + this.$route.params.id)
  },
  beforeRouteEnter(to, from, next) { // [3]
    console.log('component: beforeRouteEnter');
    next();
  },
  beforeRouteUpdate(to, from, next) { // [4]
    console.log('component: beforeRouteUpdate');
    console.log(to.params.id)
    if (to.params.id === 'hanako'){
      alert("ハナコぉぉぉ");
      next('/');
    }else{
      next();
    }

  }
};

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home
  },
  {
    path: '/about',
    name: "About",
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ "../views/About.vue"),
    beforeEnter: (to, from, next) => { // [2]
      console.log('router: beforeEnter');

      next();
    }
  },
  {
    path: '/user/:id', component: TestComponent, 
    beforeEnter: (to, from, next) => { // [2]
      console.log('router: beforeEnter');
      
      next();
    }
  },
];

const router = new VueRouter({
  routes
});

router.beforeEach((to, from, next) => { // [1]
  console.log('global: beforeEach');
  
  next();
});
export default router;
```

####グローバルでのナビゲーションガード

```JavaScript
router.beforeEach((to, from, next) => { // [1]
  console.log('global: beforeEach');
  
  next();
});
```
まず、コードの構成としては、

`router`:    Vuerouterのインスタンス

`beforeEach`: グローバルナビゲーションガードを使用するための関数

`to`: Route: 次にナビゲーションされる対象の ルートオブジェクト。
('ルートオブジェクト': to = this.$routeで置き換え表現ができるかと)

`from`: Route: ナビゲーションされる前の現在のルートです。

`next()`: パイプラインの次のフックに移動します。
もしフックが残っていない場合は、このナビゲーションは 確立 されます。ここで、false、または、遷移先を指定することが可能です。

###ルート単位のナビゲーションガード

```Javascript
    beforeEnter: (to, from, next) => { // [2]
      console.log('router: beforeEnter');

      next();
    }
```
内容については、定義名のみ`beforEnter`となり、内容はグローバルと変わりません。
ルートを指定する際に追加で記載することで可能になります。
特定のページを指定する際にはこれを使用しても良いかもです。

###コンポーネントのナビゲーションガード

```JavaScript
  beforeRouteEnter(to, from, next) { // [3]
    console.log('component: beforeRouteEnter');
    next();
  },
  beforeRouteUpdate(to, from, next) { // [4]
    console.log('component: beforeRouteUpdate');
    console.log(to.params.id)
    if (to.params.id === 'hanako'){
      // alert("ハナコぉぉぉ");

      next();
    }else{
      next();
    }
  },
  beforeRouteLeave(to, from, next) {
    console.log('component: beforeRouteLeave');
    console.log(to.params.id)
    next();
  }
```

コンポーネントのナビゲーションガイドは、3つあります。
サンプルでは、"user/taro/"と"user/hanako/"を用意しました。
同じコンポーネントで名前を切り替えるような形にしています。

`beforeRouteEnter`:コンポーネントを描画するルートが確立する前に呼ばれます。
　サンプルデータで言うとホームまたは、aboutのページからuser/~にアクセスする際にコンポーネントが新規で描画されるのでそのタイミングで呼び出されています。

`beforeRouteUpdate`:コンポーネントの再利用を行うときに呼び出されます。
サンプルで言うと"user/taro/"から"user/hanako"に切り替える時にはコンポーネントはそのまま再利用され、名前だけ置き換わる場合に利用されます。
この場合には、beforeRoutEnterは呼び出されないので、コンポーネントによる再利用の場合には両方設定することをお勧めします。

`beforeRouteLeave`:こちらは、違うページに遷移する際に呼び出されます。
ユーザーマニュアルでは、ユーザが保存されていない編集内容で誤って経路を離れるのを防ぐために使用すると記載があります。


###まとめ

今回は、ナビゲーションガードについてまとめてみました。
検証している上で、気になるとすれば、グローバルでのナビゲーションカードはコンポーネントでの使用についても必ず呼び出されているので（ルート単位は呼び出されない）、よっぽど必要なければグローバルでの使用は控えた良さそうに感じます。



####実際に実装をしてみて、、、

````JavaScript
  beforeRouteEnter(to, from, next) { // [3]
    console.log('component: beforeRouteEnter');
    next();
  },

```
今回色々と試した結果として
「コンポーネントのナビゲーションガード」を実装しました。
理解したと思っていたら、見事にハマってしまいました。
前提条件として、
ナビゲーションガードの判定結果にvuexのstoreを参照しようとしていました。
なので、thisを用いて下記のように記載しました。。

```  Java
 beforeRouteEnter(to, form, next) {
    if (!this.$store.getters.logOnUser.userName) {
      alert('ログイン情報がありません。');
      next('/login');
    }
    next();
  },
```

...あれ？　thisが呼び出されない。。
と思ったら、しっかりVue Routerのガイドに記載されていました。


`この beforeRouteEnter ガードは this へのアクセスはできないです。なぜならば、ナビゲーションが確立する前にガードが呼び出されるからです。したがって、新しく入ってくるコンポーネントはまだ作られていないです。`
`しかしながら、 next にコールバックを渡すことでインスタンスにアクセスすることができます。このコールバックはナビゲーションが確立した時に呼ばれ、コンポーネントインスタンスはそのコールバックの引数として渡されます。`

```JavaScript
beforeRouteEnter (to, from, next) {
  next(vm => {
    // `vm` を通じてコンポーネントインスタンスにアクセス
  })
}
```
chromeのデベロッパーモードで`vm`をconsole.log上で確認すると
![スクリーンショット 2021-01-31 10.47.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/961363/c446a9c9-c8b0-fac1-9dc8-5b48ca3addea.png)

という感じで、$storeも参照できることを確認出来ました。

最終の実装はこちら

```JavaScript
  beforeRouteEnter(to, form, next) {
    next((vm) => {
      if (!vm.$store.getters.logOnUser.userName) {
        alert('ログイン情報がありません。');
        next('/login');
      }
    });
  },
```

無事ナビゲーションガードを活用して実装が出来ました。
コンポーネントの`beforeRouteEnter`を使用する際はご注意ください^^