Vue開發之路由進階

1.路由組件傳參

在一個頁面中,須要根據路由得到參數,而後在頁面進行邏輯處理,能夠經過$route來獲取相關參數前端

可是這樣一來,頁面組件與路由耦合過高,爲了解耦,頁面組件能夠在更大程度上進行復用,可使用路由組組傳參vue

路由組件傳參有三種形式web

1.1 第一種:布爾模式

適用於在動態路由匹配中,有動態路由參數的路由配置中vue-router

在前面的例子裏,定義了一個動態路由參數的路由後端

修改src/router/router/js文件瀏覽器

import Home from '@/views/Home.vue'

export default [
    {
        path: '/',
        name: 'home',
        alias:'/home_page',
        component: Home,
    }, {
        path: '/about',
        name: 'about',
        component: () => import('@/views/About.vue'),
    },{
        path:'/argu/:name',
        name:'argu',
        component:() => import('@/views/argu.vue'),
        props:true
    }
]

修改src/views/argu.vue文件內容爲:app

<template>
    <div>
        {{ $route.params.name }}
    </div>
</template>

<script>
export default {

}
</script>

用瀏覽器打開URL:http://localhost:8080/#/argu/orange,效果以下less

在上面的argu頁面,能夠經過$route對象獲取動態路由中的參數,此時也可使用布爾模式獲取動態路由中定義的參數異步

修改src/router/router/js文件,定義布爾模式來獲取動態路由參數函數

import Home from '@/views/Home.vue'

export default [
    {
        path: '/',
        name: 'home',
        alias:'/home_page',
        component: Home,
    }, {
        path: '/about',
        name: 'about',
        component: () => import('@/views/About.vue'),
    },{
        path:'/argu/:name',
        name:'argu',
        component:() => import('@/views/argu.vue'),
        props:true      // 定義布爾模式獲取路由參數
    }
]

修改src/views/argu.vue文件

<template>
    <div>
        {{ name }}
    </div>
</template>

<script>
export default {
    props:{
        name:{
            type:String,            // 定義name的值的類型,String表示name的值必須爲字符串,Number表示name的值爲整數
            default:'renpingsheng'  // 定義默認值,能夠省略
        }
    }
}
</script>

更改路由的參數,效果以下

1.2 第二種:對象模式

普通路由的傳值,例如在about路由頁面中傳參

修改src/router/router.js文件,在about路由中傳值

import Home from '@/views/Home.vue'

export default [
    {
        path: '/',
        name: 'home',
        alias:'/home_page',
        component: Home,
    }, {
        path: '/about',
        name: 'about',
        component: () => import('@/views/About.vue'),
        props:{
            fruit:"banana"
        }
    },{
        path:'/argu/:name',
        name:'argu',
        component:() => import('@/views/argu.vue'),
        props:true
    }
]

而後修改src/views/About.vue文件

<template>
<div class="about">
    <h1>This is an about page</h1>
    <b>{{ fruit }}</b>
</div>
</template>
<script>
export default {
    props:{
        fruit:{
            type:String,
            default:'apple'     // 默認值,若是路由中沒的傳遞任何值則在頁面上顯示默認值
        }
    }
}
</script>

因爲/about路由中傳遞了fruit的值,因此頁面顯示效果以下

此時若是在路由的props屬性中沒有傳遞fruit的值

import Home from '@/views/Home.vue'

export default [
    {
        path: '/',
        name: 'home',
        alias:'/home_page',
        component: Home,
    }, {
        path: '/about',
        name: 'about',
        component: () => import('@/views/About.vue'),
        props:{
            // fruit:"banana"       // 把傳遞參數的這一行註釋
        }
    },{
        path:'/argu/:name',
        name:'argu',
        component:() => import('@/views/argu.vue'),
        props:true
    }
]

src/views/About.vue文件內容不變,則頁面上就會顯示About.vue文件中props屬性中定義的默認值,顯示效果以下

1.3 第三種:函數模式

適合在傳入的屬性中可以根據當前的路由進行邏輯判斷,從而設置傳入組件的屬性值

修改src/router/router/js文件,修改home路由

import Home from '@/views/Home.vue'

export default [
    {
        path: '/',
        name: 'home',
        alias:'/home_page',
        component: Home,
        props: route => ({
            fruit: route.query.params
        })
    }, {
        path: '/about',
        name: 'about',
        component: () => import('@/views/About.vue'),
        props:{
            // fruit:"banana"
        }
    },{
        path:'/argu/:name',
        name:'argu',
        component:() => import('@/views/argu.vue'),
        props:true
    }
]

修改src/views/Home.vue文件

<template>
<div class="home">
    <b>{{ fruit }}</b>
    <br>
    <button @click="handleClick">返回上一頁</button>
</div>
</template>

<script>

export default {
    name: 'home',
    props:{
        fruit:{
            type: String,
            default:'strawberry'
        }
    },
    methods:{
        handleClick () {
            this.$router.push({
            name:`argu`,
            params:{
                name:'banana'
            }
            })
        }
    }
}
</script>

此時用瀏覽器打開URL: http://localhost:8080/#/?params=orange,顯示效果以下

刪除URL中添加的params鍵值對,頁面顯示效果以下

當URL中包含params鍵值對時,頁面上就會顯示URL中params的值

當URL中沒有傳入params鍵值對時,頁面上就會顯示props中定義的默認值

2.導航守衛

導航守衛在實際開發中都會用到的技能,大體至關於後端開發中的中間件功能

可以在路由發生跳轉到導航結束這個階段內進行一些相應的邏輯處理

好比用戶打開某個須要登陸的頁面時判斷用戶是否登陸,若是用戶已經登陸則直接打開頁面

若是用戶沒有登陸就跳轉到登陸頁面

再好比用戶在一個頁面編輯了內容,當用戶跳轉到別的頁面時提醒用戶是否保存等功能均可以由導航守衛來完成

2.1 全局守衛

全局守衛就是在全局設置的守衛

修改src/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 IS_LOGINED = true         // 模擬用戶是否登陸

router.beforeEach((to,from,next) => {
    if(to.name !== 'login'){
        if(IS_LOGINED) next()
        else next({name:'login'})
    }else{
        if(IS_LOGINED) next({name:'home'})
        else next()
    }
})
export default router

修改src/router/router.js文件,添加login命名路由

import Home from '@/views/Home.vue'

export default [
    {
        path: '/',
        name: 'home',
        alias:'/home_page',
        component: Home,
        props: route => ({
            fruit: route.query.params
        })
    }, {
        path: '/about',
        name: 'about',
        component: () => import('@/views/About.vue'),
        props:{
            // fruit:"banana"
        }
    },{
        path:'/argu/:name',
        name:'argu',
        component:() => import('@/views/argu.vue'),
        props:true
    },{
        path:'/login',
        name:'login',
        component:() => import('@/views/login.vue')
    }
]

而後在src/views/目錄下新建login.vue文件,文件內容以下

<template>
    <div>
        <b>this is login page</b>
    </div>
</template>

<script>
export default {
    
}
</script>

在瀏覽器中輸入路由列表中已配置的任何URL,都會進入對應的頁面

當把src/router/index.js文件中IS_LOGINED的值設置爲false

import Vue from 'vue'
import Router from 'vue-router'
import routes from './router'

Vue.use(Router)

const router = new Router({
    routes
})

const IS_LOGINED = false

router.beforeEach((to,from,next) => {
    if (to.name !== 'login') {
        if (IS_LOGINED) next()
        else next({ name: 'login' })
    } else {
        if (IS_LOGINED) next({ name: 'home' })
        else next()
    }
})
export default router

在瀏覽器中輸入任何URL,都會進入login頁面

每一個守衛方法接收三個參數:

to: Route: 即將要進入的目標 路由對象
from: Route: 當前導航正要離開的路由
next: Function: 必定要調用該方法來 resolve 這個鉤子。執行效果依賴 next 方法的調用參數。
其中,next方法能夠傳入的參數:
    next(): 進行管道中的下一個鉤子。若是所有鉤子執行完了,則導航的狀態就是 confirmed (確認的)。
    next(false): 中斷當前的導航。若是瀏覽器的 URL 改變了 (多是用戶手動或者瀏覽器後退按鈕),那麼 URL 地址會重置到 from 路由對應的地址。
    next('/') 或者 next({ path: '/' }): 跳轉到一個不一樣的地址。當前的導航被中斷,而後進行一個新的導航。你能夠向 next 傳遞任意位置對象,且容許設置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。

必定要確保在全部路由守衛最後要執行next方法,不然鉤子就不會被 resolved

2.2 後置守衛

後置守衛不能阻止頁面的跳轉

修改src/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 IS_LOGINED = true

router.beforeEach((to,from,next) => {
    if (to.name !== 'login') {
        if (IS_LOGINED) next()
        else next({ name: 'login' })
    } else {
        if (IS_LOGINED) next({ name: 'home' })
        else next()
    }
})

router.afterEach((to, from) => {
    console.log(from)
    console.log(to)
})
export default router

先用瀏覽器打開home頁面,再跳轉到about頁面,根據後置守衛中打印出的結果

2.3 beforeResolve前端守衛

beforeResolve也是全局守衛,beforeResolve做用於導航被確認以前在全部組件內守衛以及異步路由被解析以後守衛被調用

導航被確認的表示全部導航鉤子都結束,則表示導航被確認

2.4 路由獨享守衛

路由獨享守衛是在路由列表中配置的,例如在home路由中配置一個只在home路由中被調用的守衛

修改src/router/router.js文件,爲home路由添加路由獨享守衛

import Home from '@/views/Home.vue'

export default [
    {
        path: '/',
        name: 'home',
        alias:'/home_page',
        component: Home,
        props: route => ({
            fruit: route.query.params
        }),
        beforeEnter:(to,from,next) => {
            if(from.name === 'about') alert("這是從about頁來的")
            else alert("這不是從about頁來的")
            next()
        }
    }, {
        path: '/about',
        name: 'about',
        component: () => import('@/views/About.vue'),
        props:{
            // fruit:"banana"
        }
    },{
        path:'/argu/:name',
        name:'argu',
        component:() => import('@/views/argu.vue'),
        props:true
    },{
        path:'/login',
        name:'login',
        component:() => import('@/views/login.vue')
    }
]

此時,用瀏覽器打開home頁,而後跳轉到about頁,再從about頁跳轉回到home頁,則會彈出對話框

取消彈出框,在瀏覽器中輸入URL: http://localhost:8080/#/argu/orange,再從argu頁面進入home頁面,則會彈出對話框

若是添加了beforeEnter路由獨享守衛,在進行邏輯處理後,必定要調用 next()方法,不然不會進行跳轉

2.5 組件內守衛

每個組件均可以有三個鉤子

註釋其餘的守衛,修改src/views/Home.vue文件,添加組件內守衛

<template>
<div class="home">
    <b>{{ fruit }}</b>
    <br>
    <button @click="handleClick">返回上一頁</button>
</div>
</template>

<script>

export default {
    name: 'home',
    props:{
        fruit:{
            type: String,
            default:'strawberry'
        }
    },
    beforeRouteEnter(to,from,next){
        console.log(this)
        console.log(from.name)
        console.log(to.name)
        next()
    },
    methods:{
        handleClick () {
            this.$router.push({
            name:`argu`,
            params:{
                name:'banana'
            }
            })
        }
    }
}
</script>

用瀏覽器打開URL: http://localhost:8080/#/,而後跳轉到About頁面,再跳轉回Home頁

在瀏覽器的調試頁面能夠看到

當直接打開Home頁面時,from.name的值是null,to.name的值爲home
而當從About頁面跳轉到Home頁面時,from.name的值是about,to.name的值仍然是home

組件內守衛是在路由已經被觸發,在進入對應頁面調用,此時對應頁面尚未渲染,因此在組件內守衛中調用this時,是獲取不到當前組件的實例的,正如上面的例子裏其值爲undefined

跟路由獨享守衛同樣,若是添加了beforeRouteEnter守衛,在進行邏輯處理後,必定要調用 next()方法,不然不會進行跳轉
若是想在組件內守衛中獲取當前組件的實例,能夠在next方法中獲取

修改src/views/Home.vue文件,在組件內守衛的next方法中獲取組件實例

<template>
<div class="home">
    <b>{{ fruit }}</b>
    <br>
    <button @click="handleClick">返回上一頁</button>
</div>
</template>

<script>

export default {
    name: 'home',
    props:{
        fruit:{
            type: String,
            default:'strawberry'
        }
    },
    beforeRouteEnter(to,from,next){
        next( vm => {
            console.log(vm)
            console.log(vm.$root)
        })
    },
    methods:{
        handleClick () {
            this.$router.push({
            name:`argu`,
            params:{
                name:'banana'
            }
            })
        }
    }
}
</script>

刷新瀏覽器,打印結果以下

能夠經過回調函數的方式來獲取當前組件的實例,而後就能夠獲取某個屬性的結果了

修改src/views/Home.vue文件,添加beforeRouteLeave守衛

<template>
<div class="home">
    <b>{{ fruit }}</b>
    <br>
    <button @click="handleClick">返回上一頁</button>
</div>
</template>

<script>

export default {
    name: 'home',
    props:{
        fruit:{
            type: String,
            default:'strawberry'
        }
    },
    beforeRouteEnter(to,from,next){
        next( vm => {
            console.log(vm)
            console.log(vm.$root)
        })
    },
    beforeRouteLeave(to,from,next){
        const is_leave = confirm("您肯定要離開本頁面嗎?")
        if(is_leave) next()
        else next(false)
    },
    methods:{
        handleClick () {
            this.$router.push({
            name:`argu`,
            params:{
                name:'banana'
            }
            })
        }
    }
}
</script>

刷新瀏覽器,顯示效果以下

點擊About標籤後,顯示效果以下

點擊肯定按鈕,頁面會跳轉到About頁面

若是點擊取消按鈕,則頁面不會跳轉,仍然會停留在Home頁面

在這個守衛被調用時,頁面已經渲染好了,因此能夠在beforeRouteLeave組件裏使用this來獲取組件實例

好比咱們一個網頁發表一篇博客,當誤操做點擊跳轉到其餘頁面時,此時就可使用這個守衛來提示用戶是否保存當前的編輯內容

2.6 beforeRouteUpdate守衛

修改src/views/argu.vue文件,添加beforeRouteUpdate守衛

<template>
    <div>
        {{ name }}
    </div>
</template>

<script>
export default {
    props:{
        name:{
            type:String,
        }
    },
    beforeRouteUpdate(to,from,next){
        console.log(to.name, from.name)
    }
}
</script>

使用瀏覽器打開URL:http://localhost:8080/#/argu/apple,此時在瀏覽器的調試頁面不會打印任何數據

修改URL爲:http://localhost:8080/#/argu/orange,頁面顯示效果以下

第一次進入argu頁面時,沒有打印任何內容,說明beforeRouteUpdate守衛沒有被調用
當路由不改變,路由參數改變時,argu組件被複用,beforeRouteUpdate守衛才被調用

與beforeRouteLeave守衛同樣,在beforeRouteUpdate守衛裏,頁面已經渲染好了,因此可使用this來獲取組件實例

3.一個完整的導航解析流程

導航被觸發,不論是使用this.$router.push方式觸發,仍是在瀏覽器中直接輸入URL的方式觸發,
在失活的組件(即將離開的頁面組件)裏調用離開守衛 beforeRouteLeave
調用全局的前置守衛 beforeEach
在重用的組件裏調用 beforeRouteUpdate 
調用路由獨享守衛 beforeEnter
解析異步路由組件
在被激活的組件(即將進入的頁面組件)裏調用beforeRouteEnter
調用全局的解析守衛 beforeResolve
導航被確認
調用全局的後置守衛 afterEash
觸發DOM更新
用建立好的實例調用 beforeRouterEnter守衛裏傳給next的回調函數

4.路由切換的動態效果

路由的切換其實就是一個路由註銷,另外一個路由加載

修改src/App.vue文件,

<template>
<div id="app">
    <div id="nav">
    <router-link v-bind:to="{ name:'home'}">Home</router-link> |
    <router-link :to="{ name:'about'}">About</router-link>
    </div>
    <transition-group name="router">
    <router-view key="defalut"/>
    <router-view key="view1" name="view1"/>
    <router-view key="view2" name="view2"/>
    </transition-group>
</div>
</template>

<style lang="less">
.router-enter{
    opacity: 0;
}
.router-enter-active{
    transition: opacity 2s ease;
}
.router-enter-to {
    opacity:1;
}
.router-leave{
    opacity:1;
}
.router-leave-active{
    transition:opacity 2s ease;
}
.router-leave-to {
    opacity:0;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
}

#nav a {
font-weight: bold;
color: #2c3e50;
}

#nav a.router-link-exact-active {
color: #42b983;
}
</style>

刷新瀏覽器,切換路由時,能夠看到路由切換時,頁面都有動態效果

相關文章
相關標籤/搜索