15
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-01-30

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

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

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

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

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

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

ルートナビゲーション処理は
・グローバル
・ルート単位
・コンポーネント内(コンポーネントについてはこちら)
の3つで使用が可能です。

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

/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;

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

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

まず、コードの構成としては、

router: Vuerouterのインスタンス

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

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

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

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

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

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

      next();
    }

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

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

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

###まとめ

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

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

  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`を使用する際はご注意ください^^



15
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?