[Vue 牛刀小試]:第十四章 - 程式設計式導航與實現元件與 Vue Router 之間的解耦

墨墨墨墨小宇發表於2019-07-03

 一、前言

  在上一章的學習中,通過舉例說明,我們瞭解了 Vue Router 中命名路由、命名檢視的使用方法,以及如何通過 query 查詢引數傳參,或者是採用 param 傳參的方式實現路由間的引數傳遞。通過學習我們可以發現,在實現路由間的引數傳遞時,我們將 Vue Router 與我們的元件強耦合在一起,這無疑是不合適的,那麼本章我們就來學習,如何實現元件和 Vue Router 之間的解耦。

  學習系列目錄地址:https://www.cnblogs.com/danvic712/p/9549100.html

  倉儲地址:https://github.com/Lanesra712/VueTrial/blob/master/chapter02-bronze/router/decoupling.html

 二、乾貨合集

  1、程式設計式導航

  在使用 Vue Router 的時候,我們通常會通過 router-link 標籤去生成跳轉到指定路由的連結,但是在實際的前端開發中,我們更多的是通過 js 的方式進行跳轉。就像我們很常見的一個互動需求,使用者提交表單,提交成功後跳轉到上一頁面,提交失敗則留在當前頁。這時候如果我們還是通過 router-link 標籤進行跳轉就不合適了,我們需要通過 js 根據表單返回的狀態進行動態的判斷。

  在使用 Vue Router 時,我們已經將 Vue Router 的例項掛載到了 Vue 例項上,因此我們就可以藉助 $router 的例項方法,通過編寫 js 程式碼的方式實現路由間的跳轉,而這種方式就是一種程式設計式的路由導航。

  在 Vue Router 中具有三種導航方法,分別為 push、replace 和 go。我們最常見的通過在頁面上設定 router-link 標籤進行路由地址間的跳轉,就等同於執行了一次 push 方法。

  在這一小節的示例中,我將使用程式設計式導航實現通過點選不同的按鈕實現路由間的跳轉,最終實現的示意圖如下所示。

  在之前學習 Vue Router 的基礎使用方法時,我們瞭解到,前端路由的實現方式,實際上就是對於瀏覽器的 history api 的操作。瀏覽器的 history 物件提供了對瀏覽器的會話歷史的訪問,它暴露了很多有用的方法和屬性,允許我們在使用者瀏覽歷史中向前和向後跳轉,同時從 HTML5 開始提供了對 history 棧中內容的操作。

  而 Vue Router 所提供的 push、replace 和 go 方法則完全可以對應到瀏覽器 history api 中所提供的 history.pushState、history.replaceState 和 history.go 方法。

  1.1、push

  當我們需要跳轉新頁面時,我們就可以通過 push 方法將一條新的路由記錄新增到瀏覽器的 history 棧中,通過 history 的自身特性,從而驅使瀏覽器進行頁面的跳轉。同時,因為在 history 會話歷史中會一直保留著這個路由資訊,所以當我們後退時還是可以退回到當前的頁面。

  在 push 方法中,引數可以是一個字串路徑,或者是一個描述地址的物件,這裡其實就等同於我們呼叫了 history.pushState 方法。

// 字串 => /first
this.$router.push('first')

// 物件 => /first
this.$router.push({ path: 'first' })

// 帶查詢引數 => /first?abc=123
this.$router.push({ path: 'first', query: { abc: '123' }})

  這裡需要注意,當我們傳遞的引數為一個物件並且當 path 與 params 共同使用時,物件中的 params 屬性不會起任何的作用,我們需要採用命名路由的方式進行跳轉,或者是直接使用帶有引數的全路徑。

const userId = '123'

// 使用命名路由 => /user/123
this.$router.push({ name: 'user', params: { userId }})

// 使用帶有引數的全路徑 => /user/123
this.$router.push({ path: `/user/${userId}` })

// 這裡的 params 不生效 => /user
this.$router.push({ path: '/user', params: { userId }}) 

  1.2、go

  當我們使用 go 方法時,我們就可以在 history 記錄中向前或者後退多少步,也就是說通過 go 方法你可以在已經儲存的 history 路由歷史中來回跳。

// 在瀏覽器記錄中前進一步,等同於 history.forward()
this.$router.go(1)

// 後退一步記錄,等同於 history.back()
this.$router.go(-1)

// 前進 3 步記錄
this.$router.go(3)

// 如果 history 記錄不夠用,那就默默地失敗唄
this.$router.go(-100)
this.$router.go(100)

  1.3、replace

  replace 方法同樣可以達到實現路由跳轉的目的,不過,從名字中你也可以看出,與使用 push 方法跳轉不同是,當我們使用 replace 方法時,並不會往 history 棧中新增一條新的記錄,而是會替換掉當前的記錄,因此,你無法通過後退按鈕再回到被替換前的頁面。

this.$router.replace({
    path: '/special'
})

  通過程式設計式路由實現路由間切換的示例程式碼如下所示,你可以自己嘗試一下,去熟悉如何通過 js 來實現路由地址間的切換。

<div id="app">
    <div class="main">
        <div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
            <div class="btn-group mr-2" role="group" aria-label="First group">
                <button type="button" class="btn btn-secondary" @click="goFirst">第一頁</button>
                <button type="button" class="btn btn-secondary" @click="goSecond">第二頁</button>
                <button type="button" class="btn btn-secondary" @click="goThird">第三頁</button>
                <button type="button" class="btn btn-secondary" @click="goFourth">第四頁</button>
            </div>
            <div class="btn-group mr-2" role="group" aria-label="Second group">
                <button type="button" class="btn btn-secondary" @click="pre">後退</button>
                <button type="button" class="btn btn-secondary" @click="next">前進</button>
            </div>
            <div class="btn-group mr-2" role="group" aria-label="Third group">
                <button type="button" class="btn btn-secondary" @click="replace">替換當前頁為特殊頁</button>
            </div>
        </div>

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

<script>
    const first = {
        template: '<h3>當前是第一頁</h3>'
    }

    const second = {
        template: '<h3>當前是第二頁</h3>'
    }

    const third = {
        template: '<h3>當前是第三頁</h3>'
    }

    const fourth = {
        template: '<h3>當前是第四頁</h3>'
    }

    const special = {
        template: '<h3>特殊頁面</h3>'
    }


    const router = new VueRouter({
        routes: [{
            path: '/first',
            component: first
        }, {
            path: '/second',
            component: second
        }, {
            path: '/third',
            component: third
        }, {
            path: '/fourth',
            component: fourth
        }, {
            path: '/special',
            component: special
        }]
    })

    const vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            goFirst() {
                this.$router.push({
                    path: '/first'
                })
            },
            goSecond() {
                this.$router.push({
                    path: '/second'
                })
            },
            goThird() {
                this.$router.push({
                    path: '/third'
                })
            },
            goFourth() {
                this.$router.push({
                    path: '/fourth'
                })
            },
            next() {
                this.$router.go(1)
            },
            pre() {
                this.$router.go(-1)
            },
            replace() {
                this.$router.replace({
                    path: '/special'
                })
            }
        },
        router: router
    })
</script>

  2、解耦

   在文章開頭我們有提到過,在使用路由傳參的時候,我們將元件與 Vue Router 強繫結在了一塊,這意味著在任何需要獲取路由引數的地方,我們都需要載入 Vue Router。那麼,如何解決這一強繫結呢?

  在之前學習元件相關的知識時,我們提到了可以通過元件的 props 選項來實現子元件接收父元件傳遞的值。而在 Vue Router 中,同樣給我們提供了通過使用元件的 props 選項來進行解耦的功能。

  在下面的示例中,在定義路由模板時,我們通過指定需要傳遞的引數為 props 選項中的一個資料項,之後,我們通過在定義路由規則時,指定 props 屬性為 true,即可實現對於元件以及 Vue Router 之間的解耦。

<script>
    const second = {
        props: ['id'],
        template: '<h3>當前是第二頁 --- {{id}} </h3>'
    }

    const router = new VueRouter({
        routes: [{
            path: '/second/:id',
            component: second,
            props: true
        }]
    })

    const vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            goSecond() {
                this.$router.push({
                    path: '/second'
                })
            }
        },
        router: router
    })
</script>

  可以看到,這裡採用 param 傳參的方式進行引數傳遞,而在元件中我們並沒有載入 Vue Router 例項,也完成了對於路由引數的獲取。需要注意的是,採用此方法,只能實現基於 param 方式進行傳參的解耦。

  針對定義路由規則時,指定 props 屬性為 true 這一種情況,在 Vue Router 中,我們還可以給路由規則的 props 屬性定義成一個物件或是函式。不過,如果定義成物件或是函式,此時並不能實現對於元件以及 Vue Router 間的解耦。

  在將路由規則的 props 定義成物件後,此時不管路由引數中傳遞是任何值,最終獲取到的都是物件中的值。同時,需要注意的是,props 中的屬性值必須是靜態的,也就是說,你不能採用類似於子元件同步獲取父元件傳遞的值作為 props 中的屬性值。

<script>
    const third = {
        props: ['name'],
        template: '<h3>當前是第三頁 --- {{name}} </h3>'
    }

    const router = new VueRouter({
        routes: [{
            path: '/third/:name',
            component: third,
            props: {
                name: 'zhangsan'
            }
        }]
    })

    const vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            goThird() {
                this.$router.push({
                    path: '/third'
                })
            }
        },
        router: router
    })
</script>

  在物件模式中,我們只能接收靜態的 props 屬性值,而當我們使用函式模式之後,就可以對靜態值做資料的進一步加工或者是與路由傳參的值進行結合。

<script>
    const fourth = {
        props: ['id', 'name'],
        template: '<h3>當前是第四頁 --- {{id}}  --- {{name}} </h3>'
    }

    const router = new VueRouter({
        routes: [{
            path: '/fourth',
            component: fourth,
            props: (route) => ({
                id: route.query.id,
                name: 'zhangsan'
            })
        }]
    })

    const vm = new Vue({
        el: '#app',
        data: {},
        methods: {
            goFourth() {
                this.$router.push({
                    path: '/fourth'
                })
            }
        },
        router: router
    })
</script>

 三、總結

  這一章主要學習瞭如何通過使用 Vue Router 的例項方法,從而實現程式設計式導航,以及如何實現元件與 Vue Router 之間的解耦。至此,Vue Router 的一些基礎使用方法也就大概介紹完了,其它的知識點將在後面的專案中具體使用到的時候再進行介紹,歡迎持續關注哈~~~

 四、參考

  1、History API與瀏覽器歷史堆疊管理

  2、可能比文件還詳細--VueRouter完全指北

  3、十全大補vue-router

相關文章