[TOC]
>[success] # 路由詳解(二)----進階篇
<br>
>[success] ## 路由組件傳參(.vue文件路由跳頁傳參)
我們如果在一個頁面中 **需要根據路由獲取參數, 在頁面中用參數做一些邏輯處理**,首先可以使用 **路由** 的實例 **$route** 來 **獲取路由的參數** , 但是這樣有一個 **缺點** ,我們的 **頁面** 和 **路由** 進行了 **高度耦合** ,為了 **解耦** ,使我們的組件能在最大程度上 **復用** , 我們就會用到 **路由組件傳參** , **路由組件傳參** 有 **3種** 模式:
>[success] ### 布爾模式
**布爾模式**: 適用于在 **路由對象** 中有 **路由參數(例如:path: '/about/:name')** 的 **路由配置**。
**場景** : 我有一個 **demo.vue頁面** 中使用 **$route.params.name** 在頁面中進行 **邏輯判斷** ,這樣 **耦合性就很高** ,只能用 **$route.params.name** 方式判斷,其他頁面想使用這個 **demo.vue頁面** 也只能通過 **路由傳參** 的方式使用該頁面,如果使用了 **布爾模式** , **demo.vue頁面** 中就可以在 **props** 中設置 **name** 屬性,頁面中可以直接使用 **name** 屬性,而不是這樣使用 **$route.params.name** ,這樣其他頁面使用 **demo.vue頁面** 作為 **組件** 使用可以直接寫 :
~~~
<demo :name="'小明'" />
~~~
這樣的話 **demo.vue** 的 **name參數** 即可以用 **路由params方式傳參** ,也可以用 **父傳子的props方式傳參**。
2. **布爾模式使用方法有2種如下**:
>[success] #### 字符串模板拼接方式
1. 首先在 **router.js文件** 對應頁面的 **路由對象** 中設置 **props屬性** 置為 **true** , **$route.params 將會被設置為組件屬性**,所以 **query的傳參方式是不好用的** , **路由對象的path屬性** 一定要是 **屬性拼接** ( **path:'/about/:name/:age'** )或者 **params** 的寫法。
**router.js**
~~~
import Home from '@/views/Home'
export default [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about/:name/:age', // 1. 路由參數
name: 'about',
component: () => import('@/views/About.vue'),
props: true // 2. 開啟布爾模式
}
~~~
2. 在 **Home.vue** 頁面通過 **點擊事件** 跳轉 **About.vue** 頁面,給 **path屬性** 用 **字符串模板** 進行 **參數拼接** 的方式進行 **傳參** 。
**Home.vue**
~~~
<template>
<div>
<button @click="handleClick">點擊我跳轉About頁面</button>
</div>
</template>
<script>
export default {
data(){
return{
name: '小明',
age: '18'
}
},
methods: {
handleClick(){
this.$router.push({
path: `/about/${ this.name }/${ this.age }`, // 3. 向跳轉的頁面傳參
})
}
}
}
</script>
~~~
3. 在 **使用參數的頁面(About.vue)中** 用 **props設置屬性** ,可以給 **屬性** 設置 **類型(支持多種類型,數組形式即可)、默認值** ,這種方式可以直接在使 **template標簽** 中使用 **name** 屬性,不用像之前用 **$route** 的形式使用 **參數** ,具體寫法如下:
**About.vue**
~~~
<template>
<div>
我的名字叫{{ name }},今年{{ age }}歲
</div>
</template>
<script>
export default{
props: { // 4. 設置參數在頁面中使用
name: { // 參數名稱
type: String, // 類型,支持數組形式例如:type: [String, Number]
default: '我是默認值的name' // 默認值
},
age: { // 參數名稱
type: String, // 類型,支持數組形式例如:type: [String, Number]
default: '10' // 默認值
},
}
}
</script>
~~~
>[success] #### params方式
1. **params** 跟 **字符串模板拼接方式** 的不同點就是 **Home.vue** 頁面的傳參寫法不一樣。
**Home.vue**
~~~
handleClick(){
this.$router.push({ // params方式傳參
name: 'about',
params: { // 向跳轉的頁面傳參
name: this.name,
age: this.age
}
})
~~~
>[success] ### 對象模式
**對象模式**:如果 **router.js** 中的 **路由對象** 的 **props** 是一個 **對象** , **props對象** 中的 **屬性** 為 **組件屬性** 。**當props是靜態的時候有用**。
**場景**: 也就是說使用 **對象模式** 時,該頁面會**有一個靜態寫死的props屬性(跟props的default屬性有一點像,但它不支持修改)**,而且不支持 **動態修改**,如果想修改,只能 **手動** 去 **router.js** 中對應的 **路由對象** 中 **修改props對象中的屬性**。
>[success] #### 對象模式使用方法
1. 在 **router.js** 中設置 **props屬性** 為 **對象形式**, 它的 **key** 作為 **組件屬性** , 如果 **props對象** 中 **不寫屬性** , 會顯示對應頁面中 **props對象** 中 **default** 屬性的默認值。
**router.js**
~~~
import Home from '@/views/Home'
export default [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'),
props: {
name: '小黑' // 這里不寫name屬性,就會顯示props中設置的默認值小明
}
}
]
~~~
2. **從 Home.vue 頁面通過點擊事件跳轉 About.vue 中**,強調一下,用 **對象模式傳參是無效的**。
**Home.vue**
~~~
<template>
<div>
<button @click="handleClick">點擊我跳轉About頁面</button>
</div>
</template>
<script>
export default {
methods: {
handleClick(){
this.$router.push({ // 方案1
name: 'about'
})
// this.$router.push({ // 方案2
// path: '/about'
// })}
}
}
</script>
~~~
3. **About.vue** 頁面也正常寫 **props對象屬性默認值、類型** 即可。
**About.vue**
~~~
<template>
<div>
{{ name }}
</div>
</template>
<script>
export default{
props: {
name: { // 參數名稱
type: String, // 類型,支持數組形式例如:type: [String, Number]
default: '小明' // 默認值
}
}
}
</script>
~~~
>[success] ### 函數模式
**函數模式** : **根據當前的路由傳入的參數,來做一些處理邏輯** ,從而 **設置我們傳入組件的屬性值**。這么說不太容易懂,簡單的說就是 **可以根據傳入的 params 或者 query 對象中的參數,在函數中進行判斷邏輯,最終給頁面傳出路由參數**。
**場景** : 有 **a.vue** 、 **b.vue** 、 **c.vue 3** 個頁面,**a** 和 **b頁面** 都要跳轉到 **c頁面** 并且用 **params** (或者 **query** )傳入 **name** 、 **alias屬性** ,**c頁面** 的 **路由對象** 的 **props函數** 中需要判斷 **name** 是否等于 **孫悟空** ,如果等于 **孫悟空** ,就把 **alias** 屬性值 **弼馬溫** 賦的給 **name**,這樣就做到了 **在路由層面直接處理要展示的值**,代碼如下:
<br/>
1. 在 **router.js** 中給 **c.vue** 設置 **props屬性** 為 **函數** ,它有一個 **回調參數route對象**, **route對象** 中可以取到 **當前路由對象信息,包括:params、query等**,并且通過 **route對象** 取到 **route.query.name** 來進行判斷是否等于 **孫悟空**, 如果等于 **孫悟空**,就用它的 **alias(別名)** **弼馬溫** 作為 **name** 展示。
**router.js**
~~~
export default [
{
path: '/',
name: 'a',
component: () => import('@/views/a.vue')
},
{
path: '/b',
name: 'b',
component: () => import('@/views/b.vue')
},
{
path: '/c',
name: 'c',
component: () => import('@/views/c.vue'),
// route代表當前路由對象(里面能取到params、query對象中的參數信息), return一個對象,
// 對象中的name屬性對應c頁面中props的name屬性,這里的name: route.query.name完全取決于
// 傳值的頁面使用哪種方式(query、params)傳值
props: route => ({ name: route.query.name === '孫悟空' ? route.query.alias : route.query.name })
}
]
~~~
**a.vue**
~~~
<template>
<div>
<p>a頁面</p>
<button @click="handleClick">跳轉c頁面</button>
</div>
</template>
<script>
export default {
methods: {
handleClick(){
this.$router.push({ // query方式傳參
path: '/c',
query: {
name: '孫悟空',
alias: '弼馬溫'
}
})
}
}
}
</script>
~~~
**b.vue**
~~~
<template>
<div>
<p>b頁面</p>
<button @click="handleClick">跳轉c頁面</button>
</div>
</template>
<script>
export default {
methods: {
handleClick(){
this.$router.push({ // query方式傳參
path: '/c',
query: {
name: '豬八戒',
alias: '豬剛鬣'
}
})
}
}
}
</script>
~~~
**c.vue**
~~~
<template>
<div>
我的名字叫{{ name }}
</div>
</template>
<script>
export default{
props: {
name: { // 參數名稱
type: String, // 類型,支持數組形式例如:type: [String, Number]
default: '墨跡嘴唐僧' // 默認值
}
}
}
</script>
~~~
>[success] ## HTML5 History模式
參考文章:[Vue番外篇 -- vue-router淺析原理](https://juejin.im/post/5bc6eb875188255c9c755df2#heading-2)
**vue-router** 默認 **hash模式** ,在 **URL** 中會有一個 **# 號** ,一般場景下,**hash** 和 **history** 都可以,除非你更在意顏值, **#號** 夾雜在 **URL** 里看起來確實有些不太美麗。
<br>
1. **hash** —— 即地址欄 **URL** 中的 **#號** (此 hash 不是密碼學里的散列運算)。
比如這個 **URL**: **http://www.abc.com/#/hello**, **hash** 的值為 **#/hello** 。它的特點在于: **hash** 雖然出現在 **URL** 中,但不會被包括在 **HTTP請求** 中,對后端完全沒有影響,因此 **改變 hash 不會重新加載頁面**。
2. **history** —— 利用了 **HTML5 History Interface** 中新增的 **pushState()** 和 **replaceState()** 方法。(需要特定瀏覽器支持)
這兩個方法應用于瀏覽器的歷史記錄棧,在當前已有的 **back** 、**forward**、**go** 的基礎之上,它們提供了對 **歷史記錄進行修改*** 的功能。只是當它們執行修改時,雖然改變了當前的 **URL**,但**瀏覽器不會立即向后端發送請求**。
因此可以說,**hash模式** 和 **history模式** 都屬于 **瀏覽器自身的特性**,**vue-router** 只是利用了這 **兩個特性**(通過調用瀏覽器提供的接口)來實現 **前端路由**。
3. **abstract模式** —— **abstract模式** 是使用一個 **不依賴于瀏覽器的瀏覽歷史虛擬管理后端**。
根據平臺差異可以看出,在 **Weex 環境** 中只支持使用 **abstract模式** 。 不過,**vue-router 自身會對環境做校驗,如果發現沒有瀏覽器的 API,vue-router 會自動強制進入 abstract 模式**,所以 在 **使用 vue-router 時只要不寫 mode 配置即可,默認會在瀏覽器環境中使用 hash 模式,在移動端原生環境中使用 abstract 模式**。 (當然,你也可以明確指定在所有情況下都使用 **abstract 模式** )
>[success] ### History模式配置
1. 默認 **router.js** 中的 **mode(模式)** 屬性是 **hash**,在 **RRL** 中會有 **# 符號** ,例如: **[http://localhost:8080/#/](http://localhost:8080/#/)**
~~~
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router'
Vue.use(Router)
export default new Router({
mode: 'hash', // 默認哈希模式
routes
})
~~~
2. 但是在 **正式環境** 時候,我們不希望看到這個 **# 符號**,把 **mode** 修改成 **history模式**,它是使用的 **history**的一些 **api** 來做頁面的 **無刷新跳轉**
~~~
export default new Router({
mode: 'history', // history模式
routes
})
~~~
3. 使用 **history模式** ,所有 **匹配不到靜態資源的頁面** 的 **URL** 都會指向 **index.html** ,這樣會有一個問題 **當你匹配不到靜態資源,前端路由也匹配不到組件的話**,這時候就會有問題了,所以需要在 **router.js路由列表** 里 **最后(為什么是最后,因為路由列表是有優先原則的,順序的從上到下的讀取順序,如果兩個組件name都叫同樣的名字,它會使用上面的組件)** 添加一項 **404頁面** 的 **路由對象**。
**router.js**
~~~
{ // 一定要寫在路由列表的最后面
path: '*', // * 符號,代表匹配任何的路徑
component: () => import('@/views/error_404.vue')
}
~~~
4. 當然 **history模式** 也是需要 **后端配合**
后期視頻講到部署時候這里補寫上
>[success] ## 導航守衛
**導航守衛** :在 **路由發生跳轉** 到 **導航結束** 這段時間能做一些相應的 **邏輯處理** 。
| 導航守衛名稱 | 導航守衛方法 | 參數 | 應用場景 |
| --- | --- | --- | --- |
| 全局前置守衛 | router.beforeEach | **參數** 是一個 **回調函數** ,router.beforeEach((to, from, next) => { })<br> 1. **to** :代表 **要跳轉的頁面** <br> 2. **from** :代表 **從哪個頁面跳轉來** <br> 3. **next** : **next** 是一個函數,執行 **next()** 代表 **繼續跳轉要去的頁面** | **判斷是否登錄** ,如果 **未登錄跳轉到登錄頁面** , **已登錄的話就跳轉想去的頁面** |
| 全局解析守衛 | router.beforeResolve | **參數** 跟 **router.beforeEach** 相同 | 后期補上。暫時不知道應用場景 |
| 全局后置鉤子 | router.afterEach | **參數** 里沒有 **next** 其他 **參數** 都跟 **router.beforeEach** 相同 | **跳轉頁面完畢** 之后 **關閉loading** |
| 路由獨享守衛 | beforeEnter | **參數** 跟 **router.beforeEach** 相同 | 能對 **單獨的路由頁面進行邏輯處理** |
| 組件內的守衛 | 1. beforeRouteEnter | **參數** 跟 **router.beforeEach** 相同 | 1. **進入頁面時觸發** <br>2. 不能 **獲取組件實例this** ,如果想使用 **this** ,用 **next(vm => {})** 方式。 |
| | 2. beforeRouteLeave | **參數** 跟 **router.beforeEach** 相同 | 1. **離開頁面時觸發** <br>2. 可以 **獲取組件實例this**|
| | 3. beforeRouteUpdate | **參數** 跟 **router.beforeEach** 相同 | 1. **路由發生變化,組件被復用時觸發** <br>2. 可以 **獲取組件實例this** |
>[success] ### 全局前置守衛
**場景** :比如在 **跳轉一個頁面時要判斷這個用戶有沒有登錄過,如果沒有登錄過要跳轉到登錄頁面,如果登錄過就跳轉要跳轉的頁面** ,以及做一些 **路由權限** 的 **判斷處理** 。
1. 你可以使用 **router.beforeEach** 注冊一個 **全局前置守衛** ,在里面寫 **判斷跳轉頁面是否登錄的邏輯**,代碼如下:
**router / index.js**
~~~
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router'
Vue.use(Router)
const router = new Router({ routes })
// 這個登錄狀態是由接口來返回的,這里只是模擬一下登錄跳轉邏輯
const HAS_LOGINED = false
// 注冊全局前置守衛
router.beforeEach((to, from, next) => {
if(to.name !== 'login'){ // 訪問的頁面不是 【登錄頁面】
// 【已登錄】就繼續跳轉要去的頁面
if(HAS_LOGINED) next()
// 【未登錄】跳轉到login頁面。next方法里面傳入的參數跟$router.push同樣
else next({ name: 'login' })
} else { // 訪問的頁面是 【登錄頁面】
// 【已登錄】就跳轉到首頁
if(HAS_LOGINED) next({ name: 'home' })
// 【未登錄】跳轉到登錄頁
else next()
}
})
export default router
~~~
**router / router.js**
~~~
import Home from '@/views/Home'
export default [
{
path: '/',
name: 'home',
alias: '/home_page',
component: Home,
},
{
path: '/login',
name: 'login',
component: () => import('@/views/login.vue'),
},
{
path: '*', // * 符號,代表匹配任何的路徑
component: () => import('@/views/error_404.vue')
}
]
~~~
**login.vue**
~~~
<template>
<div>登錄頁面</div>
</template>
~~~
>[success] ### 全局解析守衛
**全局解析守衛** :在 **導航被確認之前** ,**異步路由組件被解析之后**, **解析守衛** 就被調用。
1. 問:什么叫 **導航被確認之前?** 答: **所有導航鉤子(導航守衛)都結束,導航就是被確定了** 。
2. 問: 什么叫 **路由組件被解析之后** ? 答:頁面渲染后。
**用法**:
~~~
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router'
import { Loading } from 'element-ui';
// 注冊路由
Vue.use(Router)
// vue-router實例
const router = new Router({ routes })
// 全局解析守衛
router.beforeResolve((to, from, next) => {
// 邏輯處理
next()
})
export default router
~~~
>[success] ### 全局后置鉤子
**場景** :我們在跳轉頁面時候要在 **全局前置守衛(router.beforeEach)** 中執行 **打開 Loading 加載提示** ,跳轉完畢后需要在 **全局后置鉤子(router.afterEach)** 中 **關閉 Loading 加載提示** 。
**router / index.js**
~~~
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router'
import { Loading } from 'element-ui';
// 注冊路由
Vue.use(Router)
// vue-router實例
const router = new Router({ routes })
// 登錄狀態
const HAS_LOGINED = true
// loading實例
let loadingInstance = null
// 注冊全局前置守衛
router.beforeEach((to, from, next) => {
// 打開loading
loadingInstance = Loading.service({
lock: true, // 鎖定屏幕滾動
text: '拼命加載中... >_<', // 提示文字
spinner: 'el-icon-loading', // 自定義加載圖標類名
background: 'rgba(0, 0, 0, 0.7)' // loading背景顏色
})
// 是否登錄頁面跳轉邏輯
if(to.name !== 'login'){
if(HAS_LOGINED) next()
else next({ name: 'login' })
} else {
if(HAS_LOGINED) next({ name: 'home' })
else next()
}
})
// 注冊全局后置鉤子
router.afterEach((to, from) => {
// 關閉loading
loadingInstance && loadingInstance.close()
})
export default router
~~~
>[success] ### 路由獨享守衛
**路由獨享守衛** : 給 **指定的路由添加路由守衛** ,**單獨做邏輯處理**。
**用法**:
1. 在想要 **進行邏輯處理頁面** 的 **路由對象** 中寫上 **beforeEnter屬性** ,屬性的參數跟 **router.beforeEach** 相同。
~~~
import Home from '@/views/Home'
export default [
{
path: '/',
name: 'home',
alias: '/home_page',
component: Home
},
{
path: '/about',
name: 'about',
component: () => import('@/views/About.vue'),
beforeEnter: (to, from, next) => { // 在里面進行邏輯處理
if(from.nmae === 'home') alert('這是從home頁來的')
else alert('這不是從home頁來的')
next()
}
}
]
~~~
>[success] ### 組件內的守衛
每個 **組件** 都可以有 **3個鉤子** 包括: **beforeRouteEnter** 、 **beforeRouteLeave** 、 **beforeRouteUpdate**
**組件內的守衛** : 在 **組件進入(beforeRouteEnter)** 、 **組件離開(beforeRouteLeave )** 、 **組件改變(beforeRouteUpdate)** 頁面時觸發。
>[success] #### beforeRouteEnter
**beforeRouteEnter** : 渲染 **組件** 的 **對應路由** ,被確認前調用(跳轉 **對應頁面** 時 **beforeRouteEnter** 才會 **被調用** )
**用法** :
1. **Home.vue**
~~~
<template>
<div>
我是Home.vue頁面
</div>
</template>
<script>
export default {
beforeRouteEnter(to, from, next){ // 參數跟beforeEach方法一樣
next()
}
}
</script>
~~~
2. **需要注意**: **beforeRouteEnter** 是在 **路由觸發要進頁面的時候調用** ,此時 **頁面還沒有渲染** ,所以此時在 **beforeRouteEnter** 中 **訪問組件實例 this** 是無效的,如下:
~~~
<script>
export default {
data(){
return{
name: '星哥'
}
},
beforeRouteEnter(to, from, next){ // 參數跟beforeEach方法一樣
console.log(this.name) // Cannot read property 'name' of undefined 此時是取不到this實例的
next()
}
}
</script>
~~~
如果想 **訪問組件實例 this** 的話可以這樣寫:
~~~
<script>
export default {
data(){
return{
name: '星哥'
}
},
beforeRouteEnter(to, from, next){ // 參數跟beforeEach方法一樣
next(vm => { // vm是組件的實例
console.log(vm,'vm')
})
}
}
</script>
~~~
>[success] #### beforeRouteLeave
**beforeRouteLeave** :在頁面要離開時觸發 **beforeRouteLeave** 。 **補充一句 beforeRouteLeave** 可以 **訪問組件實例this** 。
用法:
1. 例如現在有個 **編輯頁面**,還沒 **編輯** 完,就要 **跳轉其他頁面** ,此時要彈出一個 **提示信息:您確定要離開嗎?** ,代碼如下:
**Home.vue**
~~~
<template>
<div>
我是Home.vue頁面
</div>
</template>
<script>
export default {
beforeRouteLeave(to, from, next) { // 參數跟beforeEach方法一樣
const leave = confirm('您確定要離開嗎?')
if(leave) next() // 確定離開
else next(false) // 取消離開
},
}
</script>
~~~
>[success] #### beforeRouteUpdate
**beforeRouteUpdate** : **路由發生變化,組件被復用時觸發 beforeRouteUpdate** 。**補充一句 beforeRouteUpdate** 可以 **訪問組件實例this** 。
**用法**:
1. 在 **router.js** 中配置好 **路由對象** 信息。
**router.js**
~~~
import Home from '@/views/Home'
export default [
{
path: '/',
name: 'a',
component: () => import('@/views/a.vue')
},
{
path: '/c/:name',
name: 'c',
component: () => import('@/views/c.vue'),
props: true // 開啟布爾模式
}
]
~~~
**a.vue**
~~~
<template>
<div>
<p>我是a頁面</p>
<button @click="handleClick">點擊我跳轉c頁面</button>
</div>
</template>
<script>
export default {
methods: {
handleClick() {
this.$router.push({ // params方式傳參
name: 'c',
params: {
name: '小a'
}
})
}
}
}
</script>
~~~
重點是下面這個 **c.vue** , 在 **c.vue** 中使用了 **c.vue** ,也就是應了上面那句話: **路由發生變化,組件被復用時觸發 beforeRouteUpdate** ,下面的 **c.vue** 中路由參數 **name發生變化** ,同時 **組件也被復用** 。
**c.vue**
~~~
<template>
<div>
<p>{{ name }}</p>
<p>我是c頁面</p>
<button @click="handleClick">點我還跳轉c頁面</button>
</div>
</template>
<script>
export default{
props: {
name: { // 參數名稱
type: String, // 類型,支持數組形式例如:type: [String, Number]
default: '小c' // 默認值
}
},
methods: {
handleClick() {
this.$router.push({ // params方式傳參
name: 'c',
params: {
name: '小c2',
}
})
}
},
beforeRouteUpdate(to, from, next){
console.log(to.name, from.name) // c c
next()
}
}
</script>
~~~
>[success] ### 完整的導航解析流程(vue-router鉤子生命周期執行順序)
1. 導航被觸發(**this.$router.push** 或者 **修改URL** 觸發)
2. 在失活的組件(**即將離開的頁面組件**)里調用 **beforeRouteLeave** 守衛
3. 調用全局的前置守衛 **beforeEach**
4. 在重用的組件里調用 **beforeRouteUpdate**
5. 調用路由獨享的守衛 **beforeEnter**
6. 解析異步路由組件
7. 在被激活的組件里(**即將計入的頁面組件**)里調用 **beforeRouteEnter**
8. 調用全局的解析守衛
9. 導航被確認
10. 調用全局的后置守衛 **afterEach**
11. 觸發 **DOM** 更新
12. 用創建好的實例調用 **beforeRouteEnter** 守衛中傳給 **next** 的**回調函數(next (vm => {}))**。
>[success] ## 路由元信息
**路由元信息**:每個 **路由對象** 都有一個 **meta** 屬性,里面可以 **配置一些權限** 。
**場景**:例如 **title** , 默認的 **title** 是在 **index.html** 文件中寫死的,如果想每個頁面有不同的 **title** , **home** 的 **title** 是 【首頁】, **about** 的 **title** 是 【關于】,就要在對應的 **路由對象** 中配置 **meta** 配置。
**用法** :
1. 在 **路由列表** 中的 **路由對象** 上設置 **title** 信息
**router/router.js**
~~~
import Home from '@/views/Home'
export default [
{
path: '/',
name: 'home',
alias: '/home_page',
component: Home,
meta: { // 配置title信息
title: '首頁'
}
},
{
path: '/about',
name: 'about',
component: () => import('@/views/About.vue')
},
{
path: '*', // * 符號,代表匹配任何的路徑
component: () => import('@/views/error_404.vue')
}
]
~~~
2. 在 **全局前置守衛(router.beforeEach)** 中通過 **setTitle方法** 動態設置 **title**
**router/index.js**
~~~
import Vue from 'vue'
import Router from 'vue-router'
import routes from './router'
import { Loading } from 'element-ui';
import { setTitle } from '@/lib/util'
// 注冊路由
Vue.use(Router)
// vue-router實例
const router = new Router({ routes })
// 登錄狀態
const HAS_LOGINED = true
// loading實例
let loadingInstance = null
// 注冊全局前置守衛
router.beforeEach((to, from, next) => {
// 動態設置title
to.meta && setTitle(to.meta.title)
// 打開loading
loadingInstance = Loading.service({
lock: true, // 鎖定屏幕滾動
text: '拼命加載中... >_<', // 提示文字
spinner: 'el-icon-loading', // 自定義加載圖標類名
background: 'rgba(0, 0, 0, 0.7)' // loading背景顏色
})
// 是否登錄頁面跳轉邏輯
if(to.name !== 'login'){
if(HAS_LOGINED) next()
else next({ name: 'login' })
} else {
if(HAS_LOGINED) next({ name: 'home' })
else next()
}
})
// 注冊全局后置鉤子
router.afterEach((to, from) => {
// 關閉loading
loadingInstance && loadingInstance.close()
})
export default router
~~~
3. 引入 **setTitle方法**, 如果 **路由對象** 中沒有設置 **title屬性**的,會自動設置為 **admin** 。
**lib/util.js**
~~~
export const setTitle = (title) => {
window.document.title = title || 'admin' // 默認title
}
~~~
>[success] ## 過渡動效
**過渡動效**: 頁面的 **切換** 就是 **一個組件注銷,一個組件加載** ,過渡動效就是 **給頁面跳轉時添加過渡效果** 。
用法:
1. 在 **app.vue** 頁面中用 **transition-group** 標簽包裹 **多個視圖組件(router-view )** ,如果只有一個 **視圖組件** , 用 **transition** 標簽包裹,需要給視圖組件設置 **key** ,**transition-group**標簽設置 **name** ,這里的 **name** 對應下面的 **style** 標簽 **class** 類名前綴。
~~~
<template>
<div id="app">
<!-- 多個視圖組件(router-view)用transition-group標簽包裹,如果只有一個視圖組件用transition標簽包裹即可。 -->
<transition-group name="router">
<router-view key="default"/>
<router-view key="email" name="email"/>
<router-view key="tel" name="tel"/>
</transition-group>
</div>
</template>
<style lang="scss">
// 頁面進入的效果
.router-enter{ // 初始化加載狀態
opacity: 0;
}
.router-enter-active{ // 設置組件從無到有的過程
transform: opacity 1s ease;
}
.router-enter-to{ // 頁面完全顯示之后
opacity: 1;
}
// 頁面離開的效果
.router-leave{ // 初始化加載狀態
opacity: 1;
}
.router-leave-active{ // 設置組件從無到有的過程
transform: opacity 1s ease;
}
.router-leave-to{ // 頁面完全顯示之后
opacity: 0;
}
</style>
~~~
2. 或者也可以寫成 **動態類名**, **根據路由參數來決定**,如果你想為 **某個頁面設置特定的動畫效果** ,可以使用這種方式,寫法如下:
~~~
<template>
<div id="app">
<!-- 多個視圖組件(router-view)用transition-group標簽包裹,如果只有一個視圖組件用transition標簽包裹即可。 -->
<transition-group :name="routerTransition">
<router-view key="default"/>
<router-view key="email" name="email"/>
<router-view key="tel" name="tel"/>
</transition-group>
</div>
</template>
<script>
export default{
data(){
return{
routerTransition: '' // 類名
}
},
watch:{
'$route'(to){ // 監聽路由參數變化改變過渡效果的類
to.query && to.query.transitionName && (this.routerTransition = to.query.transitionName)
}
}
}
</script>
<style lang="scss">
// 頁面進入的效果
.router-enter{ // 初始化加載狀態
opacity: 0;
}
.router-enter-active{ // 設置組件從無到有的過程
transform: opacity 1s ease;
}
.router-enter-to{ // 頁面完全顯示之后
opacity: 1;
}
// 頁面離開的效果
.router-leave{ // 初始化加載狀態
opacity: 1;
}
.router-leave-active{ // 設置組件從無到有的過程
transform: opacity 1s ease;
}
.router-leave-to{ // 頁面完全顯示之后
opacity: 0;
}
</style>
~~~
>[success] ## 數據獲取
后期補充
>[success] ## 滾動行為
后期補充
>[success] ## 路由懶加載
后期補充
>[warning] ## 后期補充(該項后期刪除)
1. 看vue-router文檔
3. **對象模式** 的 **路由對象** 的 **props屬性**到底可不可以寫成動態的,個人理解:
3.1 布爾模式是【任意個數參數】在頁面中簡化params屬性
3.2 對象模式是【靜態的值】寫死的
3.3 函數模式是 【根據邏輯來判斷 **指定簡化的參數** 】改寫法比較靈活
4. 路由組件傳參的3種模式的應用場景
5. beforeEnter無法跳轉指定頁面,例如:

6. 組件內的守衛到底是不是3個鉤子有沒有新出的鉤子
搜索 【后期】、【補充】,把沒寫的補充上
- vue 26課
- Vue-cli3.0項目搭建
- Vue-ui 創建cli3.0項目
- Vue-ui 界面詳解
- 項目目錄詳解
- public文件夾
- favicon.ico
- index.html
- src文件夾
- api文件夾
- assets文件夾
- components文件夾
- config文件夾
- directive文件夾
- lib文件夾
- mock文件夾
- mock簡明文檔
- router文件夾
- store文件夾
- views文件夾
- App.vue
- main.js
- .browserslistrc
- .editorconfig
- .eslintrc.js
- .gitignore
- babel.config.js
- package-lock.json
- package.json
- postcss.config.js
- README.en.md
- README.md
- vue.config.js
- Vue Router
- 路由詳解(一)----基礎篇
- 路由詳解(二)----進階篇
- Vuex
- Bus
- Vuex-基礎-state&getter
- Vuex-基礎-mutation&action/module
- Vuex-進階
- Ajax請求
- 解決跨域問題
- 封裝axios
- Mock.js模擬Ajax響應
- 組件封裝
- 從數字漸變組件談第三方JS庫使用
- 從SplitPane組件談Vue中如何【操作】DOM
- 渲染函數和JSX快速掌握
- 遞歸組件的使用
- 登陸/登出以及JWT認證
- 響應式布局
- 可收縮多級菜單的實現
- vue雜項
- vue遞歸組件
- vue-cli3.0多環境打包配置
- Vue+Canvas實現圖片剪切
- vue3系統入門與項目實戰
- Vue語法初探
- 初學編寫 HelloWorld 和 Counter
- 編寫字符串反轉和內容隱藏功能
- 編寫TodoList功能了解循環與雙向綁定
- 組件概念初探,對 TodoList 進行組件代碼拆分
- Vue基礎語法
- Vue 中應用和組件的基礎概念
- 理解 Vue 中的生命周期函數
- 常用模版語法講解
- 數據,方法,計算屬性和偵聽器
- 樣式綁定語法
- 條件渲染
- 列表循環渲染
- 事件綁定
- 表單中雙向綁定指令的使用
- 探索組件的理念
- 組件的定義及復用性,局部組件和全局組件
- 組件間傳值及傳值校驗
- 單向數據流的理解
- Non-Props 屬性是什么
- 父子組件間如何通過事件進行通信
- 組件間雙向綁定高級內容
- 使用匿名插槽和具名插槽解決組件內容傳遞問題
- 作用域插槽
- 動態組件和異步組件
- 基礎語法知識點查缺補漏
- Vue 中的動畫
- 使用 Vue 實現基礎的 CSS 過渡與動畫效果
- 使用 transition 標簽實現單元素組件的過渡和動畫效果
- 組件和元素切換動畫的實現
- 列表動畫
- 狀態動畫
- Vue 中的高級語法
- Mixin 混入的基礎語法
- 開發實現 Vue 中的自定義指令
- Teleport 傳送門功能
- 更加底層的 render 函數
- 插件的定義和使用
- 數據校驗插件開發實例
- Composition API
- Setup 函數的使用
- ref,reactive 響應式引用的用法和原理
- toRef 以及 context 參數
- 使用 Composition API 開發TodoList
- computed方法生成計算屬性
- watch 和 watchEffect 的使用和差異性
- 生命周期函數的新寫法
- Provide,Inject,模版 Ref 的用法
- Vue 項目開發配套工具講解
- VueCLI 的使用和單文件組件
- 使用單文件組件編寫 TodoList
- Vue-Router 路由的理解和使用
- VueX 的語法詳解
- CompositionAPI 中如何使用 VueX
- 使用 axios 發送ajax 請求
- Vue3.0(正式版) + TS
- 你好 Typescript: 進入類型的世界
- 什么是 Typescript
- 為什么要學習 Typescript
- 安裝 Typescript
- 原始數據類型和 Any 類型
- 數組和元組
- Interface- 接口初探
- 函數
- 類型推論 聯合類型和 類型斷言
- class - 類 初次見面
- 類和接口 - 完美搭檔
- 枚舉(Enum)
- 泛型(Generics) 第一部分
- 泛型(Generics) 第二部分 - 約束泛型
- 泛型第三部分 - 泛型在類和接口中的使用
- 類型別名,字面量 和 交叉類型
- 聲明文件
- 內置類型
- 總結