總結
<router-link>
- 可以通過配置 tag 屬性生成別的標簽,默認為 a 標簽
- 當目標路由成功激活時,鏈接元素自動設置一個表示激活的 CSS 類名.router-link-active
- 無論是 HTML5 history 模式還是 hash 模式,它的表現行為一致,所以,當你要切換路由模式,或者在 IE9 降級使用 hash 模式,無須作任何變動。
- 在 HTML5 history 模式下,router-link 會守衛點擊事件,讓瀏覽器不再重新加載頁面。
- 當你在 HTML5 history 模式下使用 base 選項之后,所有的 to 屬性都不需要寫 (基路徑) 了。
- 注釋:基路徑是指具有path的路徑片段,例如:當
base:'/app'
時,需要訪問/app/about
時只需要輸入to="/about"
即可
- 注釋:
to='about'
頭部不含/
的路徑,是相對當前url的相對路徑。默認行為是刪除最后一個path然后拼接當前to指向的字符串上去。例如:當前url為http://localhost:8080/
會首先刪除''
然后拼接成http://localhost:8080/about
v-slot API (3.1.0 新增)
- router-link 通過一個作用域插槽暴露底層的定制能力。
- 在使用 v-slot API 時,需要向 router-link 傳入一個單獨的子元素。否則 router-link 將會把子元素包裹在一個 span 元素內。
- href:解析后的 URL。將會作為一個 a 元素的 href attribute。
- route:解析后的規范化的地址。注釋:就是當前route對象
- navigate:觸發導航的函數。會在必要時自動阻止事件,和 router-link 同理。
- 注釋:即執行navigate(e)路由被確認,執行頁面跳轉。需要傳入事件對象為參數
- isActive:如果需要應用激活的 class 則為 true。允許應用一個任意的 class。
- 注釋:被激活的是當前url所包含的所有片段,例如:當前路由為
http://localhost:8080/about/456
時http://localhost:8080/
的isActive為true
- isExactActive:如果需要應用精確激活的 class 則為 true。允許應用一個任意的 class。
- 如果你在 <a> 元素上添加一個 target="_blank",則 @click="navigate" 處理器會被忽略。
<router-link
to="/about"
v-slot="{ href, route, navigate, isActive, isExactActive }"
>
<NavLink :active="isActive" :href="href" @click="navigate"
>{{ route.fullPath }}</NavLink
>
</router-link>
<router-link> Props
to
- 類型: string | Location
- required
- 如果提供了 path,params 會被忽略
- 注釋:當變量存在多個時,例如:
/:id+
,那么需要params.id===[123,456]
可以生成/123/456
的路徑,並避免/被轉義。不過這時,porops.id傳入的就是數組而不是字符串
<router-link to="home">Home</router-link>
<router-link :to="{ path: 'home' }">Home</router-link>
<router-link :to="{ name: 'user', params: { userId: 123 }, query: { plan: 'private' }}">User</router-link>
replace
<router-link :to="{ path: '/abc'}" replace></router-link>
append
- 類型: boolean
- 默認值: false
- 設置 append 屬性后,則在當前 (相對) 路徑前添加基路徑。例如,我們從 /a 導航到一個相對路徑 b,如果沒有配置 append,則路徑為 /b,如果配了,則為 /a/b
- 注釋:可以視為完整保留當前url然后在后面添加to指向的相對路徑,當設置為以 / 開頭的絕對路徑時這個屬性是無效的
tag
- 類型: string
- 默認值: "a"
- 使用 tag prop 類指定何種標簽,同樣它還是會監聽點擊,觸發導航
active-class
- 類型: string
- 默認值: "router-link-active"
- 設置鏈接激活時使用的 CSS 類名。默認值可以通過路由的構造選項 linkActiveClass 來全局配置。
exact
- 類型: boolean
- 默認值: false
- “是否激活”默認類名的依據是包含匹配。 想要鏈接使用“精確匹配模式”,則使用 exact 屬性
- 注釋:未設置該屬性時,如果該鏈接被激活會添加 router-link-active 類名,當添加這個屬性后只有當鏈接被精確匹配才會添加類名,而且類名是 router-link-exact-active
<!-- 這個鏈接只會在地址為 / 的時候被激活 -->
<router-link to="/" exact></router-link>
event
- 類型: string | Array
- 默認值: 'click'
- 聲明可以用來觸發導航的事件。可以是一個字符串或是一個包含字符串的數組。
exact-active-class
- 類型: string
- 默認值: "router-link-exact-active"
- 配置當鏈接被精確匹配的時候應該激活的 class。注意默認值也是可以通過路由構造函數選項 linkExactActiveClass 進行全局配置的
——————————————————————————————————————————————————————————————————
<router-view>
- 其他屬性 (非 router-view 使用的屬性) 都直接傳給渲染的組件
- 因為它也是個組件,所以可以配合 <transition> 和 <keep-alive> 使用。如果兩個結合一起用,要確保在內層使用 <keep-alive>
- 如果你想讓每個路由組件有各自的過渡效果,可以在各路由組件內使用 <transition> 並設置不同的 name。
- 也可以在<router-view>外嵌套<transition>並綁定變量name,通過 watch $route 決定使用哪種過渡
<router-view> Props
name
- 類型: string
- 默認值: "default"
——————————————————————————————————————————————————————————————————
Router 構建選項
- 注釋:返回一個 router 對象
const router = new VueRouter({...})
routes
- 類型: Array<RouteConfig>
- routes 配置中的每個路由對象為 路由記錄
- 當一個路由匹配成功后,他可能匹配多個路由記錄
interface RouteConfig = {
path: string, //
component?: Component, // 可以是通過 Vue.extend() 創建的組件構造器,或者,只是一個組件配置對象。
name?: string, // 命名路由
components?: { [name: string]: Component }, // 命名視圖組件
redirect?: string | Location | Function, // 重定向
props?: boolean | Object | Function, // 動態路徑參數作為組件props
alias?: string | Array<string>, // 別名
children?: Array<RouteConfig>, // 嵌套路由
beforeEnter?: (to: Route, from: Route, next: Function) => void,
meta?: any,
// 2.6.0+
caseSensitive?: boolean, // 匹配規則是否大小寫敏感?(默認值:false)
pathToRegexpOptions?: Object // 編譯正則的選項
}
- path 詳解
- 可以多種參數類型、常量混搭,例如
/(apple-)?icon-:res(\\d+).png
// 動態路徑參數 以冒號開頭。
{ path: '/user/:id', component: User }
// 可以在一個路由中設置多段“路徑參數”,對應的值都會設置到 $route.params 中。
/user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: '123' }
// 注釋:*代表該變量有0個或者多個,例如:該例子中能夠匹配 /user/123 、 /user/ 、 /user
// 注釋:動態路由參數只能匹配一對間隔符號之間的值,如果有多個間隔符就需要*來匹配。例如:該例子中匹配/user/123/456,這時foo==='123/456'
// 注釋:/user/:foo* 並不等價於/user(/:foo)* 。原因不明
// 注釋:'/foo/:bar' 被解析為 /^\/foo\/([^\/]+?)\/?$/i 用來對路徑進行匹配。[^\/]表示非/字符,+?表示非貪婪匹配,非貪婪匹配
/user/:foo*
// a param can be made optional by adding "?" 注釋:可選參數
{ path: '/optional-params/:foo?' },
// 匹配一個或多個 +
/:foo+
// 注釋:帶有正則驗證,默認的 [^\/]+,所以動態路徑只能匹配一組分隔符間的值
{ path: '/params-with-regex/:id(\\d+)' },
// 注釋:可以只寫正則作為未命名參數,該path 可以匹配'/test/route'
/:foo/(.*)
// 會匹配所有路徑,包括/
path: '*'
// 會匹配以 `/user-` 開頭的任意路徑,不能夠匹配不包含user-的路徑
path: '/user-*'
// 注釋:可選路徑,常量時和其他配合時需要用()包裹
// 注釋:當是常量時params通過數值key傳入參數,而且所有參數必須符合[A-Za-z0-9_],所以這里的'foo/'是無法通過params傳入的,所以建議改為'{ path: '/optional-group/(foo)?/bar' }'
// 注釋:'{ path: '/optional-group/(foo)+/bar' }'依然無法在name跳轉時通過params傳入多個值,有未命名路徑參數時通過pathMatch或數值key通過params傳入
{ path: '/optional-group/(foo/)?bar' }
- children詳解
- 以 / 開頭的嵌套路徑會被當作根路徑。 這讓你充分的使用嵌套組件而無須設置嵌套的路徑。
- 沒有匹配到合適的子路由,出口是不會渲染任何東西,如果你想要渲染點什么,可以提供一個 空的 子路由
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{ path: '', component: UserHome },
{
// 當 /user/:id/profile 匹配成功,
// UserProfile 會被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
}
]
}
]
})
- redirect詳解
- 注釋:重定向后的參數可以是定值,例如:
{ path: '/redirect-with-params/:id', redirect: '/with-params/456' }
這個時候params.id === '456'
- 注釋:重定向前后的path段不需要完全匹配,例如:
{ path: '/redirect-with-params/other-path/:id', redirect: '/with-params/:id' }
這個時候id依然能夠正常傳遞
- 注釋:重定向的參數依據定向前參數名傳遞,而不是依據定向目標url參數名。例如目標 path 為
/with-params/:id
當前route可寫為{ path: '/redirect-with-params/:other-id', redirect: '/with-params/:other-id' }
- 注釋:重定向前的url不會寫入history中?
- 注釋:如果當前路由是目標路由重定向后的路由,該跳轉是不會被執行的,也不存在又一次重定向。
- 注意導航守衛並沒有應用在跳轉路由上,而僅僅應用在其目標上。
- 注釋:重定向會自動帶上?及查詢參數
const router = new VueRouter({
routes: [
{ path: '/redirect-with-params/:id', redirect: '/with-params/:id' }
]
})
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/root', component: Root, alias: '/root-alias' },
{ path: '/home', component: Home,
children: [
// absolute alias 絕對別名
{ path: 'foo', component: Foo, alias: '/foo' },
// relative alias (alias to /home/bar-alias) 相對別名
{ path: 'bar', component: Bar, alias: 'bar-alias' },
// multiple aliases 多個別名
{ path: 'baz', component: Baz, alias: ['/baz', 'baz-alias'] },
// default child route with empty string as alias. 空字符串別名
{ path: 'default', component: Default, alias: '' },
// nested alias 嵌套別名,/home/nested-alias/foo 等同於訪問 /home/nested/foo
{ path: 'nested', component: Nested, alias: 'nested-alias',
children: [
{ path: 'foo', component: NestedFoo }
]
}
]
}
]
})
// 區分大小寫,注釋:谷歌輸入URL會自動轉為小寫
{ path: '/foobar', component: Foobar, caseSensitive: true }
- pathToRegexpOptions詳解
- sensitive: When true the route will be case sensitive. (default: false) true時區分大小寫
- strict: When false the trailing slash is optional. (default: false) false時尾部斜杠在正則中為可有可無
- end: When false the path will match at the beginning. (default: true) false時正則將擁有,疑問:好像true才是擁有
- delimiter: Set the default delimiter for repeat parameters. (default: '/') 設置默認間隔符號
// 編譯正則的選項,這里應該是區分大小寫,不知道還有哪些
{ path: '/FooBar', component: FooBar, pathToRegexpOptions: { sensitive: true }}
- props詳解
- 如果routes中使用component標明組件, 且 props 是一個對象,它會被按原樣設置為組件props(為props傳入固定值)
- 請盡可能保持 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 }
}
// 創建一個函數返回 props
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
- component詳解,包括components中的
- 可以將異步組件定義為返回一個 Promise 的工廠函數 (該函數返回的 Promise 應該 resolve 組件本身)
- 注釋:Babel 把 import() 解析為 Promise 對象。而 Webpack 使用該標志來進行代碼分塊,注釋語句也是有 Webpack 提供。
- 把某個路由下的所有組件都打包在同個異步塊 (chunk) 中。只需要使用 命名 chunk,一個特殊的注釋語法來提供 chunk name (需要 Webpack > 2.4)
- Vue 允許你以一個工廠函數的方式定義你的組件,這個工廠函數會異步解析你的組件定義。Vue 只有在這個組件需要被渲染的時候才會觸發該工廠函數,且會把結果緩存起來供未來重渲染。
- 這個工廠函數會收到一個 resolve 回調,這個回調函數會在你從服務器得到組件定義的時候被調用
- 可以調用 reject(reason) 來表示加載失敗。
- 這里的異步組件工廠函數也可以返回一個如下格式的對象:(2.3.0+ 新增)
- 如果你希望在 Vue Router 的路由組件中使用上述語法的話,你必須使用 Vue Router 2.4.0+ 版本。
// promise
component:() => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
// 工廠函數返回組件配置對象
component:function (resolve, reject) {
resolve({
template: '<div>I am async!</div>'
})
}
// 或構造函數
component:Vue.extend({
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data: function () {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
})
// 工廠函數返回特定對象
component: () => ({
// 需要加載的組件 (應該是一個 `Promise` 對象)
component: import('./MyComponent.vue'),
// 異步組件加載時使用的組件
loading: LoadingComponent,
// 加載失敗時使用的組件
error: ErrorComponent,
// 展示加載時組件的延時時間。默認值是 200 (毫秒)
delay: 200,
// 如果提供了超時時間且組件加載也超時了,
// 則使用加載失敗時使用的組件。默認值是:`Infinity`
timeout: 3000
})
mode
- 類型: string
- 默認值: "hash" (瀏覽器環境) | "abstract" (Node.js 環境)
- 可選值: "hash" | "history" | "abstract"
- abstract: 支持所有 JavaScript 運行環境,如 Node.js 服務器端。如果發現沒有瀏覽器的 API,路由會自動強制進入這個模式。
base
- 類型: string
- 默認值: "/"
- 應用的基路徑。例如,如果整個單頁應用服務在 /app/ 下,然后 base 就應該設為 "/app/"
linkActiveClass
- 類型: string
- 默認值: "router-link-active"
- 類型: Function
- 這個功能只在支持 history.pushState 的瀏覽器中可用
- 第三個參數 savedPosition 當且僅當 popstate 導航 (通過瀏覽器的 前進/后退 按鈕觸發) 時才可用。
- 注釋:scrollBehavior只有在路由切換時才觸發,通過url進入頁面不會觸發
- 可以利用路由元信息更細顆粒度地控制滾動。
- 可以返回一個 Promise 來得出預期的位置描述(2.8.0 新增),可以用於等待頁面動畫結束之后才觸發滾動(見之后的例子)
type PositionDescriptor =
{ x: number, y: number } |
{ selector: string, offset? : { x: number, y: number }} | // offset 只在 2.6.0+ 支持
?{} // 如果返回一個 falsy 的值,或者是一個空對象,那么不會發生滾動。
type scrollBehaviorHandler = (
to: Route,
from: Route,
savedPosition?: { x: number, y: number }
) => PositionDescriptor | Promise<PositionDescriptor>
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// savedPosition
return savedPosition
// 滾動到錨點
return {
selector: to.hash
}
// 偏離
return {
offset: { y: 100 }
}
// promise
return new Promise(resolve => {...})
}
})
parseQuery / stringifyQuery
- 類型: Function
- 提供自定義查詢字符串的解析/反解析函數。覆蓋默認行為。
- 注釋:parseQuery 接收 url 傳入的查詢條件,不包含?當通過非path方式跳轉時,parseQuery 不被執行。
- 注釋:stringifyQuery ,當
to:{...,query:{...}}}
時接收query傳入的對象。當為path時接收parseQuery 的返回值。該函數的返回值作為url的查詢參數,需要包含?。當通過to:{...,query:{...}}}
方式跳轉時,返回值不會替換url的查詢參數
- 注釋:通過url訪問頁面時,先執行當前頁面的parseQuery 和stringifyQuery 。然后執行所有路由的這兩個函數
- 注釋:當點擊同一個路由時只執行當前路由的stringifyQuery
- 注釋:當通過路由跳轉時,先執行當前路由的stringifyQuery ,然后執行所有路由的這兩個函數
fallback
- 類型: boolean
- 當瀏覽器不支持 history.pushState 控制路由是否應該回退到 hash 模式。默認值為 true。
- 在 IE9 中,設置為 false 會使得每個 router-link 導航都觸發整頁刷新。它可用於工作在 IE9 下的服務端渲染應用,因為一個 hash 模式的 URL 並不支持服務端渲染。
——————————————————————————————————————————————————————————————————
Router 實例屬性
router.app
- 類型: Vue instance(實例)
- 配置了 router 的 Vue 根實例。
router.mode
router.currentRoute
Router 實例方法
router.beforeEach
router.beforeResolve
router.afterEach
router.push
- location 該方法的參數可以是一個字符串路徑,或者一個描述地址的對象
- 在 2.2.0+,可選的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回調作為第二個和第三個參數。這些回調將會在導航成功完成 (在所有的異步鈎子被解析之后) 或終止 (導航到相同的路由、或在當前導航完成之前導航到另一個不同的路由) 的時候進行相應的調用。
- 在 3.1.0+,可以省略第二個和第三個參數,此時如果支持 Promise,router.push 或 router.replace 將返回一個 Promise
router.push(location, onComplete?, onAbort?)
router.push(location).then(onComplete).catch(onAbort)
router.replace
router.replace(location, onComplete?, onAbort?)
router.replace(location).then(onComplete).catch(onAbort)
router.go
- 如果 history 記錄不夠用,那就默默地失敗唄
router.go(n)
router.back
router.forward(向前)
router.getMatchedComponents
- 返回目標位置或是當前路由匹配的組件數組 (是數組的定義/構造類,不是實例)。通常在服務端渲染的數據預加載時使用。
const matchedComponents: Array<Component> = router.getMatchedComponents(location?)
router.resolve
- 解析目標位置 (格式和 <router-link> 的 to prop 一樣)。
- current 是當前默認的路由 (通常你不需要改變它)
- 注釋:current是一個路由信息對象,當參數1為相對路徑字符串時,相對的就是該值進行解析。當該值為空時默認相對當前路徑
- append 允許你在 current 路由上附加路徑 (如同 router-link)
const resolved: {
location: Location;
route: Route;
href: string;
} = router.resolve(location, current?, append?)
router.addRoutes
- 動態添加更多的路由規則。參數必須是一個符合 routes 選項要求的數組。
- 注釋:可以根據后端返回的菜單接口生成路由數組,然后動態加入。用於控制路由權限,會比路由攔截更加節省性能
router.addRoutes(routes: Array<RouteConfig>)
router.onReady
- 該方法把一個回調排隊,在路由完成初始導航時調用,這意味着它可以解析所有的異步進入鈎子和路由初始化相關聯的異步組件。
- 注釋:先執行所有路由守衛,然后才執行onReady,在beforeCreate之前
- 這可以有效確保服務端渲染時服務端和客戶端輸出的一致。
- 第二個參數 errorCallback 只在 2.4+ 支持。它會在初始化路由解析運行出錯 (比如解析一個異步組件失敗) 時被調用。
- 注釋:callback接收一個to路由信息對象作為參數
router.onReady(callback, [errorCallback])
router.onError
- 注冊一個回調,該回調會在路由導航過程中出錯時被調用。注意被調用的錯誤必須是下列情形中的一種
- 錯誤在一個路由守衛函數中被同步拋出;
- 錯誤在一個路由守衛函數中通過調用 next(err) 的方式異步捕獲並處理;
- 渲染一個路由的過程中,需要嘗試解析一個異步組件時發生錯誤。
router.onError(callback)
——————————————————————————————————————————————————————————————————
路由對象
- 一個路由對象 (route object) 表示當前激活的路由的狀態信息,包含了當前 URL 解析得到的信息,還有 URL 匹配到的路由記錄 (route records)。
- 路由對象是不可變 (immutable) 的,每次成功的導航后都會產生一個新的對象。
- 路由對象出現在多個地方
- 在組件內,即 this.$route
- 在 $route 觀察者回調內,組件復用時組件的生命周期鈎子不會再被調用
- router.match(location) 的返回值。注釋:新版本文檔中已經沒有該方法了,可能被替換為 router.resolve
- 注釋:router.resolve的返回值的route屬性也是一個路由對象
- 導航守衛的參數
- scrollBehavior 方法的參數
路由對象屬性
$route.path
- 類型: string
- 字符串,對應當前路由的路徑,總是解析為絕對路徑,如 "/foo/bar"
$route.params
- 類型: Object
- 一個 key/value 對象,包含了動態片段和全匹配片段,如果沒有路由參數,就是一個空對象。
- 注釋:全匹配片段是指
*
或者(foo/)?
等方式匹配的路由
- 注釋:全匹配片段通過 pathMatch 或數值 key 訪問
- 注釋:非具名參數匹配的字段如果只有單個,對應的key為
pathMatch
,如果有多個,那么后續key由1開始。例如:path: "/other/(foo/)?(foo2/)?bar"
中pathMatch==='foo/
'1==='foo2/'
- 注釋:其實
0==='foo/'
和pathMatch是一樣的
$route.query
- 類型: Object
- 一個 key/value 對象,表示 URL 查詢參數。例如,對於路徑 /foo?user=1,則有 $route.query.user == 1,如果沒有查詢參數,則是個空對象。
- 注釋:始終和url的?查詢參數保持一致,即當url被stringifyQuery改變時才發生改變,和parseQuery / stringifyQuery的返回結果沒有直接關系,通過name跳轉頁面時,stringifyQuery是不會改變url的
$route.hash
- 類型: string
- 當前路由的 hash 值 (帶 #) ,如果沒有 hash 值,則為空字符串。
$route.fullPath
- 類型: string
- 完成解析后的 URL,包含查詢參數和 hash 的完整路徑。
$route.matched
- 類型: Array<RouteRecord>
- 一個數組,包含當前路由的所有嵌套路徑片段的路由記錄 。路由記錄就是 routes 配置數組中的對象副本 (還有在 children 數組)。
- 注釋:數組是因為路由可能存在嵌套,每一級都會匹配一個路由記錄。
- 注釋:雖然是副本,但不完全等於路由配置項
$route.name
$route.redirectedFrom
- 如果存在重定向,即為重定向來源的路由的名字。
- 注釋:如果/a重定向到/b,當前頁面是/b時,點擊/a鏈接是不會跳轉和重定向的,該值也將返回空。
——————————————————————————————————————————————————————————————————
組件注入
注入的屬性
- 通過在 Vue 根實例的 router 配置傳入 router 實例,下面這些屬性成員會被注入到每個子組件。
- this.$router
- this.$route 當前激活的路由信息對象。這個屬性是只讀的,里面的屬性是 immutable (不可變) 的,不過你可以 watch (監測變化) 它。
增加的組件配置選項
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
——————————————————————————————————————————————————————————————————
導航守衛
- 導航被觸發。
- 在失活的組件里調用離開守衛。beforeRouteLeave
- 參數或查詢的改變並不會觸發進入/離開的導航守衛。
- 可以訪問組件實例
this
- 調用全局的 beforeEach(全局前置守衛) 守衛。
router.beforeEach((to, from, next) => {...})
- 當一個導航觸發時,全局前置守衛按照創建順序調用。
- 在 2.5.0+ 這三個方法都返回一個移除已注冊的守衛/鈎子的函數。
- 在重用的組件里調用 beforeRouteUpdate 守衛 (2.2+)。
- 2.2 新增
- 由於會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鈎子就會在這個情況下被調用。可以訪問組件實例
this
- 注釋:初次進入不會觸發,只有當前頁面復用才會觸發,需要對頁面進行初始化建議使用 watch $route
- 在路由配置里調用 beforeEnter。
routes[0].beforeEnter = (to, from, next) => {...}
- 參數或查詢的改變並不會觸發進入/離開的導航守衛。
- 注釋:beforeEnter無法訪問 vue 實例
- 解析異步路由組件。
- 在被激活的組件里調用 beforeRouteEnter。
beforeRouteEnter (to, from, next) { next(vm => {...}) }
- 參數或查詢的改變並不會觸發進入/離開的導航守衛。
- 不!能!獲取組件實例
this
- 可以通過傳一個回調給 next來訪問組件實例。在導航被確認的時候執行回調,並且把組件實例作為回調方法的參數。
- 調用全局的 beforeResolve 守衛 (2.5+)。
router.beforeResolve((to, from, next) => {...})
- 在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之后,解析守衛就被調用。
- 在 2.5.0+ 這三個方法都返回一個移除已注冊的守衛/鈎子的函數。
- 注釋:router.beforeResolve 無法訪問 vue 實例
- 導航被確認。
- 導航被確認前,用戶會停留在當前的界面,如果采用在路由守衛中獲取數據,建議在數據獲取期間,顯示一些進度條或者別的指示。
- 調用全局的 afterEach 鈎子。
router.afterEach((to, from) => {...})
- 在 2.5.0+ 這三個方法都返回一個移除已注冊的守衛/鈎子的函數。
- 注釋:router.afterEach 中沒有 vue 實例
- 觸發 DOM 更新。
- 用創建好的實例調用 beforeRouteEnter 守衛中傳給 next 的回調函數。
next
- next: Function: 一定要調用該方法來 resolve 這個鈎子。執行效果依賴 next 方法的調用參數。
- next(): 進行管道中的下一個鈎子。如果全部鈎子執行完了,則導航的狀態就是 confirmed (確認的)。
- next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應的地址。
- 注釋:當輸入url時(或點返回時),url首先改變請求新的文檔,如果該文檔中的路由守衛返回next(false)的話,會重置當前url到from路由對應的地址。但是頁面並不能正常,應該是執行了history.go(-1),谷歌中history.go應該是不會重新執行頁面中的JS的
- 注釋:被 next(false) 的路由是不會寫入 history 中的。
- 注釋:在谷歌中點擊后退,被中斷的路由依然會記錄目標路由。即點前進按鈕可以進到目標路由地址。但是改變url卻不會
- next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。你可以向 next 傳遞任意位置對象,且允許設置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。
- 注釋:這種跳轉類似於重定向,不會在history中寫入跳轉前地址。
- next(error): (2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調。
以下為原文————————————————————————————————————————————————————————————————————————————————————
安裝
直接下載 / CDN
NPM
npm install vue-router
- 在一個模塊化工程中使用它,必須要通過 Vue.use() 明確地安裝路由功能
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
構建開發版
介紹
- 對於 TypeScript 用戶來說,vue-router@3.0+ 依賴 vue@2.5+,反之亦然。
起步
HTML
<!-- 通過傳入 `to` 屬性指定鏈接. -->
<!-- <router-link> 默認會被渲染成一個 `<a>` 標簽 -->
<router-link to="/foo">Go to Foo</router-link>
<!-- 路由出口 -->
<!-- 路由匹配到的組件將渲染在這里 -->
<router-view></router-view>
JavaScript
// 每個路由應該映射一個組件。 其中"component" 可以是
// 通過 Vue.extend() 創建的組件構造器,
// 或者,只是一個組件配置對象。
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
const router = new VueRouter({
routes
})
- 通過注入路由器,我們可以在任何組件內通過 this.$router 訪問路由器,也可以通過 this.$route 訪問當前路由
- this.$router 和 router 使用起來完全一樣
- 當 <router-link> 對應的路由匹配成功,將自動設置 class 屬性值 .router-link-active。
動態路由匹配
- “動態路徑參數”(dynamic segment)
- 當匹配到一個路由時,參數值會被設置到 this.$route.params
const router = new VueRouter({
routes: [
// 動態路徑參數 以冒號開頭
{ path: '/user/:id', component: User }
]
})
- 可以在一個路由中設置多段“路徑參數”,對應的值都會設置到 $route.params 中
/user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: '123' }
響應路由參數的變化
- 當使用路由參數時,例如從 /user/foo 導航到 /user/bar,原來的組件實例會被復用。
- 這也意味着組件的生命周期鈎子不會再被調用。
- 復用組件時,想對路由參數的變化作出響應的話,你可以簡單地 watch (監測變化) $route 對象
const User = {
template: '...',
watch: {
'$route' (to, from) {
// 對路由變化作出響應...
}
}
}
- 或者使用 2.2 中引入的 beforeRouteUpdate 導航守衛
- 注釋:初次進入不會觸發,只有當前頁面復用才會觸發,需要對頁面進行初始化建議使用 watch $route
const User = {
template: '...',
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
}
捕獲所有路由或 404 Not found 路由
{
// 會匹配所有路徑
path: '*'
}
{
// 會匹配以 `/user-` 開頭的任意路徑
path: '/user-*'
}
- 當使用通配符路由時,請確保路由的順序是正確的,也就是說含有通配符的路由應該放在最后(404錯誤頁面)
- 如果你使用了History 模式,請確保正確配置你的服務器。
- 當使用一個通配符時,$route.params 內會自動添加一個名為 pathMatch 參數。它包含了 URL 通過通配符被匹配的部分
// 給出一個路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 給出一個路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
高級匹配模式
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/' },
// params are denoted with a colon ":"
{ path: '/params/:foo/:bar' },
// a param can be made optional by adding "?" 注釋:可選參數
{ path: '/optional-params/:foo?' },
// a param can be followed by a regex pattern in parens
// this route will only be matched if :id is all numbers
{ path: '/params-with-regex/:id(\\d+)' }, 注釋:正則驗證
// asterisk can match anything
{ path: '/asterisk/*' },
// make part of the path optional by wrapping with parens and add "?" 注釋:可選路徑
{ path: '/optional-group/(foo/)?bar' }
]
})
匹配優先級
- 同一個路徑可以匹配多個路由,此時,匹配的優先級就按照路由的定義順序:誰先定義的,誰的優先級就最高。
嵌套路由
- 創建vue實例時使用的根組件包含的 <router-view> 是最頂層的出口,渲染最高級路由匹配到的組件。
- 要在嵌套的出口中渲染組件,需要在 VueRouter 的參數中使用 children 配置
const User = {
template: `
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view></router-view>
</div>
`
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 當 /user/:id/profile 匹配成功,
// UserProfile 會被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
}
]
}
]
})
- 要注意,以 / 開頭的嵌套路徑會被當作根路徑。 這讓你充分的使用嵌套組件而無須設置嵌套的路徑。
- 注釋:當children中的path使用/開頭的根路徑時,通過該路徑直接訪問到這個嵌套組件(父及祖組件都會被渲染)。簡而言之,組件時嵌套的,但路由不是。
- 基於上面的配置,當你訪問 /user/foo 時,User 的出口是不會渲染任何東西,這是因為沒有匹配到合適的子路由。如果你想要渲染點什么,可以提供一個 空的 子路由
const router = new VueRouter({
routes: [
{
path: '/user/:id', component: User,
children: [
// 當 /user/:id 匹配成功,
// UserHome 會被渲染在 User 的 <router-view> 中
{ path: '', component: UserHome },
// ...其他子路由
]
}
]
})
編程式的導航
router.push(location, onComplete?, onAbort?)
- 當你點擊 <router-link> 時,這個方法會在內部調用
- 該方法的參數可以是一個字符串路徑,或者一個描述地址的對象
- 注釋:同一個router實例中不允許出現相同的name,即使他們在不同的嵌套層級,控制台會出現提示
- 注釋:通過父級命名路由跳轉,不會匹配
children
中path:""
指向的組件
// 字符串
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 會被忽略
- 同樣的規則也適用於 router-link 組件的 to 屬性
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
- 在 2.2.0+,可選的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回調作為第二個和第三個參數。這些回調將會在導航成功完成 (在所有的異步鈎子被解析之后) 或終止 (導航到相同的路由、或在當前導航完成之前導航到另一個不同的路由) 的時候進行相應的調用。
- 在 3.1.0+,可以省略第二個和第三個參數,此時如果支持 Promise,router.push 或 router.replace 將返回一個 Promise
router.replace(location, onComplete?, onAbort?)
<router-link :to="..." replace> router.replace(...)
router.go(n)
// 如果 history 記錄不夠用,那就默默地失敗唄
router.go(-100)
router.go(100)
操作 History
- Vue Router 的導航方法 (push、 replace、 go) 在各類路由模式 (history、 hash 和 abstract) 下表現一致
命名路由
命名視圖
- 同時 (同級) 展示多個視圖
- 如果 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>
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
嵌套命名視圖
<!-- UserSettings.vue -->
<div>
<h1>User Settings</h1>
<NavBar/>
<router-view/>
<router-view name="helper"/>
</div>
{
path: '/settings',
// 你也可以在頂級路由就配置命名視圖
component: UserSettings,
children: [{
path: 'emails',
component: UserEmailsSubscriptions
}, {
path: 'profile',
components: {
default: UserProfile,
helper: UserProfilePreview
}
}]
}
重定向和別名
重定向
- 下面例子是從 /redirect-with-params/:id 重定向到 /with-params/:id
- 注釋:重定向后會顯示定向后的url,router-link-active也被添加到定向后的router-link上
- 注釋:重定向后的參數可以是定值,例如:
{ path: '/redirect-with-params/:id', redirect: '/with-params/456' }
這個時候params.id === '456'
- 注釋:重定向前后的path段不需要完全匹配,例如:
{ path: '/redirect-with-params/other-path/:id', redirect: '/with-params/:id' }
這個時候id依然能夠正常傳遞
- 注釋:重定向的參數依據定向前參數名傳遞,而不是依據定向目標url參數名。例如目標 path 為
/with-params/:id
當前route可寫為{ path: '/redirect-with-params/:other-id', redirect: '/with-params/:other-id' }
- 注釋:重定向前的url不會寫入history中?
- 注釋:如果當前路由是目標路由重定向后的路由,該跳轉是不會被執行的,也不存在又一次重定向。
const router = new VueRouter({
routes: [
{ path: '/redirect-with-params/:id', redirect: '/with-params/:id' }
]
})
- 重定向的目標也可以是一個命名的路由
- 注釋:同字符串,
params.id
可以設置為固定值
const router = new VueRouter({
routes: [
{ path: '/a', redirect: { name: 'foo' }}
]
})
- 甚至是一個方法,動態返回重定向目標
- 注釋:返回的值等價於字符串或對象即可,不需要生成含有確認參數的路徑。例如:
return '/with-params/:id'
const router = new VueRouter({
routes: [
{ path: '/a', redirect: to => {
// 方法接收 目標路由 作為參數
// return 重定向的 字符串路徑/路徑對象
}}
]
})
- 注意導航守衛並沒有應用在跳轉路由上,而僅僅應用在其目標上。在下面這個例子中,為 /a 路由添加一個 beforeEach 或 beforeLeave 守衛並不會有任何效果
- 高級用法
// redirect with caseSensitive 區分大小寫
// 注釋:谷歌輸入URL會自動轉為小寫
// 注釋:在vue中通過path跳轉,可以使用大寫。但通過地址欄輸入不行,建議不開啟該功能
{ path: '/foobar', component: Foobar, caseSensitive: true }
// redirect with pathToRegexpOptions 編譯正則的選項
{ path: '/FooBar', component: FooBar, pathToRegexpOptions: { sensitive: true }},
別名
- /a 的別名是 /b,意味着,當用戶訪問 /b 時,URL 會保持為 /b,但是路由匹配則為 /a
const router = new VueRouter({
routes: [
{ path: '/a', component: A, alias: '/b' }
]
})
- “別名”的功能讓你可以自由地將 UI 結構映射到任意的 URL,而不是受限於配置的嵌套路由結構。
- 疑問:似乎children中的path由/開頭也有相同效果
- 高級用法
const router = new VueRouter({
mode: 'history',
base: __dirname,
routes: [
{ path: '/root', component: Root, alias: '/root-alias' },
{ path: '/home', component: Home,
children: [
// absolute alias 絕對別名
{ path: 'foo', component: Foo, alias: '/foo' },
// relative alias (alias to /home/bar-alias) 相對別名
{ path: 'bar', component: Bar, alias: 'bar-alias' },
// multiple aliases 多個別名
{ path: 'baz', component: Baz, alias: ['/baz', 'baz-alias'] },
// default child route with empty string as alias. 空字符串別名
{ path: 'default', component: Default, alias: '' },
// nested alias 嵌套別名,/home/nested-alias/foo 等同於訪問 /home/nested/foo
{ path: 'nested', component: Nested, alias: 'nested-alias',
children: [
{ path: 'foo', component: NestedFoo }
]
}
]
}
]
})
路由組件傳參
- 在組件中使用 $route 會使之與其對應路由形成高度耦合,從而使組件只能在某些特定的 URL 上使用
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 }
}
]
})
布爾模式
對象模式
- 如果 props 是一個對象,它會被按原樣設置為組件屬性
- 注釋:這里是指和component配合使用時設置成對象,等於向組件props傳遞了一個固定值。例如:
props:{id:10}
函數模式
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
- 請盡可能保持 props 函數為無狀態的,因為它只會在路由發生變化時起作用
HTML5 History 模式
后端配置例子
Apache(Web服務器軟件,略)
nginx(Web 服務器/反向代理服務器及電子郵件(IMAP/POP3)代理服務器,反向代理作為Web服務器的前置機來降低網絡和服務器的負載,略)
原生 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)
})
基於 Node.js 的 Express
Caddy(類似 Nginx,略)
Firebase 主機(谷歌開發APP的框架,略)
警告
- 使用 Node.js 服務器,你可以用服務端路由匹配到來的 URL,並在沒有匹配到路由的時候返回 404,以實現回退。
導航守衛
- 有多種機會植入路由導航過程中:全局的, 單個路由獨享的, 或者組件級的
- 參數或查詢的改變並不會觸發進入/離開的導航守衛。
全局前置守衛
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
- 當一個導航觸發時,全局前置守衛按照創建順序調用。
- 注釋:多次調用 router.beforeEach() 可以注冊多個全局前置守衛
- 每個守衛方法接收三個參數:
- to: Route: 即將要進入的目標 路由對象
- from: Route: 當前導航正要離開的路由
- next: Function: 一定要調用該方法來 resolve 這個鈎子。執行效果依賴 next 方法的調用參數。
- next(): 進行管道中的下一個鈎子。如果全部鈎子執行完了,則導航的狀態就是 confirmed (確認的)。
- next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應的地址。
- 注釋:當輸入url時(或點返回時),url首先改變請求新的文檔,如果該文檔中的路由守衛返回next(false)的話,會重置當前url到from路由對應的地址。但是頁面並不能正常,應該是執行了history.go(-1),谷歌中history.go應該是不會重新執行頁面中的JS的
- 注釋:被 next(false) 的路由是不會寫入 history 中的。
- 注釋:在谷歌中點擊后退,被中斷的路由依然會記錄目標路由。即點前進按鈕可以進到目標路由地址。但是改變url卻不會
- next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然后進行一個新的導航。你可以向 next 傳遞任意位置對象,且允許設置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。
- 注釋:這種跳轉類似於重定向,不會在history中寫入跳轉前地址。
- next(error): (2.4.0+) 如果傳入 next 的參數是一個 Error 實例,則導航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調。
全局解析守衛
- 2.5.0 新增
- router.beforeResolve 在導航被確認之前,同時在所有組件內守衛和異步路由組件被解析之后,解析守衛就被調用。
- 注釋:router.beforeResolve 無法訪問 vue 實例
全局后置鈎子
- 你也可以注冊全局后置鈎子,然而和守衛不同的是,這些鈎子不會接受 next 函數也不會改變導航本身
- 注釋:router.afterEach 中沒有 vue 實例
router.afterEach((to, from) => {
// ...
})
路由獨享的守衛
- 注釋:beforeEnter無法訪問 vue 實例
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
組件內的守衛
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應路由被 confirm(確認) 前調用
// 不!能!獲取組件實例 `this`
// 因為當守衛執行前,組件實例還沒被創建
},
beforeRouteUpdate (to, from, next) {
// 2.2 新增
// 在當前路由改變,但是該組件被復用時調用
// 舉例來說,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 由於會渲染同樣的 Foo 組件,因此組件實例會被復用。而這個鈎子就會在這個情況下被調用。
// 可以訪問組件實例 `this`
},
beforeRouteLeave (to, from, next) {
// 導航離開該組件的對應路由時調用
// 可以訪問組件實例 `this`
}
}
- beforeRouteEnter 可以通過傳一個回調給 next來訪問組件實例。在導航被確認的時候執行回調,並且把組件實例作為回調方法的參數。
- 注釋:當前路由改變,但是該組件被復用時不會觸發
beforeRouteEnter (to, from, next) {
next(vm => {
// 通過 `vm` 訪問組件實例
})
}
完整的導航解析流程
- 導航被觸發。
- 在失活的組件里調用離開守衛。beforeRouteLeave
- 調用全局的 beforeEach(全局前置守衛) 守衛。
- 在重用的組件里調用 beforeRouteUpdate 守衛 (2.2+)。
- 在路由配置里調用 beforeEnter。
- 解析異步路由組件。
- 在被激活的組件里調用 beforeRouteEnter。
- 調用全局的 beforeResolve 守衛 (2.5+)。
- 導航被確認。
- 調用全局的 afterEach 鈎子。
- 觸發 DOM 更新。
- 用創建好的實例調用 beforeRouteEnter 守衛中傳給 next 的回調函數。在 mounted 之后
路由元信息
- routes 配置中的每個路由對象為 路由記錄
- 當一個路由匹配成功后,他可能匹配多個路由記錄
- 注釋:所有級別路由都可以配置元信息 meta,不管是根級路由還是末級路由
- 一個路由匹配到的所有路由記錄會暴露為 $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()
}
})
過渡動效
- <router-view> 是基本的動態組件,所以我們可以用 <transition> 組件給它添加一些過渡效果
<transition>
<router-view></router-view>
</transition>
單個路由的過渡
- 如果你想讓每個路由組件有各自的過渡效果,可以在各路由組件內使用 <transition> 並設置不同的 name。
- 注釋:適合同一個視圖中不同組件擁有自定義的過渡
const Foo = {
template: `
<transition name="slide">
<div class="foo">...</div>
</transition>
`
}
const Bar = {
template: `
<transition name="fade">
<div class="bar">...</div>
</transition>
`
}
基於路由的動態過渡
<!-- 使用動態的 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'
}
}
數據獲取
導航完成后獲取數據
在導航完成前獲取數據
- 在為后面的視圖獲取數據時,用戶會停留在當前的界面,因此建議在數據獲取期間,顯示一些進度條或者別的指示。
滾動行為
- 這個功能只在支持 history.pushState 的瀏覽器中可用
- 第三個參數 savedPosition 當且僅當 popstate 導航 (通過瀏覽器的 前進/后退 按鈕觸發) 時才可用。
- 這個方法返回滾動位置的對象信息,長這樣:
- 可以利用路由元信息更細顆粒度地控制滾動。
- 注釋:scrollBehavior只有在路由切換時才觸發,通過url進入頁面不會觸發
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// savedPosition
return savedPosition
// 滾動到錨點
return {
selector: to.hash
}
// 偏離
return {
offset: { y: 100 }
}
}
})
異步滾動
- 2.8.0 新增
- 可以返回一個 Promise 來得出預期的位置描述
- 注釋:可以等待動畫結束之后才觸發滾動
- 注釋:例子中返回的 Promise 中采用 $once 在根組件上綁定事件,而不是使用 $on ,是因為每次 scrollBehavior 執行后都返回一個新的 Promise,根組件觸發 triggerScroll 事件后需要 resolve 這個新的 Promise
const scrollBehavior = function (to, from, savedPosition) {
return new Promise(resolve => {
const position = {...}
// wait for the out transition to complete (if necessary)
this.app.$root.$once('triggerScroll', () => {
// if the resolved position is falsy or an empty object,
// will retain current scroll position.
resolve(position)
})
})
}
}
const router = new VueRouter({
mode: 'history',
base: __dirname,
scrollBehavior,
routes: [
...
]
})
new Vue({
router,
template: `
<div id="app">
<ul>
<li><router-link to="/">/</router-link></li>
<li><router-link to="/foo">/foo</router-link></li>
</ul>
<transition name="fade" mode="out-in" @after-leave="afterLeave">
<router-view class="view"></router-view>
</transition>
</div>
`,
methods: {
afterLeave () {
this.$root.$emit('triggerScroll')
}
}
}).$mount('#app')
路由懶加載
- 可以將異步組件定義為返回一個 Promise 的工廠函數 (該函數返回的 Promise 應該 resolve 組件本身)
const Foo = () => Promise.resolve({ /* 組件定義對象 */ })
- 使用動態 import 語法來定義代碼分塊點 (split point):
- 如果您使用的是 Babel,你將需要添加 syntax-dynamic-import 插件,才能使 Babel 可以正確地解析語法。
- 注釋:Babel 把import解析為返回 Promise 的函數,Webpack 使用該標志來進行代碼分塊,注釋語句也是有 Webpack 提供。
import('./Foo.vue') // 返回 Promise
- 結合這兩者,這就是如何定義一個能夠被 Webpack 自動代碼分割的異步組件
const Foo = () => import('./Foo.vue')
把組件按組分塊
- 把某個路由下的所有組件都打包在同個異步塊 (chunk) 中。只需要使用 命名 chunk,一個特殊的注釋語法來提供 chunk name (需要 Webpack > 2.4)
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
————————————————————————————————————————————————————————————————————————————————————
異步組件
- Vue 允許你以一個工廠函數的方式定義你的組件,這個工廠函數會異步解析你的組件定義。Vue 只有在這個組件需要被渲染的時候才會觸發該工廠函數,且會把結果緩存起來供未來重渲染。
- 這個工廠函數會收到一個 resolve 回調,這個回調函數會在你從服務器得到組件定義的時候被調用
- 可以調用 reject(reason) 來表示加載失敗。
- 如何獲取組件取決於你自己。一個推薦的做法是將異步組件和 webpack 的 code-splitting 功能一起配合使用:https://webpack.docschina.org/guides/code-splitting/
Vue.component('async-webpack-example', function (resolve, reject) {
// 這個特殊的 `require` 語法將會告訴 webpack
// 自動將你的構建代碼切割成多個包,這些包
// 會通過 Ajax 請求加載
require(['./my-async-component'], resolve)
// 向 `resolve` 回調傳遞組件定義
// 實際最后回調
resolve({
template: '<div>I am async!</div>'
})
})
- 也可以在工廠函數中返回一個 Promise,所以把 webpack 2 和 ES2015 語法加在一起,我們可以寫成這樣:
Vue.component(
'async-webpack-example',
// 這個 `import` 函數會返回一個 `Promise` 對象。
() => import('./my-async-component')
)
處理加載狀態
- 2.3.0+ 新增
- 這里的異步組件工廠函數也可以返回一個如下格式的對象:
- 如果你希望在 Vue Router 的路由組件中使用上述語法的話,你必須使用 Vue Router 2.4.0+ 版本。
const AsyncComponent = () => ({
// 需要加載的組件 (應該是一個 `Promise` 對象)
component: import('./MyComponent.vue'),
// 異步組件加載時使用的組件
loading: LoadingComponent,
// 加載失敗時使用的組件
error: ErrorComponent,
// 展示加載時組件的延時時間。默認值是 200 (毫秒)
delay: 200,
// 如果提供了超時時間且組件加載也超時了,
// 則使用加載失敗時使用的組件。默認值是:`Infinity`
timeout: 3000
})
————————————————————————————————————————————————————————————————————————————————————
- Turn an Express-style path string such as /user/:name into a regular expression.轉變Express-style路徑為正則表達式
Installation
npm install path-to-regexp --save
Usage
var pathToRegexp = require('path-to-regexp')
// pathToRegexp(path, keys, options)
// pathToRegexp.parse(path)
// pathToRegexp.compile(path)
- path: An Express-style string, an array of strings, or a regular expression.Express-style類字符串、字符串數組、正則表達式
- keys: An array to be populated with the keys found in the path.用來存放路徑中變量對象的數組容器,解析path后會往這個數組塞解析好的變量對象
- options:
- 注釋:vue-router中的 pathToRegexpOptions 對象,應該和這的options相同
- sensitive: When true the route will be case sensitive. (default: false) true時區分大小寫
- strict: When false the trailing slash is optional. (default: false) false時尾部斜杠在正則中為可有可無
- end: When false the path will match at the beginning. (default: true) false時正則將擁有,疑問:好像true才是擁有
- delimiter: Set the default delimiter for repeat parameters. (default: '/') 設置默認間隔符號
var keys = []
var re = pathToRegexp('/foo/:bar', keys)
// re = /^\/foo\/([^\/]+?)\/?$/i
// keys = [{
name: 'bar',
prefix: '/', // 前綴
delimiter: '/', // 分隔符
optional: false, // 是否可選
repeat: false, // 是否多個,用於匹配*,例如:/:foo*
pattern: '[^\\/]+?' // 正則字符串
partial Indicates this token is a partial path segment (boolean) // 是否為部分路徑?
asterisk Indicates the token is an * match (boolean) // 是否為*匹配
}]
- Please note: The RegExp returned by path-to-regexp is intended for use with pathnames or hostnames. It can not handle the query strings or fragments of a URL.返回的正則只能處理路徑或主機名,無法處理查詢字符串?和片段#。應該是生成的正則直接就舍去了這部分
Parameters 參數
Named Parameters 命名參數
var re = pathToRegexp('/:foo/:bar', keys)
// keys = [{ name: 'foo', prefix: '/', ... }, { name: 'bar', prefix: '/', ... }]
re.exec('/test/route')
//=> ['/test/route', 'test', 'route']
- Please note: Named parameters must be made up of "word characters" ([A-Za-z0-9_]). 命名參數需要滿足 [A-Za-z0-9_]
- 注釋:命名參數可以和常量、其他參數類型混搭
- 注釋:命名參數在路徑變量keys中name為命名值,而未命名的參數的name是數字
var re = pathToRegexp('/(apple-)?icon-:res(\\d+).png', keys)
// keys = [{ name: 0, prefix: '/', ... }, { name: 'res', prefix: '', ... }]
re.exec('/icon-76.png')
//=> ['/icon-76.png', undefined, '76']
Modified Parameters 變動參數
Optional 可選的?
Zero or more 0個或更多
var re = pathToRegexp('/:foo*', keys)
// keys = [{ name: 'foo', delimiter: '/', optional: true, repeat: true }]
re.exec('/')
//=> ['/', undefined]
re.exec('/bar/baz')
//=> ['/bar/baz', 'bar/baz']
One or more 1個或更多 +
var re = pathToRegexp('/:foo+', keys)
// keys = [{ name: 'foo', delimiter: '/', optional: false, repeat: true }]
re.exec('/')
//=> null
re.exec('/bar/baz')
//=> ['/bar/baz', 'bar/baz']
Custom Match Parameters 自定義匹配參數
- All parameters can be provided a custom regexp, which overrides the default ([^/]+). 參數可以提供一個正則進行校驗,替代默認的 [^/]+
Unnamed Parameters 未命名參數
- It is possible to write an unnamed parameter that only consists of a matching group. It works the same as a named parameter, except it will be numerically indexed.
- 注釋:可以只寫正則匹配規則
var re = pathToRegexp('/:foo/(.*)', keys)
// keys = [{ name: 'foo', ... }, { name: 0, ... }]
re.exec('/test/route')
//=> ['/test/route', 'test', 'route']
Asterisk
- An asterisk can be used for matching everything. It is equivalent to an unnamed matching group of (.*).
- 注釋:可以匹配所有內容,相當於一個 (.) 匹配組。這里的 * 代表含義不同,前者為通配符,后者為正則0個或n個
Parse 解析
var tokens = pathToRegexp.parse('/route/:foo/(.*)')
console.log(tokens[0])
//=> "/route"
console.log(tokens[1])
//=> { name: 'foo', prefix: '/', delimiter: '/', optional: false, repeat: false, pattern: '[^\\/]+?' }
console.log(tokens[2])
//=> { name: 0, prefix: '/', delimiter: '/', optional: false, repeat: false, pattern: '.*' }
Compile ("Reverse" Path-To-RegExp) 編譯
var toPath = pathToRegexp.compile('/user/:id')
toPath({ id: 123 }) //=> "/user/123"
toPath({ id: 'café' }) //=> "/user/caf%C3%A9"
toPath({ id: '/' }) //=> "/user/%2F"
toPath({ id: ':' }) //=> "/user/%3A"
toPath({ id: ':' }, { pretty: true }) //=> "/user/:"
var toPathRepeated = pathToRegexp.compile('/:segment+')
toPathRepeated({ segment: 'foo' }) //=> "/foo"
toPathRepeated({ segment: ['a', 'b', 'c'] }) //=> "/a/b/c"
var toPathRegexp = pathToRegexp.compile('/user/:id(\\d+)')
toPathRegexp({ id: 123 }) //=> "/user/123"
toPathRegexp({ id: '123' }) //=> "/user/123"
toPathRegexp({ id: 'abc' }) //=> Throws `TypeError`.
Working with Tokens
- pathToRegexp.tokensToRegExp(tokens, options) Transform an array of tokens into a matching regular expression. 將 tokens 轉化為正則表達式
- 注釋:tokens 來源於
var tokens = pathToRegexp.parse('/route/:foo/(.*)')
- pathToRegexp.tokensToFunction(tokens) Transform an array of tokens into a path generator function. 將 tokens 轉化為路徑生成函數
- 注釋:應該等價於
var toPath = pathToRegexp.compile('/user/:id')
Compatibility with Express <= 4.x 於Express兼容
TypeScript