Vue躬行記(8)——Vue Router

  雖然Vue.js未提供路由功能,可是官方推出了Vue Router(即vue-router庫),以插件的形式支持。它與Vue.js深度集成,可快速的建立單頁應用(Single Page Application,SPA)。vue

1、基本用法

  首先須要引入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>

2、路由模式

  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頁面。爲了不這種狀況的發生,建議在服務器上配置一個默認的候選頁面。

3、路由

  本節不只會介紹動態路由的概念,還會分析路由的命名和嵌套等操做。

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兩個組件會被同時渲染。

4、重定向和別名

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的路由。

5、組件傳參

  除了在組件中經過$route.params獲取路由參數以外,還能將路由參數做爲組件的props傳入,從而將組件和路由解耦,以下所示。

const List = {
  props: ["page"],
  template: "<div>{{page}}</div>"
};
const routes = [
  { path: "/list/:page", component: List, props: true }
];

  須要注意的是,在路由配置時要添加props選項,並賦予true後,纔會讓路由參數和props關聯。

6、導航守衛

  導航守衛就是路由發生變化時的鉤子函數,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參數的回調函數。

相關文章
相關標籤/搜索