雖然Vue.js未提供路由功能,可是官方推出了Vue Router(即vue-router庫),以插件的形式支持。它與Vue.js深度集成,可快速的建立單頁應用(Single Page Application,SPA)。vue
首先須要引入vue和vue-router兩個庫,若是像下面這樣全局引用(即存在全局變量Vue),那麼vue-router會自動調用Vue.use()方法註冊其自身;但若是以模塊的方式引用,那麼就得顯式地調用Vue.use()。vue-router
<script src="js/vue.js"></script> <script src="js/vue-router.js"></script>
而後添加兩個內置的組件,第一個是導航用的router-link組件,它默認會被渲染成<a>元素,若是要渲染成其它元素,那麼可使用它的tag屬性;第二個是路徑匹配時用於渲染視圖的router-view組件,它仍是一個函數式組件,以下所示。編程
<div id="container"> <router-link to="/main">首頁</router-link> <router-link to="/list">列表</router-link> <router-view></router-view> </div>
再聲明要渲染的組件,這些組件會與指定的路由映射,下面是兩個很是簡單的自定義組件。瀏覽器
const Main = { template: '<div>首頁</div>' };
const List = { template: '<div>列表</div>' };
接着定義路由配置,添加一組路徑以及對應的組件,以下所示。服務器
const routes = [ { path: '/main', component: Main }, { path: '/list', component: List } ];
最後建立路由器實例,並將以前的路由配置傳遞進來,在掛載根實例時,須要將它注入,從而讓整個應用都有路由功能,以下所示。異步
const router = new VueRouter({ routes: routes }); var vm = new Vue({ el: "#container", router: router });
注意,router實例包含三個導航方法:push()、replace()和go(),能夠用編程的方式進行導航。函數
頁面在渲染完成後,獲得的DOM結構以下所示,此時router-view組件所佔的位置無任何內容。this
<div id="container"> <a href="#/main" class="">首頁</a> <a href="#/list" class="">列表</a> </div>
當router-link組件所對應的路由匹配成功時,其渲染出的元素會被自動添加一個CSS類:router-link-active。例如點擊首頁連接,獲得的結構以下所示,注意,此時router-view組件被替換成了Main組件中的內容。spa
<div id="container"> <a href="#/main" class="router-link-active">首頁</a> <a href="#/list" class="">列表</a> <div>首頁</div> </div>
Vue Router默認採用URL hash模式來保持頁面和URL的同步,其建立的URL格式須要包含井號(#),以下所示。插件
http://pwstrick.com/#/main
Vue Router還有另一種history模式,利用HTML5 History來保持頁面和URL的同步,其建立的URL格式在視覺上更爲簡潔清晰,以下所示。
http://pwstrick.com/main
若是要開啓history模式,那麼須要在路由配置時添加mode選項,並賦予history關鍵字,以下所示(直接修改了上一節中的示例)。
const router = new VueRouter({ mode: "history", routes: routes });
注意,當直接訪問history模式建立的URL時,會返回一個404頁面。爲了不這種狀況的發生,建議在服務器上配置一個默認的候選頁面。
本節不只會介紹動態路由的概念,還會分析路由的命名和嵌套等操做。
1)動態路由
Vue Router採用的路徑匹配引擎是path-to-regexp,它支持動態路由的匹配,例若有一個List組件,須要根據路徑中的頁碼參數把組件渲染成不一樣的內容,能夠像下面這樣配置。
const List = { template: '<div>{{$route.params.page}}</div>' }; const routes = [ { path: '/list/:page', component: List } ];
在路由配置中,以冒號開頭的:page是路徑參數,它的值會被保存到路由對象$route的params屬性中。路由對象不只包含解析URL獲得的信息(例如路徑、查詢字符串、錨點等),還有匹配到的路由信息(例如路由名稱、路由記錄等)。當路徑是/list/1時,$route.params的值爲{page: 1},params是一個對象,其鍵值與路徑參數對應。注意,一條路徑能夠包含多個不一樣的路徑參數。
當一個路徑能匹配多個路由時,匹配優先級會按照路由的定義順序來排,即先定義的,優先級高。下面的配置包含兩個路由,當路徑是/list/1時,只會渲染List組件。
const routes = [ { path: '/list/:page', component: List }, { path: '/list/1', component: Main } ];
2)命名路由
能夠在配置時爲路由標識一個名稱,從而就能在使用時省略路徑了,以下所示,name屬性的值就是路由名稱。
const routes = [ { path: '/list/:page', component: List, name: "list" } ];
若是要導航到一個命名路由,那麼須要動態地綁定to屬性,併爲其傳入路由的名稱以及可選的URL參數,以下所示。
<router-link :to="{ name: 'list', params: {page: 3} }">第三頁</router-link>
3)嵌套路由
Vue Router容許嵌套路由,可經過多層嵌套的組件實現。下面是一個使用示例,首先在一個組件中聲明router-view視圖組件,以下所示。
const List = { template: '<router-view></router-view>' };
const Detail = { template: '<div>詳情</div>' };
而後在路由配置中,添加children選項,並定義子路由。
const routes = [ { path: "/list", component: List, children: [{ path: "detail", component: Detail }] } ];
在通過這一系列操做後,當路徑是/list/detail時,Detail組件就會被渲染到List組件的視圖中。
嵌套路由可用來渲染多個不一樣層級的視圖,而利用命名視圖可渲染多個同級的視圖。在下面的代碼中,爲第二個router-view組件添加了name屬性,賦予它一個名稱,而第一個router-view組件的默認名稱爲default。
<router-view></router-view> <router-view name="detail"></router-view>
在路由配置時,新增用於記錄各個命名組件的components選項,以下所示。
const routes = [ { path: "/named", components: { default: Main, detail: Detail } } ];
當路徑是/named時,Main和Detail兩個組件會被同時渲染。
1)重定向
在路由配置中,經過redirect選項可重定向到一個新的頁面,該選項可取的值有三種,分別是路徑、命名路由和回調函數,以下所示,其中函數中的to參數表示目標路由對象。
const routes = [ { path: "/list/1", redirect: "/main" }, { path: "/list/1", redirect: {name: "main"} }, { path: "/list/1", redirect: to => "/main" } ];
當訪問的路徑是/list/1時,瀏覽器的地址欄會變成/main,匹配的將是路徑爲/main的路由。
2)別名
在路由配置中,經過alias選項可定義路由的別名,以下所示。
const routes = [ { path: "/list/4", component: Detail, alias: "/detail" } ];
當訪問的路徑是/main時,瀏覽器的地址欄保持爲/main,但匹配的將是路徑爲/list/4的路由。
除了在組件中經過$route.params獲取路由參數以外,還能將路由參數做爲組件的props傳入,從而將組件和路由解耦,以下所示。
const List = { props: ["page"], template: "<div>{{page}}</div>" }; const routes = [ { path: "/list/:page", component: List, props: true } ];
須要注意的是,在路由配置時要添加props選項,並賦予true後,纔會讓路由參數和props關聯。
導航守衛就是路由發生變化時的鉤子函數,Vue Router提供了三類守衛:全局、路由獨享和組件級。
1)全局守衛
在路由器實例上可註冊三個全局守衛,分別是beforeEach()、beforeResolve()和afterEach()。
beforeEach()是一個全局前置守衛,包含三個參數(以下所示),其中to是目標路由對象、from是來源路由對象,next是一個回調函數,用於解析(resolve)當前守衛。
router.beforeEach((to, from, next) => {
next();
});
因爲導航在全部守衛解析以前會處於等待狀態(即不會改變路徑,也不會渲染對應的組件),所以在回調中不能省略next()函數。注意,該函數不只能處理導航,還能將其中斷或重定向,甚至註冊一個錯誤。
beforeResolve()是一個全局解析守衛,其參數和beforeEach()的相同,在導航被確認前,而且組件內的守衛和異步路由組件被解析以後調用。afterEach()是一個全局後置守衛,它只有to和from兩個參數。
2)路由守衛
在路由配置中可直接定義beforeEnter()守衛,做用於某個單獨的路由而非全局,其參數和beforeEach()的相同,以下所示。
const routes = [ { path: "/list", component: List, beforeEnter: (to, from, next) => { next(); } } ];
3)組件守衛
組件內的導航守衛包含三個,以下所列,它們的參數都和beforeEach()的相同,也是to、from和next。
(1)beforeRouteEnter():因爲該組件在導航確認前被調用,所以組件還未被建立,從而訪問this沒法獲得組件實例。但next()函數能接收一個參數爲組件實例的回調,以下所示。
const List = { beforeRouteEnter: function(to, from, next) { next(vm => { //vm是組件實例 }); } };
(2)beforeRouteUpdate():可經過this獲得組件實例,當路由發生變化,但複用了一個組件時,會調用該守衛。例如一個路徑爲/list/:page的路由(以下配置所示),在將/list/1導航至list/2時,因爲渲染的都是List組件,所以會調用beforeRouteUpdate()守衛。
const routes = [ { path: "/list/:page", component: List } ];
(3)beforeRouteLeave():也可經過this獲得組件實例,當路由改變,而且渲染的組件不一樣時,會調用該守衛。例若有兩條路徑/main和/list,分別對應Main和List組件,在List組件中聲明瞭beforeRouteLeave()守衛(以下所示),當從/list導航至/main時,會彈出確認框,判斷是否須要取消本次導航。
const List = { beforeRouteLeave: function(to, from, next) { const answer = window.confirm("是否離開當前頁?"); answer ? next() : next(false); } };
4)解析流程
完整的導航解析流程以下所列。
(1)導航被觸發。
(2)在失活的組件裏調用beforeRouteLeave()守衛。
(3)調用全局的beforeEach()守衛。
(4)在重用的組件裏調用beforeRouteUpdate()守衛。
(5)在路由配置裏調用beforeEnter()守衛。
(6)解析異步路由組件。
(7)在被激活的組件裏調用beforeRouteEnter()守衛。
(8)調用全局的beforeResolve()守衛。
(9)導航被確認。
(10)調用全局的afterEach()守衛。
(11)觸發DOM更新。
(12)用建立好的實例調用beforeRouteEnter()守衛中傳給next參數的回調函數。