17. vue-route詳細介紹


  1. 前后端路由的來歷
  2. 前端如何實現頁面跳轉但是不刷新?
    了解hash和history兩種方法
  3. vue-router基本使用
    • 安裝vue-router

    • 搭建vue-router框架的步驟

    • vue-router路由的配置步驟

      • 第一步: 創建路由組件
      • 第二步: 配置路由映射。 即:組件和路由的關系
      • 第三步: 使用路由, 通過<router-link>和<router-view>來展示組件
    • 路由的默認配置

    • 修改靜態路由的模式為history

    • vue-link屬性設置

    • 通過代碼跳轉路由

    • 動態路由的使用

    • 路由的懶加載

  4. vue-router的嵌套
  5. vue-router參數傳遞
  6. vue-router導航守衛
  7. keep-alive

說道路由, 我們最熟悉的路由是什么呢? 那就是路由器了。 其實路由器有兩個重要的功能: 路由和傳送
  • 路由: 路由決定數據包從哪里來, 到哪里去。 也有是來源和目的地的路徑。

      路由中有一個路由表, 路由表本質上就是一個映射表,決定了數據包的傳輸方向。
    
  • 傳送: 傳送是將輸入端的數據傳送給輸出端

下面我們先來搭建一個項目, 然后一邊學習一遍在項目里實戰
創建vue-cli2項目

vue init webpacek vueroute

然后一路向下就可以了, 如果本地創建項目很慢, 可以采用離線創建項目的方式, 具體步驟:

  1. 下載webpack離線模板: https://github.com/vuejs-templates/webpack
  2. 將下載的模板壓縮包解壓,修改文件名為wepback並放入到/Users/自己電腦的用戶名/.vue-templates文件夾中.
  3. 執行創建項目的命令: vue init webpack --offline

一. 前后端路由的來歷

前后端路由, 目前經歷了三個階段, 哪三個階段呢?

  1. 前后端一體(后端路由)階段: 這個階段類似早起的jsp頁面, 和java頁面混合在一起, jsp里面有css, js, 還有java代碼, 前后端都是混合在一起的.

    缺點很明顯, 前后端分工不明確.

    這種方式采用的是后端路由

    基本流程: 請求一個controller接口 --> 通過接口找到頁面 --> 渲染頁面 --> 返回給瀏覽器

  2. 前后端分離階段: 前端通過ajax調用的形式, 調用后台接口. 前端主要負責頁面渲染, 后端主要提供api接口.

    優點: 前后端分離. 各司其職, 分明明確. API可復用性強, fe, android, ios都可用

    此時用戶請求的就是一個html頁面, 頁面需要哪個url了, 直接通過ajax請求到頁面.

    基本流程: 瀏覽器url請求 -> html頁面 -> ajax請求api接口 -> 后台接口響應數據 -> html頁面渲染 -> 返回給瀏覽器

  3. 單頁面富應用階段: 簡稱SPA, 全稱是simple page application. 最主要的特點是在前后端分離的基礎上加了一層前端路由. 這個路由是有前端來維護的一套路由.

    單頁面指的是:一個html文件 + 一個js文件 + 一個css文件.

可是就一個網站來說, 不可能只有一個頁面. 那么是如何實現的呢?我們來看下圖

前端只有一個頁面index.html, 而各個功能都是一個組件, 將所有組件都放到index.html頁面中, 然后根據用戶的請求定位到某一個組件. 這個是誰來定位的呢?就是前端路由來定位, 在vue中前端路由就是vue-router.

前端路由的核心是什么呢? 改變url, 但是頁面不進行整體刷新.

二. 前端如何實現頁面跳轉但是不刷新?

前面說了, vue使用的是單頁面富應用, 也就是一個index.html就是整個項目, 然后在內部在跳轉鏈接的時候, 不會刷新頁面, 實現這種方式有兩種方法:hash和history
這兩種模式都可以實現頁面跳轉,但是不刷新頁面. 他們如何使用, 又有什么區別呢?

1. hash

首先啟動vue項目

vue init dev

然后打開頁面

localhost:8080

在瀏覽器控制台輸入localhost.hash="about", 我們可以看到頁面鏈接變成了localhost:8080/#/about/

如上圖, 我們可以通過location.hash="鏈接"的方式來修改url,但是並不刷新頁面

2. history

除了使用hash,我們還可以使用history來改變實現改變url但不刷新頁面的方法. history有幾個常見的用法.

  1. history.pushState(state,"title","url")
    向瀏覽器新增一條歷史記錄,但是不會刷新當前頁面(不會重載)
  • state為對象,可以用作攜帶信息用,
  • title:目前來看沒啥用一般為空或null,
  • URL:即要更改頁面的URL,且必須同源,不能跨域;

案例: 我們在vue的瀏覽器界面改變http://localhost:8080為http://localhost:8080/about

如上圖所示: 當我們執行history.pushState({a:1},null,"about");的時候, 瀏覽器並沒有刷新頁面(Network沒有請求), 但是url鏈接確實發生了變化

  1. history.replaceState(state,title,URL)

更改當前瀏覽器的歷史記錄,即把當前執行此代碼頁面的記錄給替換掉,參數與第一條相同

  1. history.back()、history.forward()、history.go()
    分別為前進一個歷史,后退一個,history.go(Number),其中Number可正可負,即向前或向后若干個記錄

案例:

如上圖, 當我們使用history.back()命令的時候, 會回退到上一個頁面, 也並沒有發生更新.

  1. history.state
    返回當前頁面狀態參數,此參數一般由history.pushState(state,title,URL);以及history.replaceState(state,title,URL);附帶的state值,例子如下:

案例:

如上圖: 可以看出history.state就是取出了pushState和replaceState兩個命令中的第一個參數

  1. history.length
    返回當前頁面所產生的歷史記錄個數,即在同一個瀏覽器tab下產生的歷史記錄;
  1. history事件onpopstate
window.onpopstate = function(e){
	console.log(e.state);
}

在history.back(),history.forward(),history.go()時觸發此事件,但是在history.pushState();history.replaceState();時並不會觸發此事件,事件內可以獲取到state狀態值

可以看出vue-router中push()、go()等函數是基於hash和histroy的,但是vue-router比這個要復雜.

三. vue-router基本使用

vue-router是Vue.js官方的路有插件, 他和vue.js是深度集成的.適合構建於單頁面富應用.

vue-router官網: https://router.vuejs.org/zh/

  • vue-router是基於路由和組件的: 路由用於設定訪問路徑, 將路徑和組件映射起來
  • 在vue-router的單頁面應用中, 頁面的路徑的改變就是組件的切換.

1. 安裝vue-router

npm install vue-router --save

--save: 表示在構建以后也要使用這個路由
安裝好以后, 在package.json中就可以看到安裝的vue-router的版本了

並且在src目錄下多了一個文件夾router

2. 搭建vue-router框架的步驟

在我們安裝了vue-router以后,就會在src目錄下自動生成一個文件夾router.
我們在里面創建一個index.js文件, 然后開始配置路由相關的信息

  • 第一步: 導入路由對象,並且調用vue.use(VueRouter)
    安裝了vue-router, 要想使用, 還需要先引入路由對象
import Router from 'vue-router'

vue-router是一個插件, 所以, 我們需要使用vue.use(插件名) 來安裝插件

Vue.use(Router)
  • 第二步: 創建路由實例,並且傳入路由映射配置
    在VueRouter中主要是配置路由和組件之間的映射關系的.
const routes = [

]
const router = new VueRouter({
  // 這里配置的是路由和組件的映射關系, 是一個數組.
  routes
})

在這里路由是空的, 還沒有配置任何映射關系.

  • 第三步: 將vue-router實例對象導出
export default router
  • 第四步: 在vue實例中掛載創建的路由實例
import router from './router'

在import目錄的時候, 有一個默認的規則, 那就是如果你導入一個目錄, 比如./router, 沒指定導入哪一個文件, 他會自動導入index.js文件*

然后在vue實例中引入router

new Vue({
  el: '#app',
  // 然后通過router導入vue路由
  router,
  render: h => h(App)
})

3. vue-router路由的配置步驟

  • 第一步: 創建路由組件
    通常, 我們會在components目錄下創建組件。鼠標右擊-> Vue Component, 然后定義組件內容
    我們創建兩個組件, 一個首頁, 一個關於頁面.

首頁

<template>
 <div>
   <h2>這是Home主頁</h2>
   <div>歡迎來到home主頁</div>
 </div>
</template>

<script>
   export default {
       name: "Home"
   }
</script>

<style scoped>
</style>

關於頁面

<template>
 <div>
   <h2>關於頁面</h2>
   <div>歡迎來到關於頁面</div>
 </div>
</template>

<script>
   export default {
       name: "About"
   }
</script>

<style scoped>

</style>

這樣組件就創建好了。

  • 第二步: 配置路由映射。 即:組件和路由的關系

組件創建好了, 接下來要構建組件和路由的關系。 構建路由關系,我們通常定義在router/index.js文件

映射關系主要有兩個部分. 一個是path:路由的路徑; 一個是component:關聯的組件

在router/index.js文件中首先引入上面定義好的組件

import Home from '../components/Home';
import About from "../components/About";

然后指定路由路徑和組件

const routes = [
  {
    path: "/home",
    component: Home
  },{
    path: "/about",
    component: About
  }
]
  • 第三步: 使用路由, 通過<router-link>和<router-view>來展示組件
    現在組件有了, 路有關系也有了。 那么接下來就是要有個入口來展示組件或者路由了。 我們的入口只有一個, 那就是App.vue, 所以, 我們直接在這里定義兩個入口即可。
<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <router-link to="/home">首頁</router-link>
    <router-link to="/about">關於</router-link>

    <!-- 展示組件內容 -->
    <router-view></router-view>
  </div>
</template>

<router-link>: 是一個vue-router中內置的組件, 它會被渲染成一個a標簽。

<router-vie>: 會根據當前的路徑, 動態渲染組件的內容

網頁的其他內容, 例如:頂部的標題/導航,底部的版權信息等和/ 處於一個等級

在切換路由時, 切換的是掛在組建的內容, 其他不會發生改變

整體效果如下:

4. 路由的默認配置

現在我們進入首頁顯示的只有導航信息, 在頁面必須點擊某一個按鈕,才能渲染出對應組件的內容。通常我們會有一個默認組件的展示。 否則首頁內容就是空的了。如何設置默認展示的路由呢?

在路由表中增加一個重定向路由

{
    path:"/",
    redirect: "/home"
}

這樣, 打開首頁,直接加載home組件的內容

5. 修改靜態路由的模式為history

我們之前都是采用hash的方式來靜態路由跳轉的, 但hash方式有一個缺點, 即帶有#
例如:我們跳轉都Home頁, 他的路徑是

http://localhost:8080/#/home

帶有一個#, 這不符合我們通常路徑的使用方法,所以,我們可以考慮將其替換為history的模式。 如何替換呢? 在router/index.js文件中

const router = new Router({
  // 這里配置的是路由和組件的映射關系, 是一個數組.
  routes,
  mode: "history"
})

6. vue-link屬性設置

1. to屬性

我們在首頁, 定義vue-link跳轉的路由使用的就是to屬性

<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <router-link to="/home">首頁</router-link>
    <router-link to="/about">關於</router-link>

    <!-- 展示組件內容 -->
    <router-view></router-view>
  </div>
</template>

2. tag屬性

<router-link> 默認會被渲染成a標簽, 如果我們想要將其渲染為其他標簽是否可以呢? 當然是可以的, 使用tag屬性, 比如: 我們想要將其渲染為button標簽

<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <router-link to="/home" tag="button">首頁</router-link>
    <router-link to="/about" tag="button">關於</router-link>

    <!-- 展示組件內容 -->
    <router-view></router-view>
  </div>
</template>

3. replace屬性

如下圖, 我們在點擊導航以后, 可以在瀏覽器向前或向后導航

如果我們不想要瀏覽器記錄我們的請求行為, 可以使用replace. 我們只需要在<router-link>標簽中增加屬性replace就可以了. 這個屬性不需要設置值

<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <router-link to="/home" tag="button" replace>首頁</router-link>
    <router-link to="/about" tag="button" replace>關於</router-link>

    <!-- 展示組件內容 -->
    <router-view></router-view>
  </div>
</template>

效果如下: 瀏覽器的返回和前進按鈕都不可用

4. active-class 修改激活時樣式名稱的默認值
先來看看如何設置<router-link>元素的樣式.
在點擊按鈕的時候, 在控制台可以看到有一個樣式router-link-active, 這個樣式是控制按鈕激活的樣式, 如果我們想要修改激活的效果, 修改這個樣式即可.

比如: 我們設置按鈕激活時字體顏色為紅色.

<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <router-link to="/home" tag="button" replace>首頁</router-link>
    <router-link to="/about" tag="button" replace>關於</router-link>

    <!-- 展示組件內容 -->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
.router-link-active {
  color:red;
}
</style>

重點看style里面的樣式定義. 效果如下:

router-link-active是vue-router默認/ 激活時顯示的樣式, 如果想要修改這個樣式名稱, 可以使用active-class.

<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <router-link to="/home" tag="button" replace active-class="active">首頁</router-link>
    <router-link to="/about" tag="button" replace active-class="active">關於</router-link>

    <!-- 展示組件內容 -->
    <router-view></router-view>
  </div>
</template>

如上, 可以看到active-class="active", 表示將默認的屬性重命名為active.

后面重定義樣式的時候, 使用active即可

<style>
.active {
  color:red;
}
</style>

全局修改active-clas的默認名稱. 可以在路由里實現.

修改router/index.js文件

const router = new Router({
  // 這里配置的是路由和組件的映射關系, 是一個數組.
  routes,
  mode: "history",
  linkActiveClass: "active"
})

7. 通過代碼跳轉路由

上面我們都是在vue中直接使用<router-link>來路由, 我們還可以使用普通標簽路由, 例如button標簽, 來看看怎么實現
第一步: 定義兩個button元素

<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <button @click="clickHome">首頁</button>
    <button @click="clickAbout">關於</button>
    <!-- 展示組件內容 -->
    <router-view></router-view>
  </div>
</template>

這就是兩個普通的button, 然后定義了兩個click點擊事件, 下面我們就要在點擊事件中實現路由

第二步: 定義click事件, 路由到組件

<script>
export default {
  name: 'App',
  methods:{
    clickHome() {
      this.$router.push("/home")
      console.log("點擊home按鈕")
    },
    clickAbout() {
      this.$router.push("/about")
      console.log("點擊about按鈕")
    }
  }
}
</script>

這里定義了點擊事件. 通過this.$router.push("/home")來路由到home組件.

this.$router.push("/home"): 在每一個vue對象中, 通過this都可找到$router屬性, 這是一個全局的屬性.

this.$router.push("/home")是使用history的的方式路由到對應的組件, 可以通過瀏覽器的前進和后退按鈕路由.

this.$router.replace("/home"): 不可以使用瀏覽器的前進和后退按鈕路由.

8. 動態路由的使用

動態路由是什么概念呢? 通常我們的url是不變的, 比如. /home, 有些url是變化的,比如/user/zhangsan, /user/lisi, 對於變化的url, 我們如何路由呢?
下面就以用戶為例, 來實現動態路由
第一步: 創建一個User.vue模板

<template>
    <div>
      <h2>用戶界面</h2>
      <div>歡迎你來到用戶界面</div>
    </div>
</template>

<script>
    export default {
        name: "User"
    }
</script>

<style scoped>
</style>

第二步: 添加路由映射關系

import User from "../components/User"

const routes = [
  {
    path: "/user/:userId",
    component: User
  }
]

這里path使用了:userId占位, 表示這里是一個占位符, 將被真實的userId替換. 后面在路由傳遞的時候變量需要和這里保持一致.

第三步: 使用user路由

當我們動態路由到user的時候, 需要使用變量userId, 我們可以在data中定義一個變量

<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <router-link to="/home" tag="button" replace active-class="active">首頁</router-link>
    <router-link to="/about" tag="button" replace active-class="active">關於</router-link>
    <router-link v-bind:to="'/user/'+userId" tag="button" replace active-class="active">用戶</router-link>

    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
    return {
      userId:"zhangsan"
    }
  }
}
</script>

在上面使用了v-bind:to="'/user/'+userId"路由到/user/:userId, 而userId是我們在組件中定義的一個變量.

第三步:來看看效果

我們看到當點擊用戶的時候, 瀏覽器的url路徑變為了/user/zhangsan.

第四步: 將參數傳遞到組件

我們希望在user組件中顯示, 歡迎{{用戶名}}來到用戶頁面, 如何實現呢?

要想實現這個效果, 需要使用到this.$route對象. 這是獲取路由信息的對象.

<template>
    <div>
      <h2>用戶界面</h2>
      <div>歡迎{{userId}},來到用戶界面</div>
    </div>
</template>

<script>
    export default {
        name: "User",
        data(){
          return {
            userId:this.$route.params.userId
          }
        }
    }
</script>

<style scoped>
</style>

可以看到在data屬性中,我們通過this.$route.params.userId 獲取到了路由連接中的userId參數.
然后在頁面通過語法糖的方式展示出用戶{{userId}}即可.
來看看效果:

如上圖所示, 我們看到userId變量被傳遞到了組件里面.

這里重點想要說的就是通過this.$route.params獲取變量.

我們也可以在頁面直接使用{{$route.params.userId}}獲取路由參數

<template>
    <div>
      <h2>用戶界面</h2>
      <div>歡迎{{userId}},來到用戶界面</div>
      <div>{{$route.params.userId}}</div>
    </div>
</template>

9. 認識路由的懶加載

首先為什么需要懶加載, 原因是, 當我們打包的時候, 會將所有的js文件,css進行打包, 打包到一個文件中, 然后在index.html頁面中引入這些js,css文件.我們來看一下效果
首先, 我們先將項目打包, 然后看看打包后的文件結構

npm run build

打包完成以后, 會生成一個dist的文件夾,里面就是打包后的文件

如上圖文件結構如下:

有一個index.html文件和static文件夾

  • index.html: 項目的主頁面入口
  • static文件夾: 存放的是css和js等靜態資源
    • css文件夾: 里面只有一個app.*.css文件, 這個文件是將所有的css整合到這里了
    • js文件夾: 該文件夾中有三個文件
      • app.*.js: 這個文件整合的是我們自己寫的業務邏輯相關的js代碼
      • manifest.*.js: 這個文件整合的是支撐我們的業務邏輯的底層支撐js代碼
      • vendor.*.js: 這個文件整合了使用到的所有外部組件的js代碼

一個項目可能有很多組件, 有自定義的組件, 有引入的外部組件, 那就會有很多js, css代碼, 最終全部通過index.html加載進來, 這樣在首次加載的時候速度就會很慢. 所以, 我們需要使用懶加載, 來提高首次加載速度.

什么是懶加載呢?

使用時才加載, 就是懶加載, 而不是一次性全部加載進來

怎樣才能做到懶加載呢?

把不同的路由對應的組件分隔成不同的代碼塊, 而不是統一全部加載到app.*.js文件中,當路由被訪問的時候才加載對應的js文件, 這樣會更加高效

如何實現懶加載呢?

在路由定義的時候, 是有懶加載的方式

原來定義路由是這樣的

import Home from '../components/Home';
import About from "../components/About";
import User from "../components/User"

/*
 * 第一步: 安裝插件
 * vue-router是一個插件, 所以, 我們需要使用vue.use(插件名) 來安裝插件
 */
Vue.use(Router)

/*
 * 第二步: 構建VueRouter對象
 * 在VueRouter中主要是配置路由和組件之間的映射關系的.
 */
const routes = [
  {
    path:"/",
    redirect: "/home"
  }, {
    path: "/home",
    component: Home
  },{
    path: "/about",
    component: About
  },{
    path: "/user/:userId",
    component: User
  }
]

懶加載需要使用一個匿名函數來import, 表示使用的時候在import引入. 一個懶加載在打包的時候會單獨打包成一個js文件.

// 這里會引入你要導入的組件, 然后通過路由配置組件內容
const Home = () =>import('../components/Home');
const About = () => import('../components/About');
const User = () => import('../components/User');

/*
 * 第一步: 安裝插件
 * vue-router是一個插件, 所以, 我們需要使用vue.use(插件名) 來安裝插件
 */
Vue.use(Router)

/*
 * 第二步: 構建VueRouter對象
 * 在VueRouter中主要是配置路由和組件之間的映射關系的.
 */
const routes = [
  {
    path:"/",
    redirect: "/home"
  }, {
    path: "/home",
    component: Home
  },{
    path: "/about",
    component: About
  },{
    path: "/user/:userId",
    component: User
  }
]

我們將代碼的引入方式改變了,這樣在打包的時候, 會將每一個import進來的文件打包成一個單獨的js文件. 如下圖所示:

和之前相比, 多了3個文件, 因為使用了三個懶加載組件.

二. vue-router的嵌套

嵌套路由是一個很常見的功能, 比如主業引入了組件Home, 我們在Home里面引入了banner圖組件. 這樣就是組件的嵌套.

我們的路由映射規則是: 一個路徑映射一個組件. 訪問兩個路徑, 會分別渲染兩個組件.

下面來實現嵌套路由,

第一步: 創建組件,創建兩個組件

HomeBanner.vue組件

<template>
    <div>
      <h2>首頁banner圖</h2>
      <ul>
        <li>banner圖1</li>
        <li>banner圖2</li>
        <li>banner圖3</li>
        <li>banner圖4</li>
      </ul>
    </div>
</template>

<script>
    export default {
        name: "HomeBanner"
    }
</script>

<style scoped>

</style>

HomeNews.vue組件

<template>
    <div>
      <h2>首頁新聞</h2>
      <ul>
        <li>第一條新聞</li>
        <li>第二條新聞</li>
        <li>第三條新聞</li>
        <li>第四條新聞</li>
      </ul>
    </div>
</template>

<script>
    export default {
        name: "HomeNews"
    }
</script>

<style scoped>

</style>

第二步: 創建組件路由

我們要在Home也添加子路由, 需要在路由里面增加一個children屬性配置.

//引入子路由-使用懶加載的方式進行加載
const HomeBanner = ()=>import('../components/HomeBanner');
const HomeNews = () => import('../components/HomeNews');

{
    path: "/home",
    component: Home,
    children: [{
      path:'',
      redirect: 'HomeNew'
    },{
      path: 'HomeNew', //注意: 這里面沒有/
      component: HomeNews
    },{
      path: 'HomeBanner',
      component: HomeBanner
    }]
  }

里面的路徑依然是有兩個部分:

  • 一個是path路由路徑: 這里需要注意的是,不需要在路徑名的前面加/
  • 另一個是component: 指定組件名稱

我們要在Home頁面展示子組件, 因此需要將子組件的展示放在頁面上

<template>
  <div>
    <h2>這是Home主頁</h2>
    <div>歡迎來到home主頁</div>
    <router-link to="/home/homeNew">新聞</router-link>
    <router-link to="/home/homeBanner">Banner圖</router-link>
    <router-view></router-view>

  </div>
</template>

完成以后,效果如下:

三. vue-router參數傳遞

vue-router參數傳遞有兩種方式: 第一種是: param的方式. 第二種是: query的方式

1. param方式

這種方式在動態路由的時候有提到過. 下面來回顧一下:

第一步: 創建一個User.vue組件

<template>
    <div>
      <h2>用戶界面</h2>
    </div>
</template>

<script>
    export default {
        name: "User",
    }
</script>

<style scoped>
</style>

第二步:創建動態路由的時候, 定義變量

const routes = [
  {
    path: "/user/:userId",
    component: User
  }
]

我們定義了一個user/:userId, 這樣的動態路由. 路由可以是/user/zhangsan, 或者/user/lisi

第三步: 在App.vue中定義動態路由跳轉的組件

<template>
  <div id="app">
    <!-- 定義兩個路由鏈接 -->
    <router-link to="/home" tag="button" replace active-class="active">首頁</router-link>
    <router-link to="/about" tag="button" replace active-class="active">關於</router-link>
    <!-- 定義動態路由 -->
    <router-link v-bind:to="'/user/' + userId" tag="button" replace active-class="active">用戶</router-link>

    <!-- 展示組件內容 -->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
    return {
      userId:"zhangsan"
    }
  }
}
</script>
<style>
    .active {
      color:red;
    }
</style>

這里面userId是變化的部分. 通過bind事件動態將userId綁定到path路徑中

<router-link v-bind:to="'/user/' + userId" tag="button" replace active-class="active">用戶</router-link>

第四步: 修改User.vue組件

首先, 在User.vue腳本中獲取傳遞過來的路由參數. 我們使用[this.$route.params.變量名]的方式獲取路徑參數

<script>
    export default {
        name: "User",
        data() {
          return {
            userId: this.$route.params.userId
          }
        }
    }
</script>

然后使用語法糖, 將變量顯示出來

<template>
    <div>
      <h2>用戶界面</h2>
      <div>歡迎{{userId}},來到用戶界面</div>
      <div>{{$route.params.userId}}</div>
    </div>
</template>

也可以直接使用使用{{$route.params.userId}}的寫法

以上是使用參數的方式傳遞變量.

2. query方式

第一步: 配置路由的時候是普通配置: /user
第二步:傳遞的方式使用query的方式, query是一個對象,
第三步:傳遞后形成的路徑是:/user?useId=2, /user?userId=9

下面來舉個案例:

第一步: 創建一個組件Profile.vue

<template>
    <div>
      <h2>這是一個Profile組件</h2>
    </div>
</template>

<script>
    export default {
        name: "Profile"
    }
</script>

<style scoped>

</style>

第二步: 配置路由

// 配置路由相關的信息
// 導入vue和vue-router
import Vue from 'vue'
import Router from "vue-router";
// 這里會引入你要導入的組件, 然后通過路由配置組件內容
// 懶加載
const Profile = () => import('../components/Profile')

/*
 * 第一步: 安裝插件
 * vue-router是一個插件, 所以, 我們需要使用vue.use(插件名) 來安裝插件
 */
Vue.use(Router)

/*
 * 第二步: 構建VueRouter對象
 * 在VueRouter中主要是配置路由和組件之間的映射關系的.
 */
const routes = [
  {
    path: "/profile",
    component: Profile
  }
]
const router = new Router({
  // 這里配置的是路由和組件的映射關系, 是一個數組.
  routes,
  mode: "history",
  linkActiveClass: "active"
})


/*
 * 第三步: 將VueRouter對象導出
 *
 */
export default router

第三步: 渲染組件

 <!-- 配置動態路由 -->
    <router-link v-bind:to="{path:'/profile', query:{name:'lily',sex:'男',age:12}}" tag="button" replace active-class="active">檔案</router-link>

通常我們定義路由是這樣定義的

 <!-- 配置動態路由 -->
    <router-link to="/profile" tag="button" replace active-class="active">檔案</router-link>

但是, 這樣路由就固定寫死了, 所以肯定不行, 我們需要給他一個變量, 使用v-bind:to
然后就有了下面這個寫法

v-bind:to="{path:'/profile', query:{name:'lily',sex:'男',age:12}}"

第四步: 來看一下效果

我們看到路徑上帶了?參數.

http://localhost:8080/profile?name=lily&sex=%E7%94%B7&age=12

第五步: 在Profile組件中獲取參數

獲取參數有兩種方式

  1. 直接語法糖方式獲取{{$route.query.***}}
  2. 在腳本中獲取:this.$route.query.***

如下采用方式一獲取參數:

<template>
    <div>
      <h2>這是一個Profile組件</h2>
      <h4>{{$route.query}}</h4>
      <h4>{{$route.query.name}}</h4>
      <h4>{{$route.query.age}}</h4>
      <h4>{{$route.query.sex}}</h4>
    </div>
</template>

第六步: 查看效果

四. vue-router導航守衛

導航守衛, 聽者特別的高大上. 到底是什么意思呢?
在之前的html頁面, 如果我們想要實現title跟着頁面變, 那么只需要設置html頁面的title屬性就可以了.
在vue中, 只有一個index.html頁面,如何實現title的改變呢?
有兩種方法:

  • 第一種, 使用生命周期函數created().
  • 第二種: 使用全局導航守衛

1. 使用生命周期實現頁面更新title屬性

組件的生命周期, 這在之前有提到過. 子組件的生命周期中有很多函數, 比如created(), mouted(), updated(), 我們可以在他的生命周期增加處理邏輯.
,我們可以在created()方法中增加處理邏輯.

<template>
  <div>
    <h2>這是Home主頁</h2>
    <div>歡迎來到home主頁</div>
    <router-link to="/home/homeNew">新聞</router-link>
    <router-link to="/home/homeBanner">Banner圖</router-link>
    <router-view></router-view>

  </div>
</template>

<script>
    export default {
        name: "Home",
      created() {
          document.title="首頁"
      }
    }
</script>

<style scoped>
</style>

我們看到在script中增加一個created方法. 並指定了title名稱.

<script>
    export default {
        name: "Home",
      created() {
          document.title="首頁"
      }
    }
</script>

來看一下效果

這樣有什么問題? 假如現在有100個頁面, 我們需要在100個頁面中都增加created()函數. 雖然可以實現功能,但似乎有些麻煩, 有沒有可以統一修改的辦法呢?我們可以使用全局導航守衛實現

3.2. 使用全局導航守衛的方式更新title屬性

什么是導航守衛?

來看看官網的解釋: https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
正如其名,vue-router 提供的導航守衛主要用來通過跳轉或取消的方式守衛導航。有多種機會植入路由導航過程中:全局的, 單個路由獨享的, 或者組件級的。

  • 這里的導航,指的就是路由的跳轉或者取消跳轉
  • 守衛指的是: 有很多鈎子方法, 允許我們在某一個過程中植入代碼邏輯.
  • 常見的導航守衛有: 全局導航守衛(包括三個: 全局前置守衛, 全局解析守衛, 全局后置守衛), 路由獨享導航守衛, 組件內的守衛.

下面老看看[全局前置守衛]

1. 使用 router.beforeEach 注冊一個全局前置守衛

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

當一個導航觸發時,全局前置守衛按照創建順序調用。
守衛是異步解析執行,此時導航在所有守衛 resolve 完之前一直處於 等待中。

我們來看看router.beforeEach的實現.

點擊beforeEach進入到router.ts文件, 可以看到里面定義了router.beforeEach()方法的定義

beforeEach(guard: NavigationGuard): Function

這是一個函數, 方法有一個入參, 參數是一個方法, 參數名是guard. 方法體是NavigationGuard. 我們來看看NavigationGuard的實現邏輯

export type NavigationGuard<V extends Vue = Vue> = (
  to: Route,
  from: Route,
  next: NavigationGuardNext<V>
) => any

NavigationGuard也是一個函數, 並且這個函數繼承自Vue. 函數有三個入參: to, from, next. 方法實現是any.

所以,我們要想重寫beforeEach()方法. 那么參數就要定一個方法, 並且方法里有三個入參. 方法如下:

router.beforeEach(function(to, from, next) {

})

除了這種方法, 我們還可以使用箭頭函數

router.beforeEach((to, from, next) =>{

})

兩種都是可以的.

接下來, 看看函數中三個參數都是什么含義呢?

  • to: 是一個router對象, 含義是導航到的目標路徑.
  • from: 是一個router對象, 含義是當前導航正要離開的路由.
  • next: 是一個函數, 這是一個鈎子函數. 一定要調用該方法來 resolve 這個鈎子

函數實現的部分, 一定要調用next()方法. 表示導航繼續向下執行. 如果不調用next(), 那么后面的函數將不會被解析或者執行.
也就是說, 代碼這至少是這樣的

router.beforeEach((to, from, next) =>{
    next();
})

確保 next 函數在任何給定的導航守衛中都被嚴格調用一次。它可以出現多於一次,但是只能在所有的邏輯路徑都不重疊的情況下,否則鈎子永遠都不會被解析或報錯。

2. 在路由index.js文件中增加元數據屬性, 並設置title屬性值

const routes = [
  {
    path:"/",
    redirect: "/home",
  }, {
    path: "/home",
    component: Home,
    children: [{
      path:'',
      redirect: 'HomeNew'
    },{
      path: 'homeNew', //注意: 這里面沒有/
      component: HomeNews
    },{
      path: 'homeBanner',
      component: HomeBanner
    }],
    meta: {
      title: "首頁"
    }
  },{
    path: "/about",
    component: About,
    meta: {
      title: "關於"
    }
  },{
    path: "/user/:userId",
    component: User,
    meta: {
      title: "用戶"
    }
  },{
    path: "/profile",
    component: Profile,
    meta: {
      title: "檔案"
    }
  }
]

增加的元數據內容如下

    meta: {
      title: "檔案"
    }

3. 后面在全局導航路由中設置title屬性

router.beforeEach((to, from, next) => {
  console.log(to)
  console.log(from)
  document.title = to.matched[0].meta.title
  next()
})

我們通過打印to和from對象, 發現他們確實都是路由對象, 我們可以通過matched中的第0個元素,獲取到meta中的內容.

我們看到其實to下面和matched平級的也有一個meta屬性, 但是這個屬性在某些情況下值是空的. 所以, 我們還通過matched的第一個元素來獲取meta對象

以上就是全局前置導航守衛的用戶, 后置導航守衛等其他守衛, 用法與其相似, 可以查看官方文檔: https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

前置守衛也好, 后置守衛也好, 都是路由組件的鈎子函數, 通過鈎子函數, 我們可以在對應的生命周期織入業務邏輯.

理解守衛的含義


全局前置守衛, 全局解析守衛, 全局后置守衛: 定義在路由文件中, 表示對所有路由都有效

路由獨享守衛: 可以在路由配置上直接定義 beforeEnter 守衛:

組件內的守衛:你可以在路由組件內直接定義以下路由導航守衛, 有效范圍是某個組件.

                       常見的組件內守衛: beforeRouteEnter(進入路由前),beforeRouteUpdate (更新路由前)),beforeRouteLeave(離開路有前)

五. keep-alive

我們有首頁, 關於, 用戶, 檔案. 首頁下面有兩個按鈕[新聞],[消息]
當點擊首頁的[消息], 然后切換到關於頁面, 再回到首頁的時候, 我們希望能夠繼續展示[消息]的內容
默認是不會保留操作的記憶的. 下次回來直接到[首頁->新聞], 使用keep-alive就可以有保留記憶的效果了

為什么每次回到首頁, 都會展示[首頁->新聞]的內容呢?

原因是每次回到首頁都會創建一個新的Home組件.
我們來驗證每次回到首頁都會重新創建一個新的組件. 來看看vue組件的生命周期. 其實Vue對象本身也是一個大組件

如上圖所示:在vue整個生命周期, 他有很多掛載函數, 比如:beforeCreate(創建組件之前), created(創建組件后), beforeMount(掛載組件之前), mounted(掛載組件之后),
beforeUpdate(組件值更新之前),updatd(組件值更新之后),beforeDestroy(組件銷毀之前),destroyed(組件銷毀之后)

1. 驗證, 是否銷毀了原來的組件呢?

我們只需要驗證created()和destroyed()兩個函數是否每次跳走都會被執行一遍.

<script>
    export default {
        name: "Home",
      created() {
          document.title="首頁"
        console.log("created home")
      },
      destroyed() {
        console.log("destroyed home")
      }
    }
</script>

如上所示:定義了創建函數和銷毀函數, 下面來看看當跳走的時候, 是不是會執行這兩個函數.

如上圖所示: 當離開首頁,就會執行destroyed函數, 當進入首頁, 就會執行created函數. 說明每次確實都在創建新的組件

2. 如何才能讓組件有記憶,而不是每次都重新創建呢?

如果想要實現路由跳轉走以后, 返回來不需要重新創建組件, 我們可以使用keep-alive, 他的用法很簡單
在組建展示的位置增加 標簽

    <keep-alive>
        <router-view></router-view>
    </keep-alive>

這樣調走再跳回來就不會重新創建組建了, 來看看效果

我們看到只有第一次創建了home組件, 后來路由調走, 組件並沒有被銷毀.

3.案例: 實現從[首頁->banner圖]跳走后, 在跳回來, 依然定位在[首頁->banner圖]的位置

跳走, 在跳回來, 這實際上是在控制路由.我們可以讓路由調走之前記住當前組件的路由. 要想實現這個功能,需要了解一下幾個鈎子函數:

  1. activated: 路由激活時觸發
  2. deactivated: 路由取消激活時觸發

先來看這兩個: 這兩個函數生效的條件是 : 設置了<keep-alive>組件才有效. 也就是說, 組件離開時不銷毀.
我們在home組件中增加兩個方法

    activated() {
      // 路由激活
      console.log("activated home")
    },
    deactivated() {
      // 路由取消激活
      console.log("deactivated home")
    },

然后來看看效果: 我們發現跳到home路由, 觸發activated方法, 離開home路由, 觸發deactivated方法.
如何記住路由呢? 我們可以定義一個變量來記住調走前的路由.

this.$route.path : 他是獲取當前激活的路由

要想實現這個功能, 還需要使用到路由守衛.
路由守衛有三種類型

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

我們選擇使用最后一個beforeRouteLeave, 在離開當前路由的時候, 記錄下離開前的路由.
代碼實現如下:

1. 在home組件增加兩個方法, 一個是activated組件激活的時候重定向路由, 另一個是beforeRouteLeave組件離開前記錄離開前的路由

activated() {
        // 路由激活, 路由到path路徑
        this.$router.push(this.path)
      },
      beforeRouteLeave(to, from, next) {
        console.log("離開home前的路徑 "+this.path)
        this.path = this.$route.path;
        next()
      }

2. 看一下運行的效果:

以上就是keep-alive的內容, 這里重要要有一個概念, 組件調走以后是會銷毀原來的組件的.如果我們不想它被銷毀, 那么可以使用<keep-alive>組件實現.

4. keep-alive 的屬性

keep-alive有兩個屬性: 一個是include, 另一個是exclude

  • include: 字符串或者正則表達式, 只有匹配的組件才會被緩存
  • exclude: 字符串或者正則表達式, 匹配到的組件不會被緩存.

比如,我們整個組件都希望每次加載會緩存, 但是檔案組件特殊, 希望他每次都被銷毀, 重新創建.

第一步: 在Profile組件中增加created和destroy事件

created() {
          console.log("Profile created")
        },
      destroyed() {
        console.log("Profile destroyed")
      }

第二步: 在<keep-alive>中配置排除Profile組件

<keep-alive exclude="Profile">
      <router-view></router-view>
    </keep-alive>

這樣, Profile組件就不會被緩存了

第三步: 演示效果


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM