# 安裝
### [#](https://router.vuejs.org/zh/installation.html#%E7%9B%B4%E6%8E%A5%E4%B8%8B%E8%BD%BD-cdn)直接下載 / CDN
[https://unpkg.com/vue-router/dist/vue-router.js](https://unpkg.com/vue-router/dist/vue-router.js)
[Unpkg.com](https://unpkg.com/)?提供了基于 NPM 的 CDN 鏈接。上面的鏈接會一直指向在 NPM 發布的最新版本。你也可以像?`https://unpkg.com/vue-router@2.0.0/dist/vue-router.js`?這樣指定 版本號 或者 Tag。
在 Vue 后面加載?`vue-router`,它會自動安裝的:
~~~
<script src="/path/to/vue.js"></script>
<script src="/path/to/vue-router.js"></script>
~~~
### [#](https://router.vuejs.org/zh/installation.html#npm)NPM
~~~
npm install vue-router
~~~
如果在一個模塊化工程中使用它,必須要通過?`Vue.use()`?明確地安裝路由功能:
~~~
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
~~~
如果使用全局的 script 標簽,則無須如此 (手動安裝)。
### [#](https://router.vuejs.org/zh/installation.html#%E6%9E%84%E5%BB%BA%E5%BC%80%E5%8F%91%E7%89%88)構建開發版
如果你想使用最新的開發版,就得從 GitHub 上直接 clone,然后自己 build 一個?`vue-router`。
~~~
git clone https://github.com/vuejs/vue-router.git node_modules/vue-router
cd node_modules/vue-router
npm install
npm run build
~~~
# 介紹
[This project is sponsored by?](https://www.bitsrc.io/?utm_source=vue&utm_medium=vue&utm_campaign=vue&utm_term=vue&utm_content=vue)
版本說明
對于 TypeScript 用戶來說,`vue-router@3.0+`?依賴?`vue@2.5+`,反之亦然。
Vue Router 是?[Vue.js](http://cn.vuejs.org/)?官方的路由管理器。它和 Vue.js 的核心深度集成,讓構建單頁面應用變得易如反掌。包含的功能有:
* 嵌套的路由/視圖表
* 模塊化的、基于組件的路由配置
* 路由參數、查詢、通配符
* 基于 Vue.js 過渡系統的視圖過渡效果
* 細粒度的導航控制
* 帶有自動激活的 CSS class 的鏈接
* HTML5 歷史模式或 hash 模式,在 IE9 中自動降級
* 自定義的滾動條行為
現在開始[起步](https://router.vuejs.org/zh/guide/)或嘗試一下我們的[示例](https://github.com/vuejs/vue-router/tree/dev/examples)吧 (查看倉庫的?[`README.md`](https://github.com/vuejs/vue-router/)?來運行它們)。
# 起步
[This project is sponsored by?](https://www.bitsrc.io/?utm_source=vue&utm_medium=vue&utm_campaign=vue&utm_term=vue&utm_content=vue)
注意
教程中的案例代碼將使用?[ES2015](https://github.com/lukehoban/es6features)?來編寫。
同時,所有的例子都將使用完整版的 Vue 以解析模板。更多細節請[移步這里](https://cn.vuejs.org/v2/guide/installation.html#%E8%BF%90%E8%A1%8C%E6%97%B6-%E7%BC%96%E8%AF%91%E5%99%A8-vs-%E5%8F%AA%E5%8C%85%E5%90%AB%E8%BF%90%E8%A1%8C%E6%97%B6)。
用 Vue.js + Vue Router 創建單頁應用,是非常簡單的。使用 Vue.js ,我們已經可以通過組合組件來組成應用程序,當你要把 Vue Router 添加進來,我們需要做的是,將組件 (components) 映射到路由 (routes),然后告訴 Vue Router 在哪里渲染它們。下面是個基本例子:
## [#](https://router.vuejs.org/zh/guide/#html)HTML
~~~
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 組件來導航. -->
<!-- 通過傳入 `to` 屬性指定鏈接. -->
<!-- <router-link> 默認會被渲染成一個 `<a>` 標簽 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這里 -->
<router-view></router-view>
</div>
~~~
## [#](https://router.vuejs.org/zh/guide/#javascript)JavaScript
~~~
// 0\. 如果使用模塊化機制編程,導入Vue和VueRouter,要調用 Vue.use(VueRouter)
// 1\. 定義 (路由) 組件。
// 可以從其他文件 import 進來
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2\. 定義路由
// 每個路由應該映射一個組件。 其中"component" 可以是
// 通過 Vue.extend() 創建的組件構造器,
// 或者,只是一個組件配置對象。
// 我們晚點再討論嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3\. 創建 router 實例,然后傳 `routes` 配置
// 你還可以傳別的配置參數, 不過先這么簡單著吧。
const router = new VueRouter({
routes // (縮寫) 相當于 routes: routes
})
// 4\. 創建和掛載根實例。
// 記得要通過 router 配置參數注入路由,
// 從而讓整個應用都有路由功能
const app = new Vue({
router
}).$mount('#app')
// 現在,應用已經啟動了!
~~~
通過注入路由器,我們可以在任何組件內通過?`this.$router`?訪問路由器,也可以通過?`this.$route`?訪問當前路由:
~~~
// Home.vue
export default {
computed: {
username () {
// 我們很快就會看到 `params` 是什么
return this.$route.params.username
}
},
methods: {
goBack () {
window.history.length > 1
? this.$router.go(-1)
: this.$router.push('/')
}
}
}
~~~
該文檔通篇都常使用?`router`?實例。留意一下?`this.$router`?和?`router`?使用起來完全一樣。我們使用?`this.$router`?的原因是我們并不想在每個獨立需要封裝路由的組件中都導入路由。
你可以看看這個[在線的](https://jsfiddle.net/yyx990803/xgrjzsup/)例子。
要注意,當?`<router-link>`?對應的路由匹配成功,將自動設置 class 屬性值?`.router-link-active`。查看?[API 文檔](https://router.vuejs.org/zh/api/#router-link)?學習更多相關內容。
# 動態路由匹配
我們經常需要把某種模式匹配到的所有路由,全都映射到同個組件。例如,我們有一個?`User`?組件,對于所有 ID 各不相同的用戶,都要使用這個組件來渲染。那么,我們可以在?`vue-router`?的路由路徑中使用“動態路徑參數”(dynamic segment) 來達到這個效果:
~~~
const User = {
template: '<div>User</div>'
}
const router = new VueRouter({
routes: [
// 動態路徑參數 以冒號開頭
{ path: '/user/:id', component: User }
]
})
~~~
現在呢,像?`/user/foo`?和?`/user/bar`?都將映射到相同的路由。
一個“路徑參數”使用冒號?`:`?標記。當匹配到一個路由時,參數值會被設置到?`this.$route.params`,可以在每個組件內使用。于是,我們可以更新?`User`?的模板,輸出當前用戶的 ID:
~~~
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
~~~
你可以看看這個[在線例子](https://jsfiddle.net/yyx990803/4xfa2f19/)。
你可以在一個路由中設置多段“路徑參數”,對應的值都會設置到?`$route.params`?中。例如:
| 模式 | 匹配路徑 | $route.params |
| --- | --- | --- |
| /user/:username | /user/evan | `{ username: 'evan' }` |
| /user/:username/post/:post_id | /user/evan/post/123 | `{ username: 'evan', post_id: 123 }` |
除了?`$route.params`?外,`$route`?對象還提供了其它有用的信息,例如,`$route.query`?(如果 URL 中有查詢參數)、`$route.hash`?等等。你可以查看?[API 文檔](https://router.vuejs.org/zh/api/#%E8%B7%AF%E7%94%B1%E5%AF%B9%E8%B1%A1)?的詳細說明。
## [#](https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#%E5%93%8D%E5%BA%94%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0%E7%9A%84%E5%8F%98%E5%8C%96)響應路由參數的變化
提醒一下,當使用路由參數時,例如從?`/user/foo`?導航到?`/user/bar`,原來的組件實例會被復用。因為兩個路由都渲染同個組件,比起銷毀再創建,復用則顯得更加高效。不過,這也意味著組件的生命周期鉤子不會再被調用。
復用組件時,想對路由參數的變化作出響應的話,你可以簡單地 watch (監測變化)?`$route`?對象:
~~~
const User = {
template: '...',
watch: {
'$route' (to, from) {
// 對路由變化作出響應...
}
}
}
~~~
或者使用 2.2 中引入的?`beforeRouteUpdate`?[導航守衛](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html):
~~~
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
~~~
## [#](https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#%E9%AB%98%E7%BA%A7%E5%8C%B9%E9%85%8D%E6%A8%A1%E5%BC%8F)高級匹配模式
`vue-router`?使用?[path-to-regexp](https://github.com/pillarjs/path-to-regexp)?作為路徑匹配引擎,所以支持很多高級的匹配模式,例如:可選的動態路徑參數、匹配零個或多個、一個或多個,甚至是自定義正則匹配。查看它的?[文檔](https://github.com/pillarjs/path-to-regexp#parameters)?學習高階的路徑匹配,還有?[這個例子?](https://github.com/vuejs/vue-router/blob/next/examples/route-matching/app.js)?展示?`vue-router`?怎么使用這類匹配。
## [#](https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#%E5%8C%B9%E9%85%8D%E4%BC%98%E5%85%88%E7%BA%A7)匹配優先級
有時候,同一個路徑可以匹配多個路由,此時,匹配的優先級就按照路由的定義順序:誰先定義的,誰的優先級就最高。
# 嵌套路由
實際生活中的應用界面,通常由多層嵌套的組件組合而成。同樣地,URL 中各段動態路徑也按某種結構對應嵌套的各層組件,例如:
~~~
/user/foo/profile /user/foo/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
~~~
借助?`vue-router`,使用嵌套路由配置,就可以很簡單地表達這種關系。
接著上節創建的 app:
~~~
<div id="app">
<router-view></router-view>
</div>
~~~
~~~
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
~~~
這里的?`<router-view>`?是最頂層的出口,渲染最高級路由匹配到的組件。同樣地,一個被渲染組件同樣可以包含自己的嵌套?`<router-view>`。例如,在?`User`?組件的模板添加一個?`<router-view>`:
~~~
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
~~~
要在嵌套的出口中渲染組件,需要在?`VueRouter`?的參數中使用?`children`?配置:
~~~
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 當 /user/:id/profile 匹配成功,
// UserProfile 會被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 當 /user/:id/posts 匹配成功
// UserPosts 會被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
~~~
要注意,以?`/`?開頭的嵌套路徑會被當作根路徑。 這讓你充分的使用嵌套組件而無須設置嵌套的路徑。
你會發現,`children`?配置就是像?`routes`?配置一樣的路由配置數組,所以呢,你可以嵌套多層路由。
此時,基于上面的配置,當你訪問?`/user/foo`?時,`User`?的出口是不會渲染任何東西,這是因為沒有匹配到合適的子路由。如果你想要渲染點什么,可以提供一個 空的 子路由:
~~~
const router = new VueRouter({
routes: [
{
path: '/user/:id', component: User,
children: [
// 當 /user/:id 匹配成功,
// UserHome 會被渲染在 User 的 <router-view> 中
{ path: '', component: UserHome },
// ...其他子路由
]
}
]
})
~~~
提供以上案例的可運行代碼請[移步這里](https://jsfiddle.net/yyx990803/L7hscd8h/)。
# 編程式的導航
除了使用?`<router-link>`?創建 a 標簽來定義導航鏈接,我們還可以借助 router 的實例方法,通過編寫代碼來實現。
## [#](https://router.vuejs.org/zh/guide/essentials/navigation.html#router-push-location-oncomplete-onabort)`router.push(location, onComplete?, onAbort?)`
注意:在 Vue 實例內部,你可以通過?`$router`?訪問路由實例。因此你可以調用?`this.$router.push`。
想要導航到不同的 URL,則使用?`router.push`?方法。這個方法會向 history 棧添加一個新的記錄,所以,當用戶點擊瀏覽器后退按鈕時,則回到之前的 URL。
當你點擊?`<router-link>`?時,這個方法會在內部調用,所以說,點擊?`<router-link :to="...">`?等同于調用?`router.push(...)`。
| 聲明式 | 編程式 |
| --- | --- |
| `<router-link :to="...">` | `router.push(...)` |
該方法的參數可以是一個字符串路徑,或者一個描述地址的對象。例如:
~~~
// 字符串
router.push('home')
// 對象
router.push({ path: 'home' })
// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})
// 帶查詢參數,變成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
~~~
注意:如果提供了?`path`,`params`?會被忽略,上述例子中的?`query`?并不屬于這種情況。取而代之的是下面例子的做法,你需要提供路由的?`name`?或手寫完整的帶有參數的?`path`:
~~~
const userId = 123
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 這里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
~~~
同樣的規則也適用于?`router-link`?組件的?`to`?屬性。
在 2.2.0+,可選的在?`router.push`?或?`router.replace`?中提供?`onComplete`?和?`onAbort`?回調作為第二個和第三個參數。這些回調將會在導航成功完成 (在所有的異步鉤子被解析之后) 或終止 (導航到相同的路由、或在當前導航完成之前導航到另一個不同的路由) 的時候進行相應的調用。
**注意:**如果目的地和當前路由相同,只有參數發生了改變 (比如從一個用戶資料到另一個?`/users/1`?->?`/users/2`),你需要使用?[`beforeRouteUpdate`](https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#%E5%93%8D%E5%BA%94%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0%E7%9A%84%E5%8F%98%E5%8C%96)?來響應這個變化 (比如抓取用戶信息)。
## [#](https://router.vuejs.org/zh/guide/essentials/navigation.html#router-replace-location-oncomplete-onabort)`router.replace(location, onComplete?, onAbort?)`
跟?`router.push`?很像,唯一的不同就是,它不會向 history 添加新記錄,而是跟它的方法名一樣 —— 替換掉當前的 history 記錄。
| 聲明式 | 編程式 |
| --- | --- |
| `<router-link :to="..." replace>` | `router.replace(...)` |
## [#](https://router.vuejs.org/zh/guide/essentials/navigation.html#router-go-n)`router.go(n)`
這個方法的參數是一個整數,意思是在 history 記錄中向前或者后退多少步,類似?`window.history.go(n)`。
例子
~~~
// 在瀏覽器記錄中前進一步,等同于 history.forward()
router.go(1)
// 后退一步記錄,等同于 history.back()
router.go(-1)
// 前進 3 步記錄
router.go(3)
// 如果 history 記錄不夠用,那就默默地失敗唄
router.go(-100)
router.go(100)
~~~
## [#](https://router.vuejs.org/zh/guide/essentials/navigation.html#%E6%93%8D%E4%BD%9C-history)操作 History
你也許注意到?`router.push`、?`router.replace`?和?`router.go`?跟?[`window.history.pushState`、?`window.history.replaceState`?和?`window.history.go`](https://developer.mozilla.org/en-US/docs/Web/API/History)好像, 實際上它們確實是效仿?`window.history`API 的。
因此,如果你已經熟悉?[Browser History APIs](https://developer.mozilla.org/en-US/docs/Web/API/History_API),那么在 Vue Router 中操作 history 就是超級簡單的。
還有值得提及的,Vue Router 的導航方法 (`push`、?`replace`、?`go`) 在各類路由模式 (`history`、?`hash`?和?`abstract`) 下表現一致。
# 命名路由
有時候,通過一個名稱來標識一個路由顯得更方便一些,特別是在鏈接一個路由,或者是執行一些跳轉的時候。你可以在創建 Router 實例的時候,在?`routes`?配置中給某個路由設置名稱。
~~~
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
~~~
要鏈接到一個命名路由,可以給?`router-link`?的?`to`?屬性傳一個對象:
~~~
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
~~~
這跟代碼調用?`router.push()`?是一回事:
~~~
router.push({ name: 'user', params: { userId: 123 }})
~~~
這兩種方式都會把路由導航到?`/user/123`?路徑。
完整的例子請[移步這里](https://github.com/vuejs/vue-router/blob/next/examples/named-routes/app.js)。
# 命名視圖
有時候想同時 (同級) 展示多個視圖,而不是嵌套展示,例如創建一個布局,有?`sidebar`?(側導航) 和?`main`?(主內容) 兩個視圖,這個時候命名視圖就派上用場了。你可以在界面中擁有多個單獨命名的視圖,而不是只有一個單獨的出口。如果?`router-view`?沒有設置名字,那么默認為?`default`。
~~~
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
~~~
一個視圖使用一個組件渲染,因此對于同個路由,多個視圖就需要多個組件。確保正確使用?`components`配置 (帶上 s):
~~~
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
~~~
以上案例相關的可運行代碼請[移步這里](https://jsfiddle.net/posva/6du90epg/)。
## [#](https://router.vuejs.org/zh/guide/essentials/named-views.html#%E5%B5%8C%E5%A5%97%E5%91%BD%E5%90%8D%E8%A7%86%E5%9B%BE)嵌套命名視圖
我們也有可能使用命名視圖創建嵌套視圖的復雜布局。這時你也需要命名用到的嵌套?`router-view`?組件。我們以一個設置面板為例:
~~~
/settings/emails /settings/profile
+-----------------------------------+ +------------------------------+
| UserSettings | | UserSettings |
| +-----+-------------------------+ | | +-----+--------------------+ |
| | Nav | UserEmailsSubscriptions | | +------------> | | Nav | UserProfile | |
| | +-------------------------+ | | | +--------------------+ |
| | | | | | | | UserProfilePreview | |
| +-----+-------------------------+ | | +-----+--------------------+ |
+-----------------------------------+ +------------------------------+
~~~
* `Nav`?只是一個常規組件。
* `UserSettings`?是一個視圖組件。
* `UserEmailsSubscriptions`、`UserProfile`、`UserProfilePreview`?是嵌套的視圖組件。
注意:*我們先忘記 HTML/CSS 具體的布局的樣子,只專注在用到的組件上*
`UserSettings`?組件的?`<template>`?部分應該是類似下面的這段代碼:
~~~
<!-- UserSettings.vue -->
<div>
<h1>User Settings</h1>
<NavBar/>
<router-view/>
<router-view name="helper"/>
</div>
~~~
*嵌套的視圖組件在此已經被忽略了,但是你可以在[這里](https://jsfiddle.net/posva/22wgksa3/)找到完整的源代碼*
然后你可以用這個路由配置完成該布局:
~~~
{
path: '/settings',
// 你也可以在頂級路由就配置命名視圖
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
~~~
一個可以工作的示例的 demo 在[這里](https://jsfiddle.net/posva/22wgksa3/)。
# 重定向和別名
## [#](https://router.vuejs.org/zh/guide/essentials/redirect-and-alias.html#%E9%87%8D%E5%AE%9A%E5%90%91)重定向
重定向也是通過?`routes`?配置來完成,下面例子是從?`/a`?重定向到?`/b`:
~~~
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
~~~
重定向的目標也可以是一個命名的路由:
~~~
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
~~~
甚至是一個方法,動態返回重定向目標:
~~~
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目標路由 作為參數
// return 重定向的 字符串路徑/路徑對象
}}
]
})
~~~
注意[導航守衛](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html)并沒有應用在跳轉路由上,而僅僅應用在其目標上。在下面這個例子中,為?`/a`?路由添加一個?`beforeEach`?或?`beforeLeave`?守衛并不會有任何效果。
其它高級用法,請參考[例子](https://github.com/vuejs/vue-router/blob/next/examples/redirect/app.js)。
## [#](https://router.vuejs.org/zh/guide/essentials/redirect-and-alias.html#%E5%88%AB%E5%90%8D)別名
“重定向”的意思是,當用戶訪問?`/a`時,URL 將會被替換成?`/b`,然后匹配路由為?`/b`,那么“別名”又是什么呢?
`/a`?的別名是?`/b`,意味著,當用戶訪問?`/b`?時,URL 會保持為?`/b`,但是路由匹配則為?`/a`,就像用戶訪問?`/a`?一樣。
上面對應的路由配置為:
~~~
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
~~~
“別名”的功能讓你可以自由地將 UI 結構映射到任意的 URL,而不是受限于配置的嵌套路由結構。
更多高級用法,請查看[例子](https://github.com/vuejs/vue-router/blob/next/examples/route-alias/app.js)。
# 路由組件傳參
在組件中使用?`$route`?會使之與其對應路由形成高度耦合,從而使組件只能在某些特定的 URL 上使用,限制了其靈活性。
使用?`props`?將組件和路由解耦:
取代與?`$route`?的耦合
~~~
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
~~~
通過?`props`?解耦
~~~
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true },
// 對于包含命名視圖的路由,你必須分別為每個命名視圖添加 `props` 選項:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
~~~
這樣你便可以在任何地方使用該組件,使得該組件更易于重用和測試。
## [#](https://router.vuejs.org/zh/guide/essentials/passing-props.html#%E5%B8%83%E5%B0%94%E6%A8%A1%E5%BC%8F)布爾模式
如果?`props`?被設置為?`true`,`route.params`?將會被設置為組件屬性。
## [#](https://router.vuejs.org/zh/guide/essentials/passing-props.html#%E5%AF%B9%E8%B1%A1%E6%A8%A1%E5%BC%8F)對象模式
如果?`props`?是一個對象,它會被按原樣設置為組件屬性。當?`props`?是靜態的時候有用。
~~~
const router = new VueRouter({
routes: [
{ path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
]
})
~~~
## [#](https://router.vuejs.org/zh/guide/essentials/passing-props.html#%E5%87%BD%E6%95%B0%E6%A8%A1%E5%BC%8F)函數模式
你可以創建一個函數返回?`props`。這樣你便可以將參數轉換成另一種類型,將靜態值與基于路由的值結合等等。
~~~
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
~~~
URL?`/search?q=vue`?會將?`{query: 'vue'}`?作為屬性傳遞給?`SearchUser`?組件。
請盡可能保持?`props`?函數為無狀態的,因為它只會在路由發生變化時起作用。如果你需要狀態來定義?`props`,請使用包裝組件,這樣 Vue 才可以對狀態變化做出反應。
更多高級用法,請查看[例子](https://github.com/vuejs/vue-router/blob/dev/examples/route-props/app.js)。
# HTML5 History 模式
`vue-router`?默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,于是當 URL 改變時,頁面不會重新加載。
如果不想要很丑的 hash,我們可以用路由的?history 模式,這種模式充分利用?`history.pushState`?API 來完成 URL 跳轉而無須重新加載頁面。
~~~
const router = new VueRouter({
mode: 'history',
routes: [...]
})
~~~
當你使用 history 模式時,URL 就像正常的 url,例如?`http://yoursite.com/user/id`,也好看!
不過這種模式要玩好,還需要后臺配置支持。因為我們的應用是個單頁客戶端應用,如果后臺沒有正確的配置,當用戶在瀏覽器直接訪問?`http://oursite.com/user/id`?就會返回 404,這就不好看了。
所以呢,你要在服務端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態資源,則應該返回同一個?`index.html`?頁面,這個頁面就是你 app 依賴的頁面。
## [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%90%8E%E7%AB%AF%E9%85%8D%E7%BD%AE%E4%BE%8B%E5%AD%90)后端配置例子
#### [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#apache)Apache
~~~
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
~~~
除了?`mod_rewrite`,你也可以使用?[`FallbackResource`](https://httpd.apache.org/docs/2.2/mod/mod_dir.html#fallbackresource)。
#### [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#nginx)nginx
~~~
location / {
try_files $uri $uri/ /index.html;
}
~~~
#### [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%8E%9F%E7%94%9F-node-js)原生 Node.js
~~~
const http = require('http')
const fs = require('fs')
const httpPort = 80
http.createServer((req, res) => {
fs.readFile('index.htm', 'utf-8', (err, content) => {
if (err) {
console.log('We cannot open "index.htm" file.')
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8'
})
res.end(content)
})
}).listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
})
~~~
#### [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%9F%BA%E4%BA%8E-node-js-%E7%9A%84-express)基于 Node.js 的 Express
對于 Node.js/Express,請考慮使用?[connect-history-api-fallback 中間件](https://github.com/bripkens/connect-history-api-fallback)。
#### [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#internet-information-services-iis)Internet Information Services (IIS)
1. 安裝?[IIS UrlRewrite](https://www.iis.net/downloads/microsoft/url-rewrite)
2. 在你的網站根目錄中創建一個?`web.config`?文件,內容如下:
~~~
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Handle History Mode and custom 404/500" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
~~~
#### [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#caddy)Caddy
~~~
rewrite {
regexp .*
to {path} /
}
~~~
#### [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#firebase-%E4%B8%BB%E6%9C%BA)Firebase 主機
在你的?`firebase.json`?中加入:
~~~
{
"hosting": {
"public": "dist",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
~~~
## [#](https://router.vuejs.org/zh/guide/essentials/history-mode.html#%E8%AD%A6%E5%91%8A)警告
給個警告,因為這么做以后,你的服務器就不再返回 404 錯誤頁面,因為對于所有路徑都會返回?`index.html`?文件。為了避免這種情況,你應該在 Vue 應用里面覆蓋所有的路由情況,然后在給出一個 404 頁面。
~~~
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '*', component: NotFoundComponent }
]
})
~~~
或者,如果你使用 Node.js 服務器,你可以用服務端路由匹配到來的 URL,并在沒有匹配到路由的時候返回 404,以實現回退。更多詳情請查閱?[Vue 服務端渲染文檔](https://ssr.vuejs.org/zh/)。
# 導航守衛
譯者注
“導航”表示路由正在發生改變。
正如其名,`vue-router`?提供的導航守衛主要用來通過跳轉或取消的方式守衛導航。有多種機會植入路由導航過程中:全局的, 單個路由獨享的, 或者組件級的。
記住參數或查詢的改變并不會觸發進入/離開的導航守衛。你可以通過[觀察?`$route`?對象](https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#%E5%93%8D%E5%BA%94%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0%E7%9A%84%E5%8F%98%E5%8C%96)來應對這些變化,或使用?`beforeRouteUpdate`?的組件內守衛。
## [#](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%AE%88%E5%8D%AB)全局守衛
你可以使用?`router.beforeEach`?注冊一個全局前置守衛:
~~~
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
~~~
當一個導航觸發時,全局前置守衛按照創建順序調用。守衛是異步解析執行,此時導航在所有守衛 resolve 完之前一直處于?等待中。
每個守衛方法接收三個參數:
* `to: Route`: 即將要進入的目標?[路由對象](https://router.vuejs.org/zh/api/#%E8%B7%AF%E7%94%B1%E5%AF%B9%E8%B1%A1)
* `from: Route`: 當前導航正要離開的路由
* `next: Function`: 一定要調用該方法來?resolve?這個鉤子。執行效果依賴?`next`?方法的調用參數。
* `next()`: 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是?confirmed?(確認的)。
* `next(false)`: 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到?`from`?路由對應的地址。
* `next('/')`?或者?`next({ path: '/' })`: 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。你可以向?`next`?傳遞任意位置對象,且允許設置諸如?`replace: true`、`name: 'home'`?之類的選項以及任何用在?[`router-link`?的?`to`?prop](https://router.vuejs.org/zh/api/#to)?或?[`router.push`](https://router.vuejs.org/zh/api/#router-push)?中的選項。
* `next(error)`: (2.4.0+) 如果傳入?`next`?的參數是一個?`Error`?實例,則導航會被終止且該錯誤會被傳遞給?[`router.onError()`](https://router.vuejs.org/zh/api/#router-onerror)?注冊過的回調。
確保要調用?`next`?方法,否則鉤子就不會被 resolved。
## [#](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E8%A7%A3%E6%9E%90%E5%AE%88%E5%8D%AB)全局解析守衛
> 2.5.0 新增
在 2.5.0+ 你可以用?`router.beforeResolve`?注冊一個全局守衛。這和?`router.beforeEach`?類似,區別是在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之后,解析守衛就被調用。
## [#](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%90%8E%E7%BD%AE%E9%92%A9%E5%AD%90)全局后置鉤子
你也可以注冊全局后置鉤子,然而和守衛不同的是,這些鉤子不會接受?`next`?函數也不會改變導航本身:
~~~
router.afterEach((to, from) => {
// ...
})
~~~
## [#](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E8%B7%AF%E7%94%B1%E7%8B%AC%E4%BA%AB%E7%9A%84%E5%AE%88%E5%8D%AB)路由獨享的守衛
你可以在路由配置上直接定義?`beforeEnter`?守衛:
~~~
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
~~~
這些守衛與全局前置守衛的方法參數是一樣的。
## [#](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E7%BB%84%E4%BB%B6%E5%86%85%E7%9A%84%E5%AE%88%E5%8D%AB)組件內的守衛
最后,你可以在路由組件內直接定義以下路由導航守衛:
* `beforeRouteEnter`
* `beforeRouteUpdate`?(2.2 新增)
* `beforeRouteLeave`
~~~
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應路由被 confirm 前調用
// 不!能!獲取組件實例 `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` 訪問組件實例
})
}
~~~
注意?`beforeRouteEnter`?是支持給?`next`?傳遞回調的唯一守衛。對于?`beforeRouteUpdate`?和?`beforeRouteLeave`?來說,`this`?已經可用了,所以不支持傳遞回調,因為沒有必要了。
~~~
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
~~~
這個離開守衛通常用來禁止用戶在還未保存修改前突然離開。該導航可以通過?`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)
}
}
~~~
## [#](https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%AE%8C%E6%95%B4%E7%9A%84%E5%AF%BC%E8%88%AA%E8%A7%A3%E6%9E%90%E6%B5%81%E7%A8%8B)完整的導航解析流程
1. 導航被觸發。
2. 在失活的組件里調用離開守衛。
3. 調用全局的?`beforeEach`?守衛。
4. 在重用的組件里調用?`beforeRouteUpdate`?守衛 (2.2+)。
5. 在路由配置里調用?`beforeEnter`。
6. 解析異步路由組件。
7. 在被激活的組件里調用?`beforeRouteEnter`。
8. 調用全局的?`beforeResolve`?守衛 (2.5+)。
9. 導航被確認。
10. 調用全局的?`afterEach`?鉤子。
11. 觸發 DOM 更新。
12. 用創建好的實例調用?`beforeRouteEnter`?守衛中傳給?`next`?的回調函數。
# 路由元信息
定義路由的時候可以配置?`meta`?字段:
~~~
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
children: [
{
path: 'bar',
component: Bar,
// a meta field
meta: { requiresAuth: true }
}
]
}
]
})
~~~
那么如何訪問這個?`meta`?字段呢?
首先,我們稱呼?`routes`?配置中的每個路由對象為?路由記錄。路由記錄可以是嵌套的,因此,當一個路由匹配成功后,他可能匹配多個路由記錄
例如,根據上面的路由配置,`/foo/bar`?這個 URL 將會匹配父路由記錄以及子路由記錄。
一個路由匹配到的所有路由記錄會暴露為?`$route`?對象 (還有在導航守衛中的路由對象) 的?`$route.matched`?數組。因此,我們需要遍歷?`$route.matched`?來檢查路由記錄中的?`meta`?字段。
下面例子展示在全局導航守衛中檢查元字段:
~~~
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next() // 確保一定要調用 next()
}
})
~~~
`<span class="token punctuation" style="color: rgb(204, 204, 204);"></span>`
# 過渡動效
`<router-view>`?是基本的動態組件,所以我們可以用?`<transition>`?組件給它添加一些過渡效果:
~~~
<transition>
<router-view></router-view>
</transition>
~~~
[Transition 的所有功能](https://cn.vuejs.org/guide/transitions.html)?在這里同樣適用。
## [#](https://router.vuejs.org/zh/guide/advanced/transitions.html#%E5%8D%95%E4%B8%AA%E8%B7%AF%E7%94%B1%E7%9A%84%E8%BF%87%E6%B8%A1)單個路由的過渡
上面的用法會給所有路由設置一樣的過渡效果,如果你想讓每個路由組件有各自的過渡效果,可以在各路由組件內使用?`<transition>`?并設置不同的 name。
~~~
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}
const Bar = {
template: `
<transition name="fade">
<div class="bar">...</div>
</transition>
`
}
~~~
## [#](https://router.vuejs.org/zh/guide/advanced/transitions.html#%E5%9F%BA%E4%BA%8E%E8%B7%AF%E7%94%B1%E7%9A%84%E5%8A%A8%E6%80%81%E8%BF%87%E6%B8%A1)基于路由的動態過渡
還可以基于當前路由與目標路由的變化關系,動態設置過渡效果:
~~~
<!-- 使用動態的 transition name -->
<transition :name="transitionName">
<router-view></router-view>
</transition>
~~~
~~~
// 接著在父組件內
// watch $route 決定使用哪種過渡
watch: {
'$route' (to, from) {
const toDepth = to.path.split('/').length
const fromDepth = from.path.split('/').length
this.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'
}
}
~~~
查看完整例子請[移步這里](https://github.com/vuejs/vue-router/blob/next/examples/transitions/app.js)。
# 數據獲取
有時候,進入某個路由后,需要從服務器獲取數據。例如,在渲染用戶信息時,你需要從服務器獲取用戶的數據。我們可以通過兩種方式來實現:
* 導航完成之后獲取:先完成導航,然后在接下來的組件生命周期鉤子中獲取數據。在數據獲取期間顯示“加載中”之類的指示。
* 導航完成之前獲取:導航完成前,在路由進入的守衛中獲取數據,在數據獲取成功后執行導航。
從技術角度講,兩種方式都不錯 —— 就看你想要的用戶體驗是哪種。
## [#](https://router.vuejs.org/zh/guide/advanced/data-fetching.html#%E5%AF%BC%E8%88%AA%E5%AE%8C%E6%88%90%E5%90%8E%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE)導航完成后獲取數據
當你使用這種方式時,我們會馬上導航和渲染組件,然后在組件的?`created`?鉤子中獲取數據。這讓我們有機會在數據獲取期間展示一個 loading 狀態,還可以在不同視圖間展示不同的 loading 狀態。
假設我們有一個?`Post`?組件,需要基于?`$route.params.id`?獲取文章數據:
~~~
<template>
<div class="post">
<div class="loading" v-if="loading">
Loading...
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
</div>
</template>
~~~
~~~
export default {
data () {
return {
loading: false,
post: null,
error: null
}
},
created () {
// 組件創建完后獲取數據,
// 此時 data 已經被 observed 了
this.fetchData()
},
watch: {
// 如果路由有變化,會再次執行該方法
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// replace getPost with your data fetching util / API wrapper
getPost(this.$route.params.id, (err, post) => {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.post = post
}
})
}
}
}
~~~
## [#](https://router.vuejs.org/zh/guide/advanced/data-fetching.html#%E5%9C%A8%E5%AF%BC%E8%88%AA%E5%AE%8C%E6%88%90%E5%89%8D%E8%8E%B7%E5%8F%96%E6%95%B0%E6%8D%AE)在導航完成前獲取數據
通過這種方式,我們在導航轉入新的路由前獲取數據。我們可以在接下來的組件的?`beforeRouteEnter`?守衛中獲取數據,當數據獲取成功后只調用?`next`?方法。
~~~
export default {
data () {
return {
post: null,
error: null
}
},
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 路由改變前,組件就已經渲染完了
// 邏輯稍稍不同
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, (err, post) => {
this.setData(err, post)
next()
})
},
methods: {
setData (err, post) {
if (err) {
this.error = err.toString()
} else {
this.post = post
}
}
}
}
~~~
在為后面的視圖獲取數據時,用戶會停留在當前的界面,因此建議在數據獲取期間,顯示一些進度條或者別的指示。如果數據獲取失敗,同樣有必要展示一些全局的錯誤提醒。
# 滾動行為
使用前端路由,當切換到新路由時,想要頁面滾到頂部,或者是保持原先的滾動位置,就像重新加載頁面那樣。?`vue-router`?能做到,而且更好,它讓你可以自定義路由切換時頁面如何滾動。
注意: 這個功能只在支持?`history.pushState`?的瀏覽器中可用。
當創建一個 Router 實例,你可以提供一個?`scrollBehavior`?方法:
~~~
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滾動到哪個的位置
}
})
~~~
`scrollBehavior`?方法接收?`to`?和?`from`?路由對象。第三個參數?`savedPosition`?當且僅當?`popstate`導航 (通過瀏覽器的 前進/后退 按鈕觸發) 時才可用。
這個方法返回滾動位置的對象信息,長這樣:
* `{ x: number, y: number }`
* `{ selector: string, offset? : { x: number, y: number }}`?(offset 只在 2.6.0+ 支持)
如果返回一個 falsy (譯者注:falsy 不是?`false`,[參考這里](https://developer.mozilla.org/zh-CN/docs/Glossary/Falsy))的值,或者是一個空對象,那么不會發生滾動。
舉例:
~~~
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
~~~
對于所有路由導航,簡單地讓頁面滾動到頂部。
返回?`savedPosition`,在按下 后退/前進 按鈕時,就會像瀏覽器的原生表現那樣:
~~~
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
~~~
如果你要模擬“滾動到錨點”的行為:
~~~
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
}
}
}
~~~
我們還可以利用[路由元信息](https://router.vuejs.org/zh/guide/advanced/meta.html)更細顆粒度地控制滾動。查看完整例子請[移步這里](https://github.com/vuejs/vue-router/blob/next/examples/scroll-behavior/app.js)。
## [#](https://router.vuejs.org/zh/guide/advanced/scroll-behavior.html#%E5%BC%82%E6%AD%A5%E6%BB%9A%E5%8A%A8)異步滾動
> 2.8.0 新增
你也可以返回一個 Promise 來得出預期的位置描述:
~~~
scrollBehavior (to, from, savedPosition) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ x: 0, y: 0 })
}, 500)
})
}
~~~
將其掛載到從頁面級別的過渡組件的事件上,令其滾動行為和頁面過渡一起良好運行是可能的。但是考慮到用例的多樣性和復雜性,我們僅提供這個原始的接口,以支持不同用戶場景的具體實現。
# 路由懶加載
當打包構建應用時,Javascript 包會變得非常大,影響頁面加載。如果我們能把不同路由對應的組件分割成不同的代碼塊,然后當路由被訪問的時候才加載對應組件,這樣就更加高效了。
結合 Vue 的[異步組件](https://cn.vuejs.org/v2/guide/components-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6)和 Webpack 的[代碼分割功能](https://doc.webpack-china.org/guides/code-splitting-async/#require-ensure-/),輕松實現路由組件的懶加載。
首先,可以將異步組件定義為返回一個 Promise 的工廠函數 (該函數返回的 Promise 應該 resolve 組件本身):
~~~
const Foo = () => Promise.resolve({ /* 組件定義對象 */ })
~~~
第二,在 Webpack 2 中,我們可以使用[動態 import](https://github.com/tc39/proposal-dynamic-import)語法來定義代碼分塊點 (split point):
~~~
import('./Foo.vue') // 返回 Promise
~~~
注意
如果您使用的是 Babel,你將需要添加?[`syntax-dynamic-import`](https://babeljs.io/docs/plugins/syntax-dynamic-import/)?插件,才能使 Babel 可以正確地解析語法。
結合這兩者,這就是如何定義一個能夠被 Webpack 自動代碼分割的異步組件。
~~~
const Foo = () => import('./Foo.vue')
~~~
在路由配置中什么都不需要改變,只需要像往常一樣使用?`Foo`:
~~~
const router = new VueRouter({
routes: [
{ path: '/foo', component: Foo }
]
})
~~~
## [#](https://router.vuejs.org/zh/guide/advanced/lazy-loading.html#%E6%8A%8A%E7%BB%84%E4%BB%B6%E6%8C%89%E7%BB%84%E5%88%86%E5%9D%97)把組件按組分塊
有時候我們想把某個路由下的所有組件都打包在同個異步塊 (chunk) 中。只需要使用?[命名 chunk](https://webpack.js.org/guides/code-splitting-require/#chunkname),一個特殊的注釋語法來提供 chunk name (需要 Webpack > 2.4)。
~~~
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
~~~
Webpack 會將任何一個異步模塊與相同的塊名稱組合到相同的異步塊中。