Skip to content

Commit 8faa523

Browse files
committed
add vue-router document
1 parent 191030c commit 8faa523

25 files changed

+2109
-4
lines changed

src/about/awesome.md

Lines changed: 637 additions & 0 deletions
Large diffs are not rendered by default.

src/router/advanced/data-fetching.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
---
2+
title: 数据获取
3+
type: router
4+
order: 13
5+
---
6+
7+
# Data Fetching
8+
9+
Sometimes you need to fetch data from the server when a route is activated. For example, before rendering a user profile, you need to fetch the user's data from the server. We can achieve this in two different ways:
10+
11+
- **Fetching After Navigation**: perform the navigation first, and fetch data in the incoming component's lifecycle hook. Display a loading state while data is being fetched.
12+
13+
- **Fetching Before Navigation**: Fetch data before navigation in the route enter guard, and perform the navigation after data has been fetched.
14+
15+
Technically, both are valid choices - it ultimately depends on the user experience you are aiming for.
16+
17+
## Fetching After Navigation
18+
19+
When using this approach, we navigate and render the incoming component immediately, and fetch data in the component's `created` hook. It gives us the opportunity to display a loading state while the data is being fetched over the network, and we can also handle loading differently for each view.
20+
21+
Let's assume we have a `Post` component that needs to fetch the data for a post based on `$route.params.id`:
22+
23+
``` html
24+
<template>
25+
<div class="post">
26+
<div class="loading" v-if="loading">
27+
Loading...
28+
</div>
29+
30+
<div v-if="error" class="error">
31+
{{ error }}
32+
</div>
33+
34+
<div v-if="post" class="content">
35+
<h2>{{ post.title }}</h2>
36+
<p>{{ post.body }}</p>
37+
</div>
38+
</div>
39+
</template>
40+
```
41+
42+
``` js
43+
export default {
44+
data () {
45+
return {
46+
loading: false,
47+
post: null,
48+
error: null
49+
}
50+
},
51+
created () {
52+
// fetch the data when the view is created and the data is
53+
// already being observed
54+
this.fetchData()
55+
},
56+
watch: {
57+
// call again the method if the route changes
58+
'$route': 'fetchData'
59+
},
60+
methods: {
61+
fetchData () {
62+
this.error = this.post = null
63+
this.loading = true
64+
// replace getPost with your data fetching util / API wrapper
65+
getPost(this.$route.params.id, (err, post) => {
66+
this.loading = false
67+
if (err) {
68+
this.error = err.toString()
69+
} else {
70+
this.post = post
71+
}
72+
})
73+
}
74+
}
75+
}
76+
```
77+
78+
## Fetching Before Navigation
79+
80+
With this approach we fetch the data before actually navigating to the new
81+
route. We can perform the data fetching in the `beforeRouteEnter` guard in the incoming component, and only call `next` when the fetch is complete:
82+
83+
``` js
84+
export default {
85+
data () {
86+
return {
87+
post: null,
88+
error: null
89+
}
90+
},
91+
beforeRouteEnter (route, redirect, next) {
92+
getPost(route.params.id, (err, post) => {
93+
if (err) {
94+
// display some global error message
95+
} else {
96+
next(vm => {
97+
vm.post = post
98+
})
99+
}
100+
})
101+
},
102+
// when route changes when this component is already rendered,
103+
// the logic will be slightly different.
104+
watch: {
105+
$route () {
106+
this.post = null
107+
getPost(this.$route.params.id, (err, post) => {
108+
if (err) {
109+
this.error = err.toString()
110+
} else {
111+
this.post = post
112+
}
113+
})
114+
}
115+
}
116+
}
117+
```
118+
119+
The user will stay on the current view while the resource is being fetched for the incoming view. It is therefore recommended to display a progress bar or some kind of indicator while the data is being fetched. If the data fetch fails, it's also necessary to display some kind of global warning message.

src/router/advanced/lazy-loading.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
title: 异步加载
3+
type: router
4+
order: 15
5+
---
6+
7+
# Lazy Loading Routes
8+
9+
When building apps with a bundler, the JavaScript bundle can become quite large and thus affecting page load time. It would be more efficient if we can split each route's components into a separate chunk, and only load them when the route is visited.
10+
11+
Combining Vue's [async component feature](http://vuejs.org/guide/components.html#Async-Components) and Webpack's [code splitting feature](https://webpack.github.io/docs/code-splitting.html), it's trivially easy to
12+
lazy-load route components.
13+
14+
All we need to do is defining our route components as async components:
15+
16+
``` js
17+
const Foo = resolve => {
18+
// require.ensure is Webpack's special syntax for a code-split point.
19+
require.ensure(['./Foo.vue'], () => {
20+
resolve(require('./Foo.vue'))
21+
})
22+
}
23+
```
24+
25+
There's also an alternative code-split syntax using AMD style require, so this can be simplified to:
26+
27+
``` js
28+
const Foo = resolve => require(['./Foo.vue'], resolve)
29+
```
30+
31+
Nothing needs to change in the route config, just use `Foo` as usual:
32+
33+
``` js
34+
const router = new VueRouter({
35+
routes: [
36+
{ path: '/foo', component: Foo }
37+
]
38+
})
39+
```
40+
41+
### Grouping Components in the Same Chunk
42+
43+
Sometimes we may want to group all the components nested under the same route into the same async chunk. To achieve that we need to use [named chunks](https://webpack.github.io/docs/code-splitting.html#named-chunks) by providing a chunk name to `require.ensure` as the 3rd argument:
44+
45+
``` js
46+
const Foo = r => require.ensure([], () => r(require('./Foo.vue')), 'group-foo')
47+
const Bar = r => require.ensure([], () => r(require('./Bar.vue')), 'group-foo')
48+
const Baz = r => require.ensure([], () => r(require('./Baz.vue')), 'group-foo')
49+
```
50+
51+
Webpack will group any async module with the same chunk name into the same async chunk - this also means we don't need to explicitly list dependencies for `require.ensure` anymore (thus passing an empty array).

src/router/advanced/meta.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
title: Route Meta Fields
3+
type: router
4+
order: 11
5+
---
6+
7+
# Route Meta Fields
8+
9+
You can include a `meta` field when defining a route:
10+
11+
``` js
12+
const router = new VueRouter({
13+
routes: [
14+
{
15+
path: '/foo',
16+
component: Foo,
17+
children: [
18+
{
19+
path: 'bar',
20+
component: Bar,
21+
// a meta field
22+
meta: { requiresAuth: true }
23+
}
24+
]
25+
}
26+
]
27+
})
28+
```
29+
30+
So how do we access this `meta` field?
31+
32+
First, each route object in the `routes` configuration is called a **route record**. Route records may be nested. Therefore when a route is matched, it can potentially match more than one route record.
33+
34+
For example, with the above route config, the URL `/foo/bar` will match both the parent route record and the child route record.
35+
36+
All route records matched by a route are exposed on the `$route` object (and also route objects in navigation guards) as the `$route.matched` Array. Therefore, we will need to iterate over `$route.matched` to check for meta fields in route records.
37+
38+
An example use case is checking for a meta field in the global navigation guard:
39+
40+
``` js
41+
router.beforeEach((route, redirect, next) => {
42+
if (route.matched.some(record => record.meta.requiresAuth)) {
43+
// this route requires auth, check if logged in
44+
// if not, redirect to login page.
45+
if (!auth.loggedIn()) {
46+
redirect({
47+
path: '/login',
48+
query: { redirect: route.fullPath }
49+
})
50+
} else {
51+
next()
52+
}
53+
} else {
54+
next()
55+
}
56+
})
57+
```
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
---
2+
title: Navigation Guards
3+
type: router
4+
order: 10
5+
---
6+
7+
# Navigation Guards
8+
9+
As the name suggests, the navigation guards provided by `vue-router` are primarily used to guard navigations either by redirecting it or canceling it. There are a number of ways to hook into the route navigation process: globally, per-route, or in-component.
10+
11+
### Global Guards
12+
13+
You can register global before guards using `router.beforeEach`:
14+
15+
``` js
16+
const router = new VueRouter({ ... })
17+
18+
router.beforeEach((route, redirect, next) => {
19+
// ...
20+
})
21+
```
22+
23+
Global before guards are called in creation order, whenever a navigation is triggered. Guards may be resolved asynchronously, and the navigation is considered **pending** before all hooks have been resolved.
24+
25+
Every guard function receives three arguments:
26+
27+
- `route: Route`: the target [Route Object](../api/route-object.md) being navigated to.
28+
29+
- `redirect: Function`: calling this function will abort the current navigation and start a new navigation towards the redirect target.
30+
31+
- `next: Function`: resolve this guard and proceed to the next guard in the pipeline. If there are no hooks left, then the navigation is **confirmed**.
32+
33+
**If neither `redirect` nor `next` is called, the navigation will be cancelled.**
34+
35+
You can also register global after hooks, however unlike guards, these hooks are much simpler and cannot affect the navigation:
36+
37+
``` js
38+
router.afterEach(route => {
39+
// ...
40+
})
41+
```
42+
43+
### Per-Route Guard
44+
45+
You can define `beforeEnter` guards directly on a route's configuration object:
46+
47+
``` js
48+
const router = new VueRouter({
49+
routes: [
50+
{
51+
path: '/foo',
52+
component: Foo,
53+
beforeEnter: (route, redirect, next) => {
54+
// ...
55+
}
56+
}
57+
]
58+
})
59+
```
60+
61+
These guards have the exact same signature as global before guards.
62+
63+
### In-Component Guards
64+
65+
Finally, you can directly define route navigation guards inside route components with `beforeRouteEnter` and `beforeRouteLeave`:
66+
67+
``` js
68+
const Foo = {
69+
template: `...`,
70+
beforeRouteEnter (route, redirect, next) => {
71+
// called before the route that renders this component is confirmed.
72+
// does NOT have access to `this` component instance,
73+
// because it has not been created yet when this guard is called!
74+
},
75+
beforeRouteLeave (route, redirect, next) => {
76+
// called when the route that renders this component is about to
77+
// be navigated away from.
78+
// has access to `this` component instance.
79+
}
80+
}
81+
```
82+
83+
The `beforeRouteEnter` guard does **NOT** have access to `this`, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet.
84+
85+
However, you can access the instance by passing a callback to `next`. The callback will be called when the navigation is confirmed, and the component instance will be passed to the callback as the argument:
86+
87+
``` js
88+
beforeRouteEnter (route, redirect, next) => {
89+
next(vm => {
90+
// access to component instance via `vm`
91+
})
92+
}
93+
```
94+
95+
You can directly access `this` inside `beforeRouteLeave`. The leave guard is usually used to prevent the user from accidentally leaving the route with unsaved edits. The navigation can be canceled by simply not calling `next` or `redirect`.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
title: 滚动
3+
type: router
4+
order: 14
5+
---
6+
7+
# Scroll Behavior
8+
9+
When using client-side routing, we may want to scroll to top when navigating to a new route, or preserve the scrolling position of history entries just like real page reload does. `vue-router` allows you to achieve these and even better, allows you to completely customize the scroll behavior on route navigation.
10+
11+
**Note: this feature only works in HTML5 history mode.**
12+
13+
When creating the router instance, you can provide the `scrollBehavior` function:
14+
15+
``` js
16+
const router = new VueRouter({
17+
routes: [...],
18+
scrollBehavior (to, from, savedPosition) {
19+
// return desired position
20+
}
21+
})
22+
```
23+
24+
The `scrollBehavior` function receives the `to` and `from` route objects. The third argument, `savedPosition`, is only available if this is a `popstate` navigation (triggered by the browser's back/forward buttons).
25+
26+
The function can return a scroll position object. The object could be in the form of:
27+
28+
- `{ x: number, y: number }`
29+
- `{ selector: string }`
30+
31+
If a falsy value or an empty object is returned, no scrolling will happen.
32+
33+
For example:
34+
35+
``` js
36+
scrollBehavior (to, from, savedPosition) {
37+
return { x: 0, y: 0 }
38+
}
39+
```
40+
41+
This will simply make the page scroll to top for all route navigations.
42+
43+
Returning the `savedPosition` will result in a native-like behavior when navigating with back/forward buttons:
44+
45+
``` js
46+
scrollBehavior (to, from, savedPosition) {
47+
if (savedPosition) {
48+
return savedPosition
49+
} else {
50+
return { x: 0, y: 0 }
51+
}
52+
}
53+
```
54+
55+
If you want to simulate the "scroll to anchor" behavior:
56+
57+
``` js
58+
scrollBehavior (to, from, savedPosition) {
59+
if (to.hash) {
60+
return {
61+
selector: to.hash
62+
}
63+
}
64+
}
65+
```
66+
67+
We can also use [route meta fields](meta.md) to implement fine-grained scroll behavior control. Check out a full example [here](https://github.com/vuejs/vue-router/blob/next/examples/scroll-behavior/app.js).

0 commit comments

Comments
 (0)