1. 程式人生 > >[Vue 牛刀小試]:第十三章 - Vue Router 基礎使用再探(命名路由、命名檢視、路由傳參)

[Vue 牛刀小試]:第十三章 - Vue Router 基礎使用再探(命名路由、命名檢視、路由傳參)

 一、前言

  在上一章的學習中,我們簡單介紹了前端路由的概念,以及如何在 Vue 中通過使用 Vue Router 來實現我們的前端路由。但是在實際使用中,我們經常會遇到路由傳參、或者一個頁面是由多個元件組成的情況。本章,我們就來介紹下在這兩種情況下 Vue Router 的使用方法以及一些可能涉及到的概念。

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

  倉儲地址:https://github.com/Lanesra712/VueTrial/tree/master/chapter02-bronze/router/pass

 二、乾貨合集

  1、命名路由

  在某些時候,我們期望生成的路由 URL 地址可能會很長,在使用中可能會顯得有些不便。這時候通過一個名稱來標識一個路會更方便一些,因此在 Vue Router 中,我們可以在建立 Router 例項的時候,通過在 routes 配置中給某個路由設定名稱,從而方便的呼叫路由。

const router = new VueRouter({
  routes: [
    {
      path: '/form',
      name: 'form',
      component: '<div>form 元件</div>'
    }
  ]
})

  當我們使用命名路由之後,當需要使用 router-link 標籤進行跳轉時,就可以採取給 router-link 的 to 屬性傳一個物件的方式,跳轉到指定的路由地址上。

<router-link :to="{ name: 'form'}">User</router-link>

  2、命名檢視

  當我們開啟一個頁面時,整個頁面可能是由多個 Vue 元件所構成的,例如,我們的後臺管理首頁可能是由 sidebar (側導航) 、header(頂部導航)和 main (主內容)這三個 Vue 元件構成的。此時,當我們通過 Vue Router 構建路由資訊時,如果一個 URL 只能對應一個 Vue 元件,整個頁面肯定是無法正確顯示的。

  在上一章的學習中,我們在構建路由資訊的時候有使用到兩個特殊的標籤:router-view 和 router-link。通過 router-view 標籤,我們就可以指定元件渲染顯示到什麼位置。因此,當我們需要在一個頁面上顯示多個元件的時候,就需要在頁面中新增多個的 router-view 標籤。

  那麼,是不是可以通過一個路由對應多個元件,然後按需渲染在不同的 router-view 標籤上呢?按照上一章中介紹的關於 Vue Router 的使用方法,我們可以很容易的實現下面的程式碼。

<div id="app">
    <router-view></router-view>
    <div class="container">
        <router-view></router-view>
        <router-view></router-view>
    </div>
</div>

<template id="sidebar">
    <div class="sidebar">
        sidebar
    </div>
</template>

<script>
    // 1、定義路由跳轉的元件模板
    const header = {
        template: '<div class="header"> header </div>'
    }

    const sidebar = {
        template: '#sidebar',
    }

    const main = {
        template: '<div class="main"> main </div>'
    }

    // 2、定義路由資訊
    const routes = [{
        path: '/',
        component: header
    }, {
        path: '/',
        component: sidebar
    }, {
        path: '/',
        component: main
    }]

    const router = new VueRouter({
        routes
    })

    // 3、掛載到當前 Vue 例項上
    const vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        router: router
    });
</script>

  可以看到,並沒有實現我們想要實現的效果,當我們將一個路由資訊對應到多個元件時,不管有多少個的 router-view 標籤,程式都會將第一個元件渲染到所有的 router-view 標籤上。

  因此,在這種情況下,我們需要實現的是一個路由資訊可以按照我們的需要去渲染到頁面中指定的 router-view 標籤上,而在 Vue Router 中,我們則可以通過命名檢視的方式實現我們的需求。

  命名檢視,從名稱上看可能無法闡述的很清楚,與命名路由的實現方式相似,命名檢視通過在 router-view 標籤上設定 name 屬性,之後,在構建路由與元件的對應關係時,以一種 name:component 的形式構造出一個元件物件,從而指明是在哪個 router-view 標籤上載入什麼元件。

  注意,這裡在指定路由對應的元件時,使用的是 components(包含 s)屬性進行配置元件。實現命名檢視的程式碼如下所示。

<div id="app">
    <router-view></router-view>
    <div class="container">
        <router-view name="sidebar"></router-view>
        <router-view name="main"></router-view>
    </div>
</div>

<script>
    // 2、定義路由資訊
    const routes = [{
        path: '/',
        components: {
            default: header,
            sidebar: sidebar,
            main: main
        }
    }]
</script>

  在 router-view 中,預設的 name 屬性值為 default,所以這裡的 header 元件對應的 router-view 標籤就可以不設定 name 屬性值。完整的示例程式碼如下。

<div id="app">
    <router-view></router-view>
    <div class="container">
        <router-view name="sidebar"></router-view>
        <router-view name="main"></router-view>
    </div>
</div>

<template id="sidebar">
    <div class="sidebar">
        sidebar
    </div>
</template>

<script>
    // 1、定義路由跳轉的元件模板
    const header = {
        template: '<div class="header"> header </div>'
    }

    const sidebar = {
        template: '#sidebar'
    }

    const main = {
        template: '<div class="main"> main </div>'
    }

    // 2、定義路由資訊
    const routes = [{
        path: '/',
        components: {
            default: header,
            sidebar: sidebar,
            main: main
        }
    }]

    const router = new VueRouter({
        routes
    })

    // 3、掛載到當前 Vue 例項上
    const vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        router: router
    });
</script>

  3、路由傳參

  在很多的情況下,例如表單提交,元件跳轉之類的操作,我們需要使用到上一個表單、元件的一些資料,這時我們就需要將需要的引數通過引數傳參的方式在路由間進行傳遞。

  例如,在下面的示例中,我們想要實現通過點選 main 元件中的子元件 form 元件上的按鈕,將表單的內容傳遞到 info 子元件中進行顯示,功能示意圖如下所示。

  3.1、query 傳參

  query 查詢引數傳參,就是將我們需要的引數以 key=value 的方式放在 url 地址中。例如這裡的需求,我們需要在 info 元件中顯示上一個頁面的資料,所以我們的 info 頁面顯示的 URL 地址應該為 /info?email=xxx&password=xxx,這裡的 email 和 password 引數值則是 form 元件上使用者輸入的值。之後,我們通過獲取這兩個引數值即可實現我們的需求。

  當我們將例項化的 VueRouter 物件掛載到 Vue 例項後,Vue Router 在我們的 Vue 例項上建立了兩個屬性物件 this.$router(router 例項) 和 this.$route(當前頁面的路由資訊)。從下圖可以看到,我們可以通過 vm.$route 獲取到當前頁面的路由資訊,而這裡的 query 物件則是我們需要的。

  可以看到,這時我們就可以直接通過 $route.query.引數名 的方式獲取到對應的引數值。同時可以發現,fullPath 屬性可以獲取到當前頁面的地址和 query 查詢引數,而 path 屬性則只是獲取到當前的路由資訊。

  同時,因為在使用 Vue Router 時已經將 VueRouter 例項掛載到 Vue 例項上,因此就可以直接通過呼叫 $router.push 方法來導航到另一個頁面,所以這裡 form 元件中的按鈕事件,我們就可以使用這種方式完成路由地址的跳轉,完整的程式碼如下。

<div id="app">
    <div class="row">
        <div class="col">
            <router-view></router-view>
        </div>
    </div>
    <div class="row">
        <div class="col-sm-2 sidebar">
            <router-view name="sidebar"></router-view>
        </div>
        <div class="col-sm-10 main">
            <router-view name="main"></router-view>
        </div>
    </div>
</div>

<template id="sidebar">
    <div>
        <ul>
            <router-link v-for="(item,index) in menu" :key="index" :to="item.url" tag="li">{{item.name}}
            </router-link>
        </ul>
    </div>
</template>

<template id="main">
    <div>
        <router-view> </router-view>
    </div>
</template>

<template id="form">
    <div>
        <form>
            <div class="form-group">
                <label for="exampleInputEmail1">Email address</label>
                <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp"
                    placeholder="Enter email" v-model="email">
                <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone
                    else.</small>
            </div>
            <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password"
                    v-model="password">
            </div>
            <button type="submit" class="btn btn-primary" @click="submit">Submit</button>
        </form>
    </div>
</template>

<template id="info">
    <div class="card" style="margin-top: 5px;">
        <div class="card-header">
            輸入的資訊
        </div>
        <div class="card-body">
            <blockquote class="blockquote mb-0">
                <p>Email Address:{{ $route.query.email }} </p>
                <p>Password:{{ $route.query.password }}</p>
                <footer class="blockquote-footer">Someone famous in <cite title="Source Title">Source Title</cite>
                </footer>
            </blockquote>
        </div>
    </div>
</template>

<script>
    // 1、定義路由跳轉的元件模板
    const header = {
        template: '<div class="header"> header </div>'
    }

    const sidebar = {
        template: '#sidebar',
        data() {
            return {
                menu: [{
                    name: 'Form',
                    url: '/form'
                }, {
                    name: 'Info',
                    url: '/info'
                }]
            }
        },
    }

    const main = {
        template: '#main'
    }

    const form = {
        template: '#form',
        data() {
            return {
                email: '',
                password: ''
            }
        },
        methods: {
            submit() {
                this.$router.push({
                    path: '/info?email=' + this.email + '&password=' + this.password
                })
            }
        },
    }

    const info = {
        template: '#info'
    }

    // 2、定義路由資訊
    const routes = [{
        path: '/',
        components: {
            default: header,
            sidebar: sidebar,
            main: main
        },
        children: [{
            path: '',
            redirect: 'form'
        }, {
            path: 'form',
            component: form
        }, {
            path: 'info',
            component: info
        }]
    }]

    const router = new VueRouter({
        routes
    })

    // 3、掛載到當前 Vue 例項上
    const vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        router: router
    });
</script>

  3.2、param 傳參

  與獲取 query 引數的方式相同,我們同樣可以通過 vm.$route 獲取到當前路由資訊,從而在 param 物件中通過 $route.params.引數名 的方式獲取到通過 param 的方式進行引數傳遞的值。

  不過,與 query 查詢引數傳參不同的是,在定義路由資訊時,我們需要以佔位符(:引數名)的方式將需要傳遞的引數指定到路由地址中,實現程式碼如下。

const routes = [{
    path: '/',
    components: {
        default: header,
        sidebar: sidebar,
        main: main
    },
    children: [{
        path: '',
        redirect: 'form'
    }, {
        path: 'form',
        name: 'form',
        component: form
    }, {
        path: 'info/:email/:password',
        name: 'info',
        component: info
    }]
}]

  因為在使用 $route.push 進行路由跳轉時,如果提供了 path 屬性,則物件中的 params 屬性會被忽略,所以這裡我們可以採用命名路由的方式進行跳轉或者直接將引數值傳遞到路由 path 路徑中。同時,與使用 query 查詢引數傳遞引數不同,這裡的引數如果不進行賦值的話,就無法與我們的匹配規則對應,也就無法跳轉到指定的路由地址中。

const form = {
    template: '#form',
    data() {
        return {
            email: '',
            password: ''
        }
    },
    methods: {
        submit() {
            // 方式1
            this.$router.push({
                name: 'info',
                params: {
                    email: this.email,
                    password: this.password
                }
            })

            // 方式2
            this.$router.push({
                path: `/info/${this.email}/${this.password}`,
            })
        }
    },
}

  其餘的部分與使用 query 查詢引數傳參的方式相同,這裡我就直接給出實現程式碼了,實現的示意圖如下。

<div id="app">
    <div class="row">
        <div class="col">
            <router-view></router-view>
        </div>
    </div>
    <div class="row">
        <div class="col-sm-2 sidebar">
            <router-view name="sidebar"></router-view>
        </div>
        <div class="col-sm-10 main">
            <router-view name="main"></router-view>
        </div>
    </div>
</div>

<template id="sidebar">
    <div>
        <ul>
            <router-link v-for="(item,index) in menu" :key="index" :to="{name:item.routeName}" tag="li">
                {{item.displayName}}
            </router-link>
        </ul>
    </div>
</template>

<template id="main">
    <div>
        <router-view> </router-view>
    </div>
</template>

<template id="form">
    <div>
        <form>
            <div class="form-group">
                <label for="exampleInputEmail1">Email address</label>
                <input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp"
                    placeholder="Enter email" v-model="email">
                <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone
                    else.</small>
            </div>
            <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password"
                    v-model="password">
            </div>
            <button type="submit" class="btn btn-primary" @click="submit">Submit</button>
        </form>
    </div>
</template>

<template id="info">
    <div class="card" style="margin-top: 5px;">
        <div class="card-header">
            輸入的資訊
        </div>
        <div class="card-body">
            <blockquote class="blockquote mb-0">
                <p>Email Address:{{ $route.params.email }} </p>
                <p>Password:{{ $route.params.password }}</p>
                <footer class="blockquote-footer">Someone famous in <cite title="Source Title">Source Title</cite>
                </footer>
            </blockquote>
        </div>
    </div>
</template>

<script>
    // 1、定義路由跳轉的元件模板
    const header = {
        template: '<div class="header"> header </div>'
    }

    const sidebar = {
        template: '#sidebar',
        data() {
            return {
                menu: [{
                    displayName: 'Form',
                    routeName: 'form'
                }, {
                    displayName: 'Info',
                    routeName: 'info'
                }]
            }
        },
    }

    const main = {
        template: '#main'
    }

    const form = {
        template: '#form',
        data() {
            return {
                email: '',
                password: ''
            }
        },
        methods: {
            submit() {
                // 方式1
                this.$router.push({
                    name: 'info',
                    params: {
                        email: this.email,
                        password: this.password
                    }
                })

                // 方式2
                // this.$router.push({
                //     path: `/info/${this.email}/${this.password}`,
                // })
            }
        },
    }

    const info = {
        template: '#info'
    }

    // 2、定義路由資訊
    const routes = [{
        path: '/',
        components: {
            default: header,
            sidebar: sidebar,
            main: main
        },
        children: [{
            path: '',
            redirect: 'form'
        }, {
            path: 'form',
            name: 'form',
            component: form
        }, {
            path: 'info/:email/:password',
            name: 'info',
            component: info
        }]
    }]

    const router = new VueRouter({
        routes
    })

    // 3、掛載到當前 Vue 例項上
    const vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        router: router
    });
</script>

 三、總結

  這一章主要是介紹了命名路由,以及如何通過使用命名檢視在 Vue Router 中將多個元件對應到同一個路由。同時,針對實際使用中我們經常會遇到的路由傳參,我們則可以通過 query 或者是 param 的方式進行引數傳遞。不過,不知道你有沒有注意到,不管是 query 傳參還是 param 傳參,最終我們都是通過 vm.$route 屬性獲取到引數資訊,這無疑意味著元件和路由耦合到了一塊,所有需要獲取引數值的地方都需要載入 Vue Router,這其實是很不應該的,因此如何實現元件與路由間的解耦,我將在下一章中進行說明。

 四、參考

  1、從頭開始學習vue-router

  2、單頁面應用路由的兩種實現方式

  3、你需要知道的單頁面路由實現原理

  4、面試官: 你瞭解前端路由嗎?

  5、前端路由實現原理(histor