File: navigation-guards.md

package info (click to toggle)
vue-router.js 3.6.5~ds1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,560 kB
  • sloc: javascript: 8,524; sh: 22; makefile: 5
file content (173 lines) | stat: -rw-r--r-- 9,715 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# ナビゲーションガード

<div class="vueschool"><a href="https://vueschool.io/courses/vue-router-for-everyone?friend=vuerouter" target="_blank" rel="sponsored noopener" title="Learn how to build powerful Single Page Applications with the Vue Router on Vue School">Watch a free video course about Vue Router on Vue School</a></div>

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

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

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

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

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

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

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

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

- **`to: Route`**: 次にナビゲーションされる対象の [ルートオブジェクト](../../api/#ルートオブジェクト)。

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

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

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

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

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

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

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

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

```js
// 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` 関数を受け取らず、ナビゲーションに影響しません。

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

## ルート単位ガード

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

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

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

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

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

- `beforeRouteEnter`
- `beforeRouteUpdate` (2.2 で追加)
- `beforeRouteLeave`

```js
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` にコールバックを渡すことでインスタンスにアクセスすることができます。このコールバックはナビゲーションが確立した時に呼ばれ、コンポーネントインスタンスはそのコールバックの引数として渡されます。

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

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

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

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

```js
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` に渡されたコールバックを呼ぶ