可能比文件還詳細--VueRouter完全指北

shotCat發表於2018-08-27

前言

關於標題,應該算不上是標題黨,因為內容真的很多很長很全面.主要是在官網的基礎上又詳細總結,舉例了很多東西.確保所有新人都能理解!所以實際上很多東西是比官網還詳細的.你想要的,在官網上沒理解的,基本在這裡都能找到解答!本來想分成兩篇發的,但想想男人長點也沒什麼不好的.所以也希望各位收藏插眼標記(滑稽)

特點:本文主要是參考了官方文件.除了不常用的過渡動效和資料獲取,都進行了分析說明.說明:每一節都在文件的基礎上進行了更通俗的解釋;例子:每一節都新增了單獨的例子進行詳細的說明,官方沒有或複雜或略過的都有詳細的說明.總結:每一節都會有tips注意點,實際開發的經驗和總結.

使用方法:

如果是新手,就從頭開始看,前5章的內容都是逐步讓你熟悉VueRouter,並且第5章也給出了本地實際搭建的程式碼示例.

如果你對VueRouter有了一定了解,則可以根據目錄,自行選擇檢視.或者全域性搜尋關鍵字快速定位.

吐槽:現在關於VueRouter相關能搜到的大部分都是copy完全摘抄官方文件拼湊的文章.很反感這樣的文章!基本沒什麼營養!你在官網上不明白的在他那裡基本也看不明白.

1, 概述

vue-router和vue.js是深度整合的,適合用於單頁面應用.傳統的路由是用一些超連結來實現頁面切換和跳轉.而vue-router在單頁面應用中,則是元件之間的切換.其本質就是:建立並管理url和對應元件之間的對映關係.

2, 簡單例項

這裡簡單先以官網的例子,瞭解vue-router有哪幾大部分組成,對vue-router有一個初步印象.

HTML

首先是html部分.這裡主要是兩個作用:

1,router-link元件來導航,使用者點選後切換到相關檢視.

2,router-view元件來設定切換的檢視在哪裡渲染.(一個頁面也可以有多個router-view分別展示特定的檢視,並且支援巢狀)

<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>

複製程式碼

JavaScript

這裡實際專案寫法程式碼可以參考我後面的第5章.下面我就簡單總結下大概分那幾步:

  • 1,材料準備:
    • 1.1 引入準備好的'Vue'和'vue-router'(前提是已經npm)
    • 1.2 引入路由跳轉的元件.下面例子中就是'Foo'和'Bar'
    • 1.3 啟動全域性元件VueRouter例如:Vue.use(VueRouter).這樣vue-router才開始執行.
  • 2, 配置路由例項:通過new VueRouter()詳細配置每個路由的路徑,對應的元件等等所有和路由相關的配置.
  • 3, 將路由例項掛載到根例項上.new Vue({router}).$mount('#app')
// 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訪問當前路由物件:

Tips:這裡我簡單說明下$router$route的區別:

  • $router是指整個路由例項,你可以操控整個路由,通過'$router.push'往其中新增任意的路由物件.
  • $route:是指當前路由例項('$router')跳轉到的路由物件;
  • 路由例項可以包含多個路由物件.它們是父子包含關係.
// 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('/')
    }
  }
}
複製程式碼

3, vue-router簡略圖示關係(有問題需要修改)

圖片中router.js就是路由例項配置檔案

vue-router簡略圖示關係
PS: 這裡圖說的有點不準確,to並不是僅僅對應path值.要分具體情況,詳情檢視官方to的api.

4, 學習的準備工作 !important

4.1 硬體準備

學習一個新知識,只看然後想想.嗯,大概是這樣的.這樣是沒有任何效果的.沒有實際敲過,使用過,都算不上是你的知識.所以,強烈建議大家邊看邊敲邊思考總結. 這裡提供兩種方式:

  • 官網線上例項:點選這裡通過線上例項,按照教程邊看邊敲!
  • 通過我第5章的教程,在自己本地的實際專案上進行構建,檢視效果.

這兩種方式都很好.個人建議有條件的選第二種,更加直觀,更加貼近自己的實際專案需求.

4.2 思想準備

學習配置vue-router,一定要考慮兩個層面:

  • Html:對應的就是vue-router裡的<router-link><router-view>
  • JavaScript:就是具體路由例項的配置. 整個vue-router的配置也是圍繞這兩個層面進行展開的,學習時一定要有這個意識,才更容易理解和使用!

5, 實際專案構建示例

5.1,下載vue-router :

	npm i vue-router -s
複製程式碼

5.2, 在vue元件內配置router-link和router-view

  • router-link:對映路由.就是建立a標籤來定義路由導航的連結(使用者通過點選實現跳轉).通過to屬性指定目標地址.預設渲染成帶有正確連結的<a>標籤.
  • router-view:就是在標籤內渲染你路由匹配到的檢視元件.router-view支援巢狀router-view,並且支援多個router-view分別渲染不同的component.詳細點選文件命名檢視.
<!--這裡引用官方例子的寫法-->
<div id='app'>
	<p>
		<router-link to="/user/foo">/user/foo</router-link>
		<router-link to="/user/bar">/user/bar</router-link>
	</p>
	<router-view>PS:寫在這裡,即router-view裡的內容是不會顯示在頁面上的!</router-view>
</div>
複製程式碼

關於to的更詳細用法: 可以參考官方的api文件.用法很簡單這裡我就不重複了.

5.3, 配置路由例項 router.js檔案

//第一步:引入必要的檔案
import Vue from 'vue';//載入全域性元件時,都需要引入Vue
import Router from 'vue-router';//引入vue-router
//引入在路由中需要用到的元件
import User from '@/components/user/User' //這裡省略了.vue
...

//第二步:載入Router
Vue.use(Router);//載入全域性元件Router

//第三步:配置路由例項
export default new Router({
	//mode:'history', //路由模式:預設為hash,如果改為history,則需要後端進行配合
	//base:'/',//基路徑:預設值為'/'.如果整個單頁應用在/app/下,base就應該設為'/app/'.一般可以寫成__dirname,在webpack中配置.
	routes:[{
		path: '/user', 
		name: 'user', //給路由命名,設定的name要唯一!
		component: User//就是第一步import的元件
		},{
			//路由懶載入:單頁面應用,首頁時,載入內容時間過長.運用懶載入對頁面元件進行劃分,減少首頁載入時間
			path:'/Page',
			name:'Page',
			component:resolve => require(['@/components/Page'],resolve)
			//此時component則不需要在第一步import
		}]
})
複製程式碼

5.4, 在main.js中引入router.js並掛載到Vue例項

建議在實際專案中將router配置單獨的js檔案,更加清晰,不要都混在main.js中.

import router from './router'

new Vue({
  el: '#app',
  router,//不簡寫就是router:router,
  store,
  template: '<App/>',
  components: {
  App

  } })
複製程式碼

5.5, router,routes,route傻傻分不清?

  • 1, router:一般指的就是路由例項.如$router.
  • 2, routes:指router路由例項的routes API.用來配置多個route路由物件.
  • 3, route:指的就是路由物件.例如;$route指的就是當前路由物件.

5.6, 小結

至此,一個基本的vue-router已經完成.這裡只是簡單示範,瞭解其實現步驟. 總結一下編寫vue路由時,記住要做這三類事: 1,準備工作: 在main.js中引入router.js掛載到Vue例項中. 2,配置路由例項(重點): 在router.js中引入Vue,vue-router,配置路由例項. 3,元件內配置: 就是配置router-link和router-view.

6, vue-router的兩種模式

一般單頁面應用是(SPA)不會請求頁面而是隻更新檢視. vue-router提供了兩種方式來實現前端路由:Hash模式和History模式,可以用mode引數來決定使用哪一種方式.

6.1,Hash模式

vue-router預設使用Hash模式.使用url的hash來模擬一個完整的url.此時url變化時,瀏覽器是不會重新載入的.Hash(即#)是url的錨點,代表的是網頁中的一個位置,僅僅改變#後面部分,瀏覽器只會滾動對應的位置,而不會重新載入頁面.#僅僅只是對瀏覽器進行指導,而對服務端是完全沒有作用的!它不會被包括在http請求中,故也不會重新載入頁面.同時hash發生變化時,url都會被瀏覽器記錄下來,這樣你就可以使用瀏覽器的後退了.

總而言之:Hash模式就是通過改變#後面的值,實現瀏覽器渲染指定的元件.

6.2,History模式

如果你不喜歡hash這種#樣式.可以使用history模式.這種模式利用了HTML5 History新增的pushState()和replaceState()方法. 除了之前的back,forward,go方法,這兩個新方法可以應用在瀏覽器歷史記錄的增加替換功能上.使用History模式,通過歷史記錄修改url,但它不會立即向後端傳送請求. 注意點: 雖然History模式可以丟掉不美觀的#,也可以正常的前進後退,但是重新整理f5後,此時瀏覽器就會訪問伺服器,在沒有後臺支援的情況下,此時就會得到一個404!官方文件給出的描述是:"不過這種模式要玩好,還需要後臺配置支援.因為我們的應用是單個客戶端應用,如果後臺沒有正確的配置,當使用者直接訪問時,就會返回404.所以呢,你要在服務端增加一個覆蓋所有情況的的候選資源;如果url匹配不到任何靜態資源,則應該返回同一個index.html頁面."

總而言之:History模式就是通過pushState()方法來對瀏覽器的瀏覽記錄進行修改,來達到不用請求後端來渲染的效果.不過建議,實際專案還是使用history模式.

例子:

const router = new VueRouter({
  mode: 'history', //如果這裡不寫,路由預設為hash模式
  routes: [...]
})
複製程式碼

7,動態路由匹配

當我們經常需要把某種模式匹配到所有的路由,全部都對映到同個元件.例如:我們有一個User元件,對於所有ID各不相同的使用者,都要使用這個元件來渲染.這時我們就可以配置動態路由來實現. 動態路由匹配本質上就是通過url進行傳參

7.1,路由物件屬性介紹:

為了下面理解的方便這裡簡單介紹下常用的路由物件屬性,在元件內可以通過this.$route(不是$router!)進行訪問.

$route.path 型別: string 字串,對應當前路由的路徑,總是解析為絕對路徑,如 "/foo/bar"

$route.params 型別: Object 一個 key/value 物件,包含了動態片段和全匹配片段,如果沒有路由引數,就是一個空物件。

$route.query 型別: Object 一個 key/value 物件,表示 URL 查詢引數。例如,對於路徑 /foo?user=1,則有 $route.query.user == 1,如果沒有查詢引數,則是個空物件。

$route .name 當前路由的名稱,如果有的話。這裡建議最好給每個路由物件命名,方便以後程式設計式導航.不過記住name必須唯一!

$route.hash 型別: string 當前路由的 hash 值 (帶 #) ,如果沒有 hash 值,則為空字串。

$route.fullPath 型別: string 完成解析後的 URL,包含查詢引數和 hash 的完整路徑。

$route.matched 型別: Array<RouteRecord> 一個陣列,包含當前路由的所有巢狀路徑片段的路由記錄 。路由記錄就是 routes 配置陣列中的物件副本 (還有在 children 陣列)。 $route.redirectedFrom 如果存在重定向,即為重定向來源的路由的名字。

7.2 使用params進行配置:

舉個例子:

routes:[{
	//動態路徑引數,以冒號開頭
	path:'/user/:id',
	component:User
}]
複製程式碼

這樣,就是使用params進行配置.像/user/foo和/user/bar都將對映到相同的路由.

  • 一個路徑引數使用':'冒號進行標記.
  • 當匹配到一個路由時,引數就會被設定到this.$route.params,可以在每個元件內使用.例如/user/foo在this.$route.params.id就為foo

這裡以官方的表格示例進行展示

模式 匹配路徑 $route.params
/user/:username /user/evan { username: 'evan' }
/user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: 123 }

這裡再舉一個稍微變化一下的例子,來加深理解:

routes:[
	{path:'/user/:shot/foo/:id', component:shotCat}
]
複製程式碼
<p>
	<router-link to="/user/shot/foo">/user/shot/foo</router-link>  	<!--無法匹配到對應路由-->	
	<router-link to="/user/shot/cat/foo">/user/shot/cat/foo</router-link>	<!--無法匹配到對應路由-->	
	<router-link to="/user/foo/foo/foo">/user/foo/foo/foo</router-link>	<!--成功匹配,$route.params.shot為foo;$route.params.cat為foo;-->	
	<router-link to="/user/shot/foo/cat">/user/shot/foo/cat</router-link><!--成功匹配,$route.params.shot為shot;$route.params.cat為cat;-->	
</p>
<router-view></router-view>
複製程式碼

tips:

  • 有時候,同一個路徑可以匹配多個路由,此時,匹配的優先順序就按照路由的定義順序.誰先定義的,誰的優先順序就最高.
  • 由於路由引數對元件例項是複用的.例如:/user/foo 和 /user/bar在使用路由引數時,複用的都是User元件.此時元件的生命週期鉤子不會再被呼叫。如果你想路徑切換時,進行一些初始化操作時,可以用以下兩種解決辦法:
    • 在元件內 watch $route 物件:
    const User = {
     template: '...',
     watch: {
       '$route' (to, from) {
         // 對路由變化作出響應...
       }
     }
    }
    複製程式碼
    • 使用2.2版本中的 beforeRouteUpdate 路由守衛:
    const User = {
      template: '...',
      beforeRouteUpdate (to, from, next) {
    	// react to route changes...
    	// don't forget to call next()
      }
    }
    複製程式碼

7.3,通過query進行配置傳參.

在專案裡我們可以通過上面提到的params進行傳參.同時也可以用query進行傳參. 舉個例子: <router-link to="/user?id=foo">foo</router-link> vue-route會自動將?後的id=foo封裝進this.$route.query裡. 此時,在元件裡this.$route.query.id值為'foo'. ==除了通過router-linkto屬性. query也可以通過後面講到的程式設計式導航進行傳參==

8, 程式設計式導航

什麼是程式設計式導航,程式設計式導航就是在vue元件內部通過this.$router訪問路由例項,並通過this.$router.push導航到不同的url,進行路由對映,所以 它的作用是和<router-link :to>是一毛一樣的! 當然,前提是你已經在routes裡配置了對應的路由物件.

一般什麼時候用到程式設計式導航? 如果,你想在路由跳轉前做點其他事情,例如許可權驗證等.但是用<router-link>的話,就直接跳轉了.此時就可以使用程式設計式導航!

8.1, 程式設計式導航的寫法

程式設計式導航一般都是用到router.push方法.該方法的引數可以是一個字串路徑,或者一個描述地址的物件.例如:

//字串
this.$router.push('home')

//物件
this.$ruter.push({path:'home'})

//命名路由
this.$router.push({name:'user',params:{userId:2333}})

//帶查詢引數,變成/register?plan=private
this.$router.push({path:'register',query:{plan:'private'}})

複製程式碼

注意:==原諒色警告==:pathparams是不能同時生效的!,否則params會被忽略掉.所以使用物件寫法進行params傳參時,要麼就是path加冒號:,要麼就是像上例中的'命名路由'.通過name和params進行傳參.然而query卻並不受影響,有沒有path都可以進行傳參.

8.2, router.replace方法

router.replace和router.push很像,寫法一樣.但實際效果不一樣.push是向history裡新增新記錄.而replace是直接將當前瀏覽器history記錄替換掉!

那最直接的後果是什麼呢? 舉個例子:

  • 用push方法,頁面1跳轉到頁面2,你使用瀏覽器的後退可以回到頁面1
  • 用replace方法,頁面1被替換成頁面2,你使用瀏覽器的後退,此時你回不到頁面1,只能回到頁面1的前一頁,頁面0.

那什麼時候會用到replace呢? 當你不想讓使用者回退到之前的頁面時,常見於許可權驗證,驗證後就不讓使用者回退到登入頁重複驗證.

8.3, router.go(n)方法

這個方法的引數就是一個整數,意思是在history記錄中前進或後退多少步.類似window.history.go(n).這樣就能控制頁面前進或者後退多少步.

9 對動態路由和程式設計式導航相關的補充小結:

補充: 實際上不通過routes配置,也可以用下面這種方法直接在router-link上通過to進行傳參. 關於to的更詳細用法: 可以參考官方的api文件.用法很簡單這裡我就不重複了.

routes:[
	{name:'shotCat',path:'/shotCat', component:shotCat}
]
複製程式碼
<router-link :to="{ name:'shotCat',params:{paramId:'hello'},query:{queryId:'world'}}">helloWorld</router-link> <!--此時通過name匹配到路由物件shotCat.-->	 
<router-link :to="{ path:'/shotCat',params:{paramId:'hello'},query:{queryId:'world'}}">helloWorld</router-link>  <!--此時通過path匹配到路由物件shotCat.但是!!!!!此時`paramId`並不能新增到`$route.params`裡,只有`queryId`成功新增到`$route.query`-->
複製程式碼

通過兩個router-link.可以發現這種寫法和程式設計式導航的規則一毛一樣, pathparams是不能同時生效的! 所以建議大家最好給每個路由物件進行命名!** query是path和name都可以正常傳參的. 這裡大家可以通過這個官方線上例子進行修改驗證{{$route.params}}{{$route.query}}是否成功傳遞. 小結:

  • 1,<router-link :to="{ }">等同於this.$router.psh(). pathparams是不能同時存在的!,想通過params,就得加上name屬性.query不受影響.
  • 2,<router-link :to="{ }">this.$router.psh()的實際效果也是一樣的.
    • 2.1 params引數都不會顯示在url位址列中.除了在路由中通過routes進行配置的.所以使用者重新整理頁面後,params引數就會丟失!
    • 2.2 query引數可以正常顯示在url位址列中.重新整理頁面後也不會丟失

通過to雖然可以進行params,query傳參.但是注意此時頁面url並不會改變!.所以你重新整理頁面後,引數就沒有了.

10 巢狀路由與單元件多檢視

  • 巢狀路由:就是父路由巢狀子路由.url上就是/user巢狀兩個子路由後就是/user/foo和/uer/bar.用一張圖表示就是:

巢狀路由

  • 單元件多檢視:就是一個元件裡有多個檢視進行展示.即包含有多個<router-view/>

10.1 巢狀路由

講之前,必須先清楚這樣一件事,一個<router-view/>對應展示的就是一個元件 因此實現巢狀路由有兩個要點:

  • 路由物件中定義子路由(巢狀子路由)
  • 元件內<router-view/>的使用.

這兩點也對應了我第4章中關於思想準備的說明.

下面我的示例還是以上圖巢狀路由為例進行講解. 理解了的可以看官方示例是巢狀路由搭配動態路由使用的例子,大家可以在此基礎上自己修改嘗試.

10.1.1 路由物件中定義子路由

const router = new VueRouter({
  routes: [
    { path: '/user', component: User,name:'user',
	//巢狀路由就寫在children配置中,寫法和rutes一樣.
      children: [
        { path: '', component: UserDefault ,name:'default',
			//children:[{}]   也可以繼續新增children巢狀
		},
		//如果/user下沒有匹配到其他子路由時,User的<router-view>是什麼都不會顯示的,如果你想讓它顯示點什麼.可以將path:''.設為空.此時UserDefault就是預設顯示的元件.
		
        { path: 'foo', component: UserFoo,name:'foo'},
		//此時path等同於'/user/foo',子路由會繼承父路由的路徑.但是不能寫成path:'/foo'.因為以 / 開頭的巢狀路徑會被當作根路徑,也就是說此時foo成了根路徑.而不是user.
		
        { path: 'bar', component: UserBar,name:'bar' }
      ]
    }
  ]
})
複製程式碼

10.1.2 元件內<router-view/>的使用.

<div id="app">
 <p>
   <router-link to="/user">/user</router-link>
   <router-link to="/user/foo">/user/foo</router-link>
   <router-link to="/user/bar">/user/bar</router-link>
 </p>
 <router-view></router-view> <!--這裡展示的是User元件;同樣User的<router-view/>也被巢狀在裡面-->
</div>
複製程式碼
const User = {
 template: `
   <div class="user">
     <h2>User</h2>
     <router-view></router-view> 
   </div>
 `
}
//User的<router-view>裡展示的就是子路由foo,bar的元件還有default預設元件

const UserDefault = { template: '<div>default</div>' }
const UserFoo = { template: '<div>foo</div>' }
const UserBar = { template: '<div>bar</div>' }
複製程式碼

10.2 單元件多檢視

如果一個元件有多個檢視,來展示多個子元件.這個時候就需要用到命名檢視 官方的線上示例在這裡,大家可以在此基礎上自己修改嘗試. 直接上我的例子:

<div id="app">
 <h1>Named Views</h1>
 <p>
   <router-link to="/avenger">復仇者聯盟</router-link>
 </p>
 <router-view ></router-view>
 <router-view name="ironMan"></router-view>
 <router-view name="captainAmerica"></router-view>
</div>
<!--這裡我們給其中兩個檢視命名為ironMan和captainAmerica;沒有設定name的檢視,會獲得預設命名為default>
複製程式碼
const router = new VueRouter({
  routes: [
    { path: '/avenger', name:'avenger',components: {
        default: stanLee,
        ironMan: ironMan,
        captainAmerica: captainAmerica
      }
	//如果有多個檢視需要展示時,以前的component換成components(加上s!!),寫成物件形式.左邊的ironMan指的就是<router-view>裡設定的name="ironMan";右邊的則指的是下面的元件ironMan.
    }
  ]
})

const stanLee = { template: '<div>斯坦李</div>' }
const ironMan = { template: '<div>鋼鐵俠</div>' }
const captainAmerica = { template: '<div>美國隊長</div>' }
複製程式碼

巢狀命名檢視: 官方的線上示例,結合了巢狀路由和命名檢視.就是兩種寫法的組合.建議初學者通過這個例子加深理解.

11 重定向和別名:

11.1 重定向配置:

重定向其實就是通過路由.攔截path,然後替換url跳轉到redirect所指定的路由上. 重定向是通過 routes 配置來完成,

//從 /a 重定向到 /b
const router = new VueRouter({
	routes:[
		{path:'/a',rediret:'/b'}
	]
})

///從 /a 重定向到 命名為'foo'的路由
const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})

//甚至是一個方法,動態返回重定向目標:
const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目標路由 作為引數
      // return 重定向的 字串路徑/路徑物件
	  const { hash, params, query } = to
	  //這裡使用了ES6的解構寫法,分別對應了to的hash模式,params,query引數.這裡解構就不具體說明了.
        if (query.to === 'foo') {
          return { path: '/foo', query: null }
        }
        if (hash === '#baz') {
          return { name: 'baz', hash: '' }
        }
        if (params.id) {
          return '/with-params/:id'
        } else {
          return '/bar'
        }
    }}
  ]
})

複製程式碼

11.2 別名

重定向是替url換路徑,達到路由跳轉.那別名就是一個路由有兩個路徑.兩個路徑都能跳轉到該路由. 舉個例子:你可能大名叫'趙日天',但你的小名(別名)可能就叫'二狗子'.但'趙日天'和'二狗子'指代的是同一個人(路由). 別名是在rutes裡的alias進行配置:

const router = new VueRouter({
//這時,路徑'/fxxksky'和'/two-dogs' 都會跳轉到A
  routes: [
    { path: '/fxxksky', component: A, alias: '/two-dogs' }
	//當有多個別名時,alias也可以寫成陣列形式.  alias: ['/two-dogs', 'three-dogs','four-dogs','five-dogs'] 
  ]
})
複製程式碼

12 路由元件傳參

路由傳參,可以通過前面介紹的params和query進行傳參.但這兩種傳參方式,本質上都是把引數放在url上,通過改變url進行的.這樣就會造成引數和元件的高度耦合. 如果我想傳參的時候,可以更自由,擺脫url的束縛.這時就可以使用rute的props進行解耦.提高元件的複用,同時不改變url.

下面就以例子進行講解: PS: 這部分官方沒有線上例項,大家可以將我下面例子的程式碼將之前的線上例子進行覆蓋就可以了

//路由配置:

const Hello = {
  props: ['name'], //使用rute的props傳參的時候,對應的元件一定要新增props進行接收,否則根本拿不到傳參
  template: '<div>Hello {{ $route.params}}和{{this.name}}</div>'
  //如果this.name有值,那麼name已經成功成為元件的屬性,傳參成功
}

const router = new VueRouter({
mode: 'history',
  routes: [
    { path: '/', component: Hello }, // 沒有傳參  所以元件什麼都拿不到
    { path: '/hello/:name', component: Hello, props: true }, //布林模式: props 被設定為 true,此時route.params (即此處的name)將會被設定為元件屬性。
    { path: '/static', component: Hello, props: { name: 'world' }}, // 物件模式: 此時就和params沒什麼關係了.此時的name將直接傳給Hello元件.注意:此時的props需為靜態!
    { path: '/dynamic/:years', component: Hello, props: dynamicPropsFn }, // 函式模式: 1,這個函式可以預設接受一個引數即當前路由物件.2,這個函式返回的是一個物件.3,在這個函式裡你可以將靜態值與路由相關值進行處理.
    { path: '/attrs', component: Hello, props: { name: 'attrs' }}
  ]
})

function dynamicPropsFn (route) {
  return {
    name: (new Date().getFullYear() + parseInt(route.params.years)) + '!'
  }
}

new Vue({
  router,
  el: '#app'
})
複製程式碼
<!--html部分-->
    <div id="app">
      <h1>Route props</h1>
      <ul>
        <li><router-link to="/">/</router-link></li>
        <li><router-link to="/hello/you">/hello/you</router-link></li>
        <li><router-link to="/static">/static</router-link></li>
        <li><router-link to="/dynamic/1">/dynamic/1</router-link></li>
        <li><router-link to="/attrs">/attrs</router-link></li>
      </ul>
      <router-view></router-view>
    </div>
複製程式碼

13 路由懶載入

vue主要用於單頁面應用,此時webpack會打包大量檔案,這樣就會造成首頁需要載入資源過多,首屏時間過長,給使用者一種不太友好的體驗. 如果使用路由懶載入,僅在你路由跳轉的時候才載入相關頁面.這樣首頁載入的東西少了,首屏時間也減少了. vueRouter的懶載入主要是靠Vue 的非同步元件Webpack 的程式碼分割功能,輕鬆實現路由元件的懶載入。 這裡寫法其實我在第5.3章裡的例子已經寫過,比較簡單隻需要將元件以promise形式引入即可.

routes:[
      path:'/',
      name:'HelloWorld',
      component:resolve=>require(['@/component/HelloWorld'],resolve)
  ]
  //此時HelloWorld元件則不需要在第一步import進來
複製程式碼

13.1 把元件按組分塊

把元件按組分塊可以把路由下的所有元件都打包在同個非同步塊 (chunk) 中,並且在f12的network裡面看到動態載入的元件名字. 前提條件:

  • Webpack版本 > 2.4
  • 需要在webpack.base.conf.js裡面的output裡面的filename下面加上chunkFileName
output: {
 path: config.build.assetsRoot,
 filename: '[name].js',
 // 需要配置的地方
 chunkFilename: '[name].js',
 publicPath: process.env.NODE_ENV === 'production'
   ? config.build.assetsPublicPath
   : config.dev.assetsPublicPath
}
複製程式碼

此時在引入元件時的寫法需要使用 命名 chunk,一個特殊的註釋語法來提供 chunk name

const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
複製程式碼

14 導航守衛

路由導航守衛,通俗點說就是路由鉤子.作用也和生命週期鉤子類似,在路由跳轉過程進行操作控制. 導航守衛有很多鉤子,這裡我就不照搬官方文件了.這裡大家自己先點選閱讀完導航守衛,然後再看我的總結和程式碼例子.

14.1 導航守衛分類

  • 1,全域性守衛::非同步執行,每個路由跳轉都會按順序執行.
    • router.beforeEach 全域性前置守衛
    • router.beforeResolve 全域性解析守衛(2.5.0+) 在beforeRouteEnter呼叫之後呼叫.
    • router.afterEach 全域性後置鉤子 進入路由之後 注意:不支援next(),只能寫成這種形式router.afterEach((to, from) => {});

每個守衛方法接收三個引數:

  • to: Route: 即將要進入的目標 路由物件

  • from: Route: 當前導航正要離開的路由物件

  • next: Function: 一定要呼叫該方法來 resolve 這個鉤子。執行效果依賴 next 方法的呼叫引數。

    • next(): 進行管道中的下一個鉤子。如果全部鉤子執行完了,則導航的狀態就是 confirmed (確認的)。

    • next(false): 中斷當前的導航。如果瀏覽器的 URL 改變了 (可能是使用者手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。

    • next('/') 或者 next({ path: '/' }): 跳轉到一個不同的地址。當前的導航被中斷,然後進行一個新的導航。你可以向 next 傳遞任意位置物件,且允許設定諸如 replace: truename: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。

    • next(error): (2.4.0+) 如果傳入 next 的引數是一個 Error 例項,則導航會被終止且該錯誤會被傳遞給 router.onError() 註冊過的回撥。

官方介紹的比較簡單,沒有實際栗子,下面我就通過栗子再進行詳細的說明

//1,可以在main.js 或者在單獨的路由配置檔案router.js中進行設定
	router.beforeEach((to, from, next) => { 
	...
      next();
    });
	
//2,也可以在元件內部設定
	this.$router.beforeEach((to, from, next) => { 
	...
      next();
    });
	
//3,對函式及next()的詳細使用說明
    router.beforeEach((to, from, next) => { 
	//首先to和from 其實是一個路由物件,所以路由物件的屬性都是可以獲取到的(具體可以檢視官方路由物件的api文件).
	//例如:我想獲取獲取to的完整路徑就是to.path.獲取to的子路由to.matched[0].
      next();//使用時,千萬不能漏寫next!!!
	//next()  表示直接進入下一個鉤子.
	//next(false)  中斷當前導航
	//next('/path路徑')或者物件形式next({path:'/path路徑'})  跳轉到path路由地址
	//next({path:'/shotcat',name:'shotCat',replace:true,query:{logoin:true}...})  這種物件的寫法,可以往裡面新增很多.router-link 的 to prop 和 router.push 中的選項(具體可以檢視api的官方文件)全都是可以新增進去的,再說明下,replace:true表示替換當前路由地址,常用於許可權判斷後的路由修改.
	//next(error)的用法,(需2.4.0+) 
    }).catch(()=>{
  //跳轉失敗頁面
  next({ path: '/error', replace: true, query: { back: false }})
})
//如果你想跳轉報錯後,再回撥做點其他的可以使用 router.onError()
router.onError(callback => { 
      console.log('出錯了!', callback);
    });
複製程式碼
  • 2,路由獨享的守衛: 即路由物件獨享的守衛
    • beforeEnter:路由只獨享這一個鉤子,在rutes裡配置
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // 使用方法和上面的beforeEach一毛一樣
      }
    }
  ]
})
複製程式碼
  • 3,元件內的守衛: 注意:這類路由鉤子是寫在元件內部的,
    • beforeRouteEnter 進入路由前,此時例項還沒建立,無法獲取到zhis
    • beforeRouteUpdate (2.2) 路由複用同一個元件時
    • beforeRouteLeave 離開當前路由,此時可以用來儲存資料,或資料初始化,或關閉定時器等等

這裡官方的例子說明的很詳細,這裡就直接進行引用了.

//在元件內部進行配置,這裡的函式用法也是和beforeEach一毛一樣
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`
  }
}
複製程式碼

14.2 完整的導航解析流程

官方文件給的說明都是文字形式的,不是特別直觀.這裡就不copy了.這裡就直接以流程圖的形式進行展示(這裡參考了這位同學的圖,在此感謝!).

導航解析流程

15 路由元資訊

15.1 什麼是路由元資訊

一句話概括:路由配置的meta物件裡的資訊. 官方栗子:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      children: [
        {
          path: 'bar',
          component: Bar,
          // a meta field
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})
複製程式碼

從栗子可以看出就是給路由新增了一個自定義的meta物件,並在裡面設定了一個requiresAuth狀態為true.

15.2 它有什麼用

從下面的另一個官方栗子裡已經給出了答案.

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    //對matched不瞭解的建議看官方api文件,或我7.1節的說明
	//陣列some方法,如果meta.requiresAuth為ture,則返回true.此時,說明進入該路由前需要判斷使用者是否已經登入 
    if (!auth.loggedIn()) {   //如果沒登入,則跳轉到登入頁
      next({
        path: '/login',
        query: { redirect: to.fullPath }  //官方例子的這個小細節很好,通過query將要跳轉的路由路徑儲存下來,待完成登入後,就可以直接獲取該路徑,直接跳轉到登入前要去的路由
      })
    } else {
      next()
    }
  } else {
    next() // 確保一定要呼叫 next()
  }
})
複製程式碼

我們可以通過在meta裡設定的狀態,來判斷是否需要進行登入驗證.如果meta裡的requiresAuth為true,則需要判斷是否已經登入,沒登入就跳轉到登入頁.如果已登入則繼續跳轉.

此時,可能會有同學說,前面說的path,params,query都可以儲存資訊,作為登入驗證的狀態標記.的確,它們也可以達到同樣的效果.如果是少量單個的驗證,使用它們問題不大. 但如果是多個路由都需要進行登入驗證呢?path,params,query是把資訊顯性地儲存在url上的.並且多個路徑都把一個相同的狀態資訊加在url上.這樣就使url不再單純,並且也很不優雅美觀. 所以要優雅要隱性地傳遞資訊,就使用meta物件吧!

16 滾動行為

當年切換路由時,可以使頁面滾動到你想要的某個地方,或者是保持之前滾動的位置,這時你就需要使用scrollBehavior這個方法.

注意點:

  • 這裡控制和記住的滾動位置都是僅對整個元件頁面而言的,並不包含你元件裡面其他的滾動條.
  • 這裡路由的模式只能是history.因為它使用了History新增的pushState().具體可以看我第6章的說明.
const router = new VueRouter({
mode:'history',//這個不能忘,預設是hash模式
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
     // to:要進入的目標路由物件,到哪裡去.和導航守衛的beforeEach一樣
	 //from:離開的路由物件,哪裡來
	 //savedPosition: 點選前進/後退的時候記錄值{x:?,y:?}.並且只有通過瀏覽器的前進後退才會觸發.
    // return 期望滾動到哪個的位置 { x: number, y: number }或者是{ selector: string, offset? : { x: number, y: number }},這裡selector接收字串形式的hash,如'#foo',同時你還可以通過offset設定偏移,版本需要大於2.6+
	//舉個例項
	if(savePosition) { //如果是瀏覽器的前進後退就,返回之前儲存的位置
      return savePosition;
    }else if(to.hash) {//如果存在hash,就滾動到hash所在位置
      return {selector: to.hash}
    }else{
	  return {x:0,y:0}//否則就滾動到頂部
	}
  }
})
複製程式碼

相關文章