vue-router基本概念總結

一、vue-router是什麼?

要搞明白這個問題,你必須先了解什麼是單頁應用(或叫單頁Web應用),以下解釋引自百度百科:

單頁Web應用(single page web application,SPA),就是隻有一張Web頁面的應用,是加載單個HTML 頁面並在用戶與應用程序交互時動態更新該頁面的Web應用程序。

單頁應用不存在頁面跳轉,它本身只有一個HTML頁面。我們傳統意義上的頁面跳轉在單頁應用的概念下轉變爲了body內某些元素的替換和更新,舉個例子,假如我們有下面的頁面index.html

<!DOCTYPE html>
<html>
  <head>...</head>
  
  <body>
    <login-page/>
  </body>
  
</html>

這裏現在加載的是登錄組件。隨後用戶進行了登錄,我們在登錄請求的回調函數中把body內的組件login-page替換成了歡迎頁組件welcome-page,因此index.html就變成了下面的樣子:

<!DOCTYPE html>
<html>
  <head>...</head>
  
  <body>
    <welcome-page/>
  </body>
  
</html>

顯然index.html仍然是它本身,但是由於用戶操作,它的整個body的內容從登錄組件變成了歡迎頁組件,我們實現了傳統意義上的頁面跳轉。但是要注意,由於當前加載的仍然是原來的HTML頁面,因此瀏覽器不會完全重載,已下載的公共資源(如框架代碼、普通的js代碼等)不會被重新執行。瀏覽器只會下載與新的組件相關的js代碼到本地執行,繼而完成頁面內容的更新。

這樣,從視覺上,頁面已經進行了跳轉。但實際上,頁面只是隨着用戶操作,實現了局部內容更新。所以你可以看到,相對於傳統的多頁面應用,單頁應用有着極高的“跳轉速度”,因此備受歡迎。

vue-router就是vue框架下管理如何進行頁面替換和更新的組件。藉助vue-router,你可以清晰地掌控整個頁面內容更新的過程,擁有像瀏覽器地址欄一樣強大的前進和回退功能。

router的中文釋義爲“路由”,它是計算機網絡中非常重要的概念,表示分組從源到目的地時,決定端到端路徑的網絡範圍的進程。換句話說,就是分組數據包從源到目的地,經歷了哪些網絡節點。在單頁應用中,它表示頁面的更新過程中所經歷的路徑變化。由於沒有頁面跳轉,因此路由無法直接映射到頁面地址上,而是通過地址後面的hash值來體現的,舉個例子,下面可能是登錄頁的地址:

https://xxxx/index.html#/login

#後面的值我們稱爲哈希值,即/login,它表示我們當前處在index.html頁面內的login路徑下。當用戶執行了登錄後,頁面地址可能變化爲:

https://xxxx/index.html#/welcome

我們看到,頁面仍然是index.html,但是它後面的哈希值卻變成了/welcome,這表示頁面內部已經切換到了/welcome所對應的組件。而我們的模板可能是這樣寫的:

<!DOCTYPE>
<html>
  // 引入vue、vue-router以及登錄、歡迎頁等組件
  <head>
    ...
  </head>
  
  <body>
    <div id="app">
      <router-view/>
    </div>
    
    <script>
      const router = new VueRouter({  // 定義路由
        routes: [
          { path: '/', redirect: '/login' }, //默認打開的頁面
          { path: '/login', component: login-page },
          { path: '/welcome', component: welcome-page }
        ]
      })

	  const app = new Vue({
		router   // 向vue實例導入路由
	  }).$mount('#app');
    </script>
  </body>
</html>

html我們指定當地址中的哈希值爲/時,重定向到/login,而/login/welcome則分別對應我們的登錄組件login-page和歡迎頁組件welcome-page<router-view/>vue-router使用的佔位組件,它將根據哈希值動態被替換爲對應的組件。

因此當你在地址欄輸入https://xxxx/index.html,會首先匹配到空路由/,隨後被重定向到/loginvue-router會將該路由對應的組件login-page替換到佔位組件<router-view/>的位置。當用戶執行了登錄,並執行類似以下的操作時:

  this.$axios.post('xxx', data).then(res => {
    this.$router.push('/welcome');
  })

該語句向路由堆棧中添加了新的值/welcome,因此vue-router會將對應的welcome-page組件替換到佔位組件<router-view/>。從視覺上,登錄頁被替換成了歡迎頁,而內部機制上,就是完成了一次組件替換,沒有發生頁面重載。

vue-router的作用就是對單頁應用中的上述過程進行管理,下面我們簡單來看一下它的基本用法。

二、基本用法

關於vue-router的基本語法,vue-router官網上有詳盡的講解,有需要的可以移步官網進行深入學習。這裏我們進行一下大致的介紹。

1. 起步

要使用vue-router,首先需要安裝和引入它。

npm install vue-router --save  // 安裝

impoer VueRouter from 'vue-router'; // 引入

const router = new VueRouter({
  routes: [
    { path: '/login', component: login-page },
    { path: '/welcome', component: welcome-page },
    ...  // 爲每個路由指定對應的組件
  ]
})

const app = new Vue({
  router   // 向vue實例導入路由
}).$mount('#app');

現在我們已經定義好了路由映射規則,並在vue實例中引入了它。當我們修改路由堆棧中的值時,vue-router就會按照這套規則將抽象組件<router-view/>替換爲對應的組件。

此時的HTML模板可能像下面一樣:

  <body>
    <div id="app">
      <router-view/>
    </div>
  </body>

那我們怎麼觸發路由的變化呢?

當我們在創建vue實例並傳入router實例對象時,vue會自動將這個router對象作爲vue實例的$router屬性,即:

const app = new Vue({
  router   // 向vue實例導入路由
}).$mount('#app');

// app.$router => vue-router實例對象

變量app是一個vue實例,接下來我們便可以通過它的$router屬性去觸發路由變化了。比如我們想修改路由,可以通過pushreplace方法:

app.$router.push('/welcome');

// app.$router.replace('/welcome');

push方法會將前一個路由/login保存在堆棧中,而replace則是直接替換上一個路由,因此在路由回退時兩者的行爲是不同的。

上面的語法稱爲編程式的導航,也就是通過js代碼進行導航。除此之外,還可以通過路由組件的方式:

  <p>
    <!-- 使用 router-link 組件來導航. -->
    <!-- 通過傳入 `to` 屬性指定鏈接. -->
    <router-link to="/login">Go to login-page</router-link>
    <router-link to="/welcome">Go to welcome-page</router-link>
  </p>

這兩個router-link組件是由vue-router定義的,它們在頁面上會被渲染爲一個a標籤。每個組件帶有一個to屬性,表示點擊該組件需要跳轉的路由地址,點擊該組件的行爲與執行push方法的結果是一致的。這種方式常用於導航欄。

2. 動態路由

在實際應用中,我們可能需要將多個路由地址映射到同一個組件。比如我們現在有一個商品列表,點擊某個商品就會進入該商品的詳細信息頁面。由於商品詳細信息的格式是一致的,因此我們只編寫了一個組件goods-detail

假設商品列表的路由地址爲/goods,而/goods/pants/goods/shoes這樣的路由地址都是某個商品對應的詳細信息頁面,它們使用的都是同一個組件goods-detail

這樣的需求我們就需要用到動態路由了。此時我們可以像下面一樣定義路由規則:

const router = new VueRouter({
  routes: [
    // 動態路徑參數 以冒號開頭
    { path: '/goods/:type', component: goods-deatil }
  ]
})

現在只要是符合/goods/xxx這種模式的路由,都會被這個規則捕獲,加載goods-detail組件,並且路由中商品的類型被定義爲了type屬性。我們可以在goods-detail組件中通過路由參數對象$route獲取這個值:
goods-detail

<template>
  <div>
    <p>{{ $route.params.type }}</p>
  </div>
</template>

當路由值爲/goods/shoes時,組件內部會渲染成:

<div>
    <p>shoes</p>
  </div>

注意,$router$route並不是同一個屬性,前者是路由實例對象本身,而後者是該實例對象對應的參數對象,一般來說,操作路由應該使用$router,如push、replace等,獲取參數應該用$route,如獲取params值。

3. 嵌套路由

當多個路由地址的前綴路徑相同時,嵌套路由可以減少路由規則定義的複雜性。比如,如果沒有嵌套路由,我們可能會像下面一樣定義一組路由:

const router = new VueRouter({
  routes: [
    { path: '/goods/detail', component: goods-deatil },
    { path: '/goods/pay', component: goods-pay },
    { path: '/goods/feedback', component: goods-feedback }
  ]
})

使用嵌套路由,你可以寫成下面的格式:

const router = new VueRouter({
  routes: [
    // 動態路徑參數 以冒號開頭
    { path: '/goods', 
      children: [
        { path: 'detail', component: goods-deatil },
        { path: 'pay', component: goods-pay },
        { path: 'feedback', component: goods-feedback }
      ]
  ]
})

children屬性作爲/goods的子路由存在,它定義的路由會被拼接到上一級路由後面,即/goods/detail/goods/pay/goods/feedback

4. 命名路由

在定義路由規則時,你可以爲路由指定一個名字,這樣你就可以不用在每次切換路由時指定路由地址了,如:

const router = new VueRouter({
  routes: [
    { path: '/goods/detail', 
      name: 'detail',
      component: goods-deatil },
  ]
})

現在'/goods/detail'這個路徑已經被命名爲detail,因此你可以像下面一樣跳轉到這個路由:

<router-link :to="{name: 'detail', params: { id: '123'}}">detail</router-link>
// 或
this.$router.push({name: 'detail', params: { id: '123' }})

5. 命名視圖

一個頁面可以存在多個router-view佔位組件,那麼當切換一個路由時,如何爲每個router-view指定要渲染的組件呢?答案就是使用命名視圖,爲每個router-view指定一個名字,然後分別定義組件,如:

const router = new VueRouter({
  routes: [
    { path: '/goods', 
      component: {
        default: goods-content, // 默認視圖
        nav: goods-nav
      } },
  ]
})

而頁面模板可能像下面這樣:

<div>
  <router-view/>
 
  <router-view name="nav"/>
</div>

當執行this.$router.push('/goods')時,第一個router-view會被替換爲goods-content組件,而第二個會被替換爲goods-nav組件。

6. 重定向和別名

重定向,顧名思義,就是將一個路由定向到另一個路由,我們在處理根路由時常常會用到。比如在最開始的例子中,我們只有/login/welcome這兩個組件,沒有/對應的組件。這樣當用戶在地址欄輸入https://xxxx/index.htmlhttps://xxxx/index.html#/時,頁面會顯示爲白頁。

假如我們希望讓/自動加載/login的組件,可能需要下面的額外定義:

{ path: '/', component: login-page },

但我們想的是這時應用應該自動將路由直接置爲/login,於是我們會這樣寫:

{ path: '/', redirect: '/login' },

它表示路由地址/現在將會被重定向到/login,所以當用戶輸入https://xxxx/index.html,地址欄的值會直接變成https://xxxx/index.html#/login,這就是一次路由重定向。

別名則是給某個路由另起一個名字。如:

{ 
  path: '/login', 
  component: login-page, 
  alias: '/userLogin' 
}

現在輸入https://xxxx/index.html#/loginhttps://xxxx/index.html#/userLogin都會匹配該路由。加載login-page組件。

7. 路由傳參

當我們在路由跳轉時向路由組件傳遞了參數,除了可以像上面一樣使用$route對象來獲取外,還可以通過props屬性,將參數轉化爲props。如:

{ path: '/user/:id', component: User, props: true },

props: true意味着現在與路由相關的參數都被作爲props傳入組件,因此你可以這樣獲取參數:

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}

路由中的id參數將作爲props中的id屬性傳入。

除了使用布爾值,props還可以使用對象或函數。

傳入對象時,它會原樣被設置爲組件屬性:

{ path: '/user/:id', component: User, props: { state: 'active' } }

組件內:

const User = {
  props: ['state'],
  template: '<div>User {{ state }}</div>'
}

傳入函數時,可以基於路由參數對象,返回更復雜的參數:

{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }

URL/search?q=vue會將{query: 'vue'}作爲屬性傳遞給 SearchUser 組件。

8. History模式

vue-router默認使用hash模式進行路由導航,因此在切換路由時,頁面並不會跳轉。但是有人可能覺得,在url中添加一個#看起來並不美觀,希望既可以不重載頁面,又可以不在地址中出現醜陋的#,HTML5的History接口爲這種需求提供了可能。

這種模式的實現需要後端的配合,比如對於http://yoursite.com/user/id這樣的地址,我們希望它加載index.html頁面,就需要在後端配置好,否則就會返回404頁面。

前端收到index.html頁面後,自行根據url進行路由導航。想了解具體的實現,請參考MDN - History

總結

本文只是關於vue-router的基本語法介紹,還有一些進階用法沒有涉及,包括導航守衛、路由元信息、過渡動效、數據獲取、滾動、路由懶加載等,需要進一步學習請參考vue-router官網

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章