1. 程式人生 > 實用技巧 >vue-router的簡單介紹及應用

vue-router的簡單介紹及應用

  vue-router是Vue的路由管理器,它是Vue的核心外掛。在當前Vue專案中一般都是單頁面應用,所以可以說vue-router它是應用在單頁面中的。在web開發中,路由是指根據url分配到對應的處理程式,vue-router它通過對url的管理,實現url和元件的對應,以及通過url進行元件之間的切換。

  那什麼又是單頁面應用(SPA)?一個專案中只有一個完整的html主頁面,其它都是html片斷組成的分頁面。瀏覽器一開始會在主頁面載入所有必須的檔案,即第一次進入頁面的時候會請求一個html檔案,在頁面跳轉互動時由路由將分頁面載入到主頁面,這時路徑也相應的發生變化,頁面內容也發生變化,但是並沒有新的html請求。js感知到url的變化後動態的將當前頁面的內容清除然後將下一個頁面的內容掛載到當前頁面上。這時前端就要判斷到底是哪個頁面進行顯示,哪個頁面進行清除。這個過程就是單頁面應用。與之相對的是多頁面應用(MPA),即一個專案是由多個完整的html頁面組成,不存在在主分頁面的說話,每一次頁面跳轉所有的資源都要重新進行載入,後臺伺服器都會返回一個新的html文件。就像傳統的頁面應用,它們是用超連結來實現頁面切換和跳轉的。

  vue-router它實現的原理就是更新檢視但不重新請求頁面。下面我們來介紹vue-router的詳細用法。

  一、安裝及使用

  1、直接下載:https://unpkg.com/[email protected]/dist/vue-router.js &npm: npm install vue-router

  2、在index.js中引入vue和vue-router還有頁面元件  

import Vue from 'vue'
import VueRouter from 'vue-router'
import First from '../components/First.vue'

  3、安裝Vue.use()外掛。Vue全域性使用Router,用來擴充套件外掛。

    Vue.use(物件或者函式)如果是物件會預設呼叫物件中的install;如果是函式則把這個函式當成install來執行。它應該在呼叫newVue()啟動前完成

Vue.use(VueRouter)

  4、建立路由物件配置規則。path:路徑name:路徑對應的名字component:元件

const routes = [
  {
    path: '/',
    name: 'First',
    component: First
  },
  {
    path: '/second',
    name: 'Second',
    //路由懶載入:只要元件不顯示就不去載入對應的元件
component: () => import( '../components/Second.vue') } ]

  5、建立一個VueRouter,然匯出。

constrouter=newVueRouter({
mode:'history',
//它是可以自己配置的。基礎路徑
base:process.env.BASE_URL,
//routes:routes路由對映表是一個數組
routes,

})
//匯出
exportdefaultrouter

  6、將路由物件傳遞給vue例項。options中加入router:router(可簡寫成router)

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

  7、在App.vue中留坑。router-link是router提供的全域性元件,是用來提供按鈕展示。router-view它也是router提供的全域性元件,是用展示對應的元件。

  <!-- <router-link> 預設會被渲染成一個 `<a>` 標籤 -->
  <!-- 通過傳入 `to` 屬性指定連結-->
  <router-link to="/">First</router-link> |
  <!-- 還可以用到:to -->
  <!-- <router-link :to="{path:'/'}">First</router-link>  -->
  <!-- :to後面還可以接name -->
  <!-- <router-link :to="{name:'first'}">First</router-link>  -->
  <router-link to="/second">Second</router-link>

  <!-- 路由出口 -->
  <!-- 路由匹配到的元件將渲染在這裡 -->
  <router-view></router-view>

  二、路由模式

  vue-router為了實現單頁面路由跳轉提供了二種模式:hash模式,history模式

  hash模式:它是vue-router的預設模式,使用了url的hash來模擬一個完整的url,當url改變時,頁面不會去重新的載入。

  hash即#它是url的錨點,代表的是網頁中的一個位置,如果只是改變#後面的部分,瀏覽器只會載入相應的內容不會重新載入網頁。也就是說,#它是用來指導瀏覽器動作的,對伺服器完全無用,http請求中不包含#,每一次改變#後的部分都會在瀏覽器的訪問歷史中增加一個記錄,使用“後退”按鈕就能回到上一個位置

  hash模式它通過錨點值的改變,根據不同的值,渲染指定的DOM位置上不同的資料。

  history模式:由於hash模式會在url中自帶#,影響美觀,這時我們可以用路由的history模式。這種模組利用了history.pushState API來完成url跳轉而不用重新載入頁面的  

// main.js
const router = new VueRouter({
  mode:'history',
  routes:[......]
})

  當使用history模式時,url就像正常的url一樣。如:http://localhost:8082/second.但這個需要後臺配置支援。

  進行配置:要使用history模式需要伺服器配置。要在服務端增加一個覆蓋所有情況的資源,如果url匹配不到任何靜態資源,則要返回一個html頁面。下面就設定瞭如果url輸入錯誤或者是url匹配不到任何資源就返回Home頁面。

export const routes = [ 
  {path: "/", name: "homeLink", component:Home}
  {path: "/register", name: "registerLink", component: Register},
  {path: "/login", name: "loginLink", component: Login},
  {path: "*", redirect: "/"}]
  

  上面的path: "*", redirect: "/"。使用了重定向功能。重定是通過routes配置來完成的。重定向的目標可以是一個檔案;也可以是一個路由;甚至是一個方法,動態返回重定向目標。

const router = new VueRouter({
  routers:[
    // 從/a重寫向到/b
    { path:'/a',redirect:'/b'},
    //它也可以是一個命名的路由
    {
      path:'/a' ,redirect:{name:'First'}
    },
    //可以是一個方法
    {
     path:'/a',redirect:to=>{
       //方法接收目標路由作為引數
       // retrun 路徑+物件
       return './first'
     }
    }
  ]
})

  與重定向redirect相關的還有一個別名alias。使用alias也可以達到redirect的效果。重定指的是當用戶訪問/a時url將會被替換成/b,然後匹配路由/b。而別名是/a的別名是/b,這說明了當用戶訪問/b時url會保持/b但是路由匹配則為/a,就像使用者訪問/a一樣的。

const router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
  ]
})

  別名的功能可以將ui結構對映到任意的url,不受限於配置巢狀路由結構。需要注意的是別名不要用在path為'/'中,不然是不起作用的。

  三、巢狀路由

  路由元件配置中需求增加children欄位,欄位值結構一致,表示子路由路徑。

  如果想在Second下新增兩個子路由,分別命名為childOne.vue和childTwo.vue。利用vue-router步驟如下:

  1、在路由元件配置中增加children欄位

const routes = [
  {
    path: '/second',
    name: 'Second',
    component: () => import( '../components/Second.vue'),
    //巢狀路由
    children:[
      {
        path:'/Second/childOne',
        component:() => import( '../components/childOne.vue'),
      },
      {
        path:'/Second/childTwo',
        component:() => import( '../components/childTwo.vue'),
      }
    ]
  },
  {
    path:'*',
    redirect:'/'
  }
]

  2、實現子路由路徑跳轉切換,就要新增一個<router-view>作為新的路由祝口,渲染在children所在的元件裡,用<keep-alive>讓元件不銷燬。

<template>
  <div>
    <h1>{{msg}}</h1>
    <router-link to="/second/childOne">childOne</router-link> ||
    <router-link to="/second/childTwo">childTwo</router-link>
    <keep-alive include='childTwo'><router-view></router-view></keep-alive>
  </div>
</template>

  四、導航鉤子

  vue-router提供的導航鉤子主要是用來攔截導航,讓它完成跳轉或者取消。

  有以下三種方式可以植入導航過程

  1、全域性

  全域性導航鉤子專案中運用較多的是:前置守衛,後置鉤子。可以使用router.beforeEach註冊一個全域性的before鉤子

const router = new VueRouter({ ...})
router.beforeEach((to, from, next) => {
  //....
})

  當一個導航觸發時,全域性的before鉤子按照建立順序呼叫 ,鉤子是非同步解析執行,此時導航在所鉤子reslove完一直是一個等待中的狀態。

  每一個鉤子函式接收三個引數,引數的含義如下:

    to:即將要進入的目標路由物件,通俗來講就是要跳轉到的那個路徑

    from:當前導航正要離開的路由,即從哪個路徑來的。

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

 示例:只是跳轉就修改頁面的title

  要完成這個操作還需求用到路由元資訊,那什麼又是路由元資訊呢?即定義路由的時候可配置meta欄位,然後進行訪問。 

//index.js
const
routes = [ { path: '/', name: 'First', component: First, meta:{ //可以用來儲存一些自定義的資訊 til:'首頁' }, } ]
//main.js
outer.beforeEach((to, from, next) => {
  document.title = to.meta.til || "example";
  //console.log(to, from)
  next();
})

  next()函式傳遞不同引數有不同的情況,情況如下:

      next():進行管理中的下一個鉤子,如果全部執行完成那導航的就是確認狀態;

      next(false):中斷當前的導航。若瀏覽器url改變,那url的地址會重置到from路由對應的地址

      next('/')&next({path:'/'}):跳轉到一個不同的地址,當前的導航被中斷,然後進行一個新的導航

   需要注意的一點是:要確保要呼叫next方法,不然鉤子就不會被確認。next函式在任何給定的導航守衛中都被嚴格呼叫一次。它可以出現多於一次,但是隻有在所有的邏輯路徑都不重疊的情況下,不然鉤子不會被解析或者是報錯。

  示例:使用者登入(假設已有login登入頁面)

router.beforeEach((to, from, next) => {
  //需求注意的是:基本身就是要去login路徑,這時就不能再next('./login'),所以外面還要再進行一次判斷
  if (to.path !== './login') {
    if (false) {
      next()
    } else {
      next('./login')
    }
  } else {
    next();//不傳參正常向下走
  }
})

  beforeEach有攔截的功能,可以做全域性的校驗,示例如下:

//使用鉤子函式對路由進行許可權跳轉
router.beforeEach((to, from, next) => {
  const role = localStorage.getItem('ms_username');
  if (!role && to.path !== './login') {
    next('./next');
  } else if (to.mata.permission) {
    //如果是管理員許可權則可進,模擬如下
    role == 'admin' ? next() : next('/403');
  } else {
    next();
  }
})

  同樣的,我們也可以註冊一個全域性的after鉤子,但它不像before鉤子那樣,after鉤子,它沒有next方法,不能改變導航。

router.afterEach(route => {
  //......
})

  2、元件內部的導航鉤子

    可以在路由元件內直接定義下面的導航鉤子

beforeRouteEnter
beforeRouterUpdate
beforeRouteLeave
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`
  }
}

   beforeRouteEnter鉤子不能訪問this,因為鉤子在導航確認前被呼叫,因此即將登場的新元件還沒被建立

  不過,可以通過傳一個回撥給 next來訪問元件例項。在導航被確認的時候執行回撥,並且把元件例項作為回撥方法的引數

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通過 `vm` 訪問元件例項
  })
}

  在 beforeRouteLeave 中直接訪問 this。這個 leave 鉤子通常用來禁止使用者在還未儲存修改前突然離開。可以通過 next(false) 來取消導航

  beforeRouteLeave的應用。有First,Second,childOne 三個頁面,Second頁面調到First頁面,需要First頁面進行快取,childOne頁面調到First頁面,First頁面不需要快取

  要通過beforeRouteLeave這個鉤子來對路由裡面的keepAlive進行賦值。從而動態確定First頁面是否需要被快取。

First的路由
{
    path: '/',
    name: 'First',
    component: First,
    meta:{
      keepAlive:true//需要被快取
    }
  }
--------------------------------------------------------------------- second頁面 export
default { name: "Second", data() { return {} }, beforeRouteLeave(to,from,next){ to.meta.keepAlive= true;// second跳轉到first時,快取,即不重新整理 next(); }, };
--------------------------------------------------------------------- childOne頁面 name:
"childOne", data() { return {} }, beforeRouteLeave(to,from,next){ to.meta.keepAlive= false;// childone跳到first時,first快取,即重新整理
next(); }, };

  3、路由中配置的單個導航鉤子

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      },
      beforeEnter: (route) => {
        // ...
      }
    }
  ]
});

  五、資料獲取 

  有時候進入到某個路由後,需要從伺服器上獲取資料,如渲染使用者資訊時,需要從伺服器獲取使用者的資料,這時可以通過兩種方式來完成。

  導航完成後獲取:先完成導航,然後在接下來的元件生命週期獲取資料,在資料獲取期間顯示正在載入中之類的指示。使用這種方式會馬上導航和渲染維護,然後在元件的created鉤子中進行獲取資料,有機會在資料獲取的期間展示一個loading的狀態,還可以在不同檢視間展示不同的loading狀態。

  假設有一個 Post 元件,需要基於 $route.params.id 獲取文章資料:

<template>
  <div class="post">
    <div v-if="loading" class="loading">
      Loading...
    </div>

    <div v-if="error" class="error">
      {{ error }}
    </div>

    <div v-if="post" class="content">
      <h2>{{ post.title }}</h2>
      <p>{{ post.body }}</p>
    </div>
  </div>
</template>
-----------------------------------------------------
export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    // 元件建立完後獲取資料,
    // 此時 data 已經被 observed 了
    this.fetchData()
  },
  watch: {
    // 如果路由有變化,會再次執行該方法
    '$route': 'fetchData'
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      getPost(this.$route.params.id, (err, post) => {
        this.loading = false
        if (err) {
          this.error = err.toString()
        } else {
          this.post = post
        }
      })
    }
  }
}

  導航完成之前就獲取:在導航完成前,在路由的enter鉤子中獲取資料,在資料獲取成功後執行導航。這種方式在導航轉入新的路由前就獲取了資料,可以在元件中的beforeRouteEnter鉤子中獲取資料,當資料獲取成功後呼叫next方法。

export default {
  data () {
    return {
      post: null,
      error: null
    }
  },
  beforeRouteEnter (to, from, next) {
    getPost(to.params.id, (err, post) => {
      next(vm => vm.setData(err, post))
    })
  },
  // 路由改變前,元件就已經渲染完了
  // 邏輯稍稍不同
  beforeRouteUpdate (to, from, next) {
    this.post = null
    getPost(to.params.id, (err, post) => {
      this.setData(err, post)
      next()
    })
  },
  methods: {
    setData (err, post) {
      if (err) {
        this.error = err.toString()
      } else {
        this.post = post
      }
    }
  }
}

  在為後面的檢視獲取資料時,使用者會停留在當前的介面,因此建議在資料獲取期間,顯示一些進度條或者別的指示。如果資料獲取失敗,同樣有必要展示一些全域性的錯誤提醒。

  六、router中的傳參

    vue-router傳參主要分為兩個大類一是程式設計式的導航,二是宣告式的導航。

  1、程式設計式導航(router.push):它傳遞的引數主要是分為兩類,一類是字串,一類是物件。

  為字串的方式是直接將路由地址用字串的方式來進行跳轉,這種方式簡單,但是不能傳遞引數,如下:

//this.$route這裡儲存的都是屬性
//this.$router這裡儲存的都是方法
// this.$router.push(引數) 引數等同於to後跟的內容

this
.$router.push('/');// /為要跳轉的地址

  為物件時它主要用到params和query來進行傳遞,使用query它是拼接到url的後面。可以用name也可以用path進行引入。當First.vue中有一個按鈕,點選按鈕可以跳轉到Second.vue,進行引數傳遞,程式碼如下:

First.vue
//使用path引入
  methods: {
    gosecond: function() {
      this.$router.push(
          { 
            path: "second", 
            query: { id:'123456',name:'tz' } 
      });
    }
  }

//使用name引入
  methods: {
    gosecond: function() {
      this.$router.push(
          { 
            name: "second", 
            query: { id:'123456',name:'tz' } 
      });
    }
  },
----------------------------------------------
Second.vue
mounted:function(){
    this.id=this.$route.params.id;
    this.name = this.$route.params.name
}

  使用params有兩點需求注意,一是用name來進行引入,所以要確保路由中有name。二是要配置路由中的path,因為path它是路由的一部分,必須在路由後面新增上引數名。

First.vue
<button @click="gosecond">按鈕</button>

methods: {
    gosecond: function() {
      this.$router.push(
          { 
            name: "Second", 
            params: { id:'123456',name:'tz' } 
      });
    }
}

Second.vue
mounted:function(){
    this.id=this.$route.params.id;
    this.name = this.$route.params.name
}

index.js
{
    path: '/second/:id/:name',
    name: 'Second',
    component: () => import( '../components/Second.vue')

}

 總結程式設計式導航三種方式:

  this.$router.push("destination"),簡單但不能傳參;

  this.$router.push({path(name):"destination",query:{id:xxx,name:xxx}})。

  this.$router.push({name:"destination",params:{id:xxx,name:xxx}})。這種方式的不足是如果沒有設定路由中的path那在目標頁面重新整理傳遞過來的引數不顯示會丟失。 

 2、宣告式導航<router-link>

  宣告式的導航和程式設計式是一樣的。

  字串:

      <router-link :to="{path:'/'}">First</router-link>

  物件,使用query

      <router-link :to="{path:'/',query:{id:'123',name:'first'}}">First</router-link>

  物件,使用params

    <router-link :to="{path:'/',params:{id:'123',name:'first'}}">First</router-link>