# ナビゲーションガード

この名前が示すように、 vue-router によって提供されるナビゲーションガードは、リダイレクトもしくはキャンセルによって遷移をガードするために主に使用されます。ルートナビゲーション処理 (グローバル、ルート単位、コンポーネント内) をフックする多くの方法があります。

パラメータまたはクエリの変更は enter/leave ナビゲーションガードをトリガーしない ということを覚えておいてください。それらの変更に対応するために $route オブジェクトを監視する、またはコンポーネント内ガード beforeRouteUpdate を使用するかの、どちらかができます。

# グローバルビフォーガード

router.beforeEach を使ってグローバル before ガードを登録できます。

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

いつナビゲーションがトリガーされようとも、グローバル before ガードは作られた順番で呼び出されます。ガードは非同期に解決されるかもしれません。そしてそのナビゲーションは全てのフックが解決されるまで 未解決状態 として扱われます。

全てのガード関数は 3 つの引数を受け取ります。

  • to: Route: 次にナビゲーションされる対象の ルートオブジェクト

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

  • next: Function: フックを 解決 するためにこの関数を呼ぶ必要があります。この振る舞いは next に渡される引数に依存します:

  • next(): パイプラインの次のフックに移動します。もしフックが残っていない場合は、このナビゲーションは 確立 されます。

  • next(false): 現在のナビゲーションを中止します。もしブラウザの URL が変化した場合は(ユーザーが手動で変更した場合でも、戻るボタンの場合でも)、 from ルートの URL にリセットされます。

  • next('/') または next({ path: '/' }): 異なる場所へリダイレクトします。現在のナビゲーションは中止され、あたらしいナビゲーションが始まります。任意のロケーションオブジェクトを next に渡すことができます。この next には、replace: truename: 'home' のようなオプション、そして router-linkto プロパティまたは router.pushで使用される任意のオプションを指定することができます。

  • next(error): (2.4.0+) next に渡された引数が Error インスタンスである場合、ナビゲーションは中止され、エラーは router.onError() を介して登録されたコールバックに渡されます。

与えられたナビゲーションガードを通過する任意のパスにおいて、常に 1 回だけ next 関数が呼び出されるようにしてください。それは 1 回以上出現することがありますが、論理パスが重ならないときだけで、そうしないないとフックは決して解決されない、またはエラーが発生します。 以下は、ユーザーが認証されていない場合、/login にリダレクトするための例です:

// BAD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  // ユーザーが認証されていない場合、 `next` は2回呼ばれる
  next()
})
// GOOD
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

# グローバル解決ガード

New in 2.5.0

2.5.0 以降では、router.beforeResolve によってグローバルガードを登録できます。これは router.beforeEach に似ていますが、すべてのコンポーネント内ガードと非同期ルートコンポーネントが解決された後、ナビゲーションが解決される直前に解決ガードが呼び出されるという違いがあります。

# グローバルな After フック

グローバル after フックを登録することもできます。しかしながら、ガードとは異なり、これらのフックは next 関数を受け取らず、ナビゲーションに影響しません。

router.afterEach((to, from) => {
  // ...
})

# ルート単位ガード

直接ルート設定オブジェクトの beforeEnter ガードを定義することができます。

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

これらのガードはグローバル before ガードと全く同じシグネチャを持ちます。

# コンポーネント内ガード

最後に、 以下のオプションでルートコンポーネント(ルータ設定に渡されるもの)の内側でルートナビゲーションガードを直接定義することができます。

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 で追加)
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // このコンポーネントを描画するルートが確立する前に呼ばれます。
    // `this` でのこのコンポーネントへのアクセスはできません。
    // なぜならばこのガードが呼び出される時にまだ作られていないからです!
  },
  beforeRouteUpdate(to, from, next) {
    // このコンポーネントを描画するルートが変更されたときに呼び出されますが、
    // このコンポーネントは新しいルートで再利用されます。
    // たとえば、動的な引数 `/foo/:id` を持つルートの場合、`/foo/1` と `/foo/2` の間を移動すると、
    // 同じ `Foo` コンポーネントインスタンスが再利用され、そのときにこのフックが呼び出されます。
    // `this` でコンポーネントインスタンスにアクセスできます。
  },
  beforeRouteLeave(to, from, next) {
    // このコンポーネントを描画するルートが間もなく
    // ナビゲーションから離れていく時に呼ばれます。
    // `this` でのコンポーネントインスタンスへのアクセスができます。
  }
}

この beforeRouteEnter ガードは this へのアクセスはできないです。なぜならば、ナビゲーションが確立する前にガードが呼び出されるからです。したがって、新しく入ってくるコンポーネントはまだ作られていないです。

しかしながら、 next にコールバックを渡すことでインスタンスにアクセスすることができます。このコールバックはナビゲーションが確立した時に呼ばれ、コンポーネントインスタンスはそのコールバックの引数として渡されます。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // `vm` を通じてコンポーネントインスタンスにアクセス
  })
}

コールバックを next に渡すことをサポートするのは、beforeRouteEnter ガードだけであるということに注意してください。beforeRouteUpdatebeforeRouteLeave の場合、 this は既に利用可能です。したがって、コールバックを渡す必要はないので、サポートされません:

beforeRouteUpdate (to, from, next) {
  // `this` を使用
  this.name = to.params.name
  next()
}

leave ガードは、通常、ユーザが保存されていない編集内容で誤って経路を離れるのを防ぐために使用されます。ナビゲーションは next(false) を呼び出すことで取り消すことができます。

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

# 完全なナビゲーション解決フロー

  1. ナビゲーションがトリガされる
  2. 非アクティブ化されたコンポーネントで beforeRouteLeave ガードを呼ぶ
  3. グローバル beforeEach ガードを呼ぶ
  4. 再利用されるコンポーネントで beforeRouteUpdate ガードを呼ぶ (2.2 以降)
  5. ルート設定内の beforeEnter を呼ぶ
  6. 非同期ルートコンポーネントを解決する
  7. アクティブ化されたコンポーネントで beforeRouteEnter を呼ぶ
  8. グローバル beforeResolve ガードを呼ぶ (2.5 以降)
  9. ナビゲーションが確定される
  10. グローバル afterEach フックを呼ぶ
  11. DOM 更新がトリガされる
  12. インスタンス化されたインスンタンスによって beforeRouteEnter ガードで next に渡されたコールバックを呼ぶ