二十三、URL 路由元素功能

在这一章中,我描述了由router-linkrouter-view元素提供的一些特性。我将向您展示如何更改处理router-link元素的方式,如何使用不同的导航事件,以及如何通过更改应用于导航元素的样式来响应路由激活。我还将向您展示如何在一个应用中使用多个router-view元素,以及当两个或多个router-view元素出现在同一个模板中时,如何管理它们。表 23-1 将本章放入上下文中。

表 23-1

将路由元素功能放在上下文中

|

问题

|

回答

| | --- | --- | | 它们是什么? | router-link元素提供了一些特性,这些特性控制生成的 HTML、触发导航的事件以及元素将被添加到的类,以指示活动路线。router-view元素提供了允许多个元素存在于单个组件模板中的特性。 | | 它们为什么有用? | 当您需要通过非标准元素呈现导航,或者想要使用 CSS 框架中的样式提供反馈时,router-link特性非常有用。router-view特性在复杂的应用中很有用,因为它们允许创建更高级的内容组合。 | | 它们是如何使用的? | 使用router-linkrouter-view元素上的属性来应用这些特性,并在应用的路由中提供相应的支持。 | | 有什么陷阱或限制吗? | 必须注意确保定义了合适的总括路线和重定向,以确保应用不会向用户显示空窗口或通过导航元素提供令人困惑的反馈。 | | 还有其他选择吗? | 这些特性是可选的,你不必使用它们。 |

23-2 总结了本章内容。

表 23-2

章节总结

|

问题

|

解决办法

|

列表

| | --- | --- | --- | | 配置用于导航的元素 | 使用由router-link元素提供的属性 | 4–7 | | 当元素与活动 URL 匹配时设置元素样式 | 定义选择router-link-activerouter-link-exact-active类的样式 | eight | | 确保仅当元素与活动路线完全匹配时,才设计元素的样式 | 应用exact属性 | nine | | 更改用于指示活动航路的类别 | 使用exact-active-classactive-class属性 | Ten | | 创建嵌套路线 | 在应用中应用多个router-view元素,并用使用children属性定义的路由来定位它们 | 11–17 |

为本章做准备

在本章中,我继续使用第 23 章中的 productapp 项目。为了准备本章,我已经注释掉了ProductDisplay组件中的create方法,如清单 23-1 所示。该方法中的语句用于演示第 20 章中数据存储的使用,本章并不需要这些语句,因为每次创建ProductDisplay组件的新实例时,这些语句都会导致数据存储值重置。

...
<script>
    import { mapState, mapMutations, mapActions, mapGetters } from "vuex";

    export default {
        computed: {
            ...mapState(["products"]),
            ...mapState({
                useStripedTable: state => state.prefs.stripedTable
            }),
            ...mapGetters({
                tableClass: "prefs/tableClass",
                editClass: "prefs/editClass",
                deleteClass: "prefs/deleteClass"
            })
        },
        methods: {
            editProduct(product) {
                this.selectProduct(product);
                this.$router.push("/edit");
            },
            createNew() {
                this.selectProduct();
                this.$router.push("/edit");
            },
            ...mapMutations({
                selectProduct: "selectProduct",
                setEditButtonColor: "prefs/setEditButtonColor",
                setDeleteButtonColor: "prefs/setDeleteButtonColor"
            }),
            ...mapActions({
                deleteProduct: "deleteProductAction"
            })
        },
        //created() {

        //    this.setEditButtonColor(false);

        //    this.setDeleteButtonColor(false);

        //}

    }
</script>
...

Listing 23-1Disabling a Method in the ProductDisplay.vue File in the src/components Folder

要启动 RESTful web 服务,打开命令提示符并运行清单 23-2 中的命令。

npm run json

Listing 23-2Starting the Web Service

打开第二个命令提示符,导航到productapp目录,运行清单 23-3 中所示的命令来启动 Vue.js 开发工具。

npm run serve

Listing 23-3Starting the Development Tools

一旦初始捆绑过程完成,打开一个新的浏览器窗口并导航到http://localhost:8080,在那里你将看到示例应用,如图 23-1 所示。

小费

你可以从 https://github.com/Apress/pro-vue-js-2 下载本章以及本书其他章节的示例项目。

img/465686_1_En_23_Fig1_HTML.jpg

图 23-1

运行示例应用

使用路由器链接元素

router-link元素比它第一次出现时更加灵活,并且支持一些有用的选项来定制它生成的 HTML 元素,并向用户提供有用的反馈。为了准备接下来的部分,我在App组件的模板中添加了router-link元素,如清单 23-4 所示。

<template>
    <div class="container-fluid">
        <div class="row">

            <div class="col text-center m-2">

                <router-link to="/list" class="m-1">List</router-link>

                <router-link to="/create" class="m-1">Create</router-link>

            </div>

        </div>

        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'App',
        created() {
            this.$store.dispatch("getProductsAction");
        }
    }
</script>

Listing 23-4Adding Navigation Elements in the App.vue File in the src Folder

这些router-link元素作为组件模板的一部分被处理,并被转换成锚元素,如图 23-2 所示。

img/465686_1_En_23_Fig2_HTML.jpg

图 23-2

使用路由器链接元素导航

通过应用表 23-3 中描述的属性来配置router-link元素,其中最有用的我将在接下来的章节中演示。

表 23-3

路由器链路属性

|

名字

|

描述

| | --- | --- | | tag | 该属性指定了转换router-link元素时将生成的 HTML 元素的标记类型,如“选择元素类型”一节所述。 | | event | 该属性指定将触发导航的事件,如“选择导航事件”一节中所述。 | | exact | 此属性指定在标识对应于活动路由的元素时是否使用部分 URL 匹配,如“设计路由器链接元素”一节中所述。 | | active-class | 该属性指定当活动 URL 以元素的导航目标开始时,元素将被添加到的类,如“设计路由器链接元素”一节中所述。 | | exact-active-class | 该属性指定当活动 URL 匹配元素的导航目标时,元素将被添加到的类,如“设计路由器链接元素”一节中所述。 | | to | 该属性指定了导航位置,并将向浏览器的历史记录中添加一个条目,相当于第 22 章中描述的push导航方法。 | | replace | 该属性指定导航位置,但不会向浏览器的历史记录中添加条目,相当于第 22 章中描述的replace导航方法。 | | append | 此属性指定一个相对 URL,当您使用用户提供的数据进行导航,并希望确保导航包含在应用的特定部分时,此属性会很有用。 |

选择元素类型

默认情况下,router-link元素被转换成锚点,也就是带有a标签的元素。如果您使用浏览器的 F12 工具来检查文档对象模型,您可以看到我在清单 23-4 中添加的router-link元素是如何被转换的。

...
<div class="col text-center m-2">
    <a href="/list" class="m-1">List</a>

    <a href="/create" class="m-1">Create</a>

</div>
...

小费

如果你点击了其中一个a元素,你会看到它已经被添加到了router-link-activerouter-link-exact-active类中。我将在“设计路由器链接元素”一节中解释这些元素的含义。

tag属性可用于选择不同的元素类型,当router-link元素被转换时,该元素类型将代替锚点使用。当您想要以不能使用a元素的方式呈现一系列导航元素时,或者当您想要在选择器不能匹配锚元素的情况下应用 CSS 样式时,这是非常有用的。在清单 23-5 中,我使用了tag属性来填充一个包含导航元素的列表。

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <ol>

                    <router-link tag="li" to="/list">List</router-link>

                    <router-link tag="li" to="/create">Create</router-link>

                </ol>

            </div>
        </div>
        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'App',
        created() {
            this.$store.dispatch("getProductsAction");
        }
    }
</script>

Listing 23-5Specifying Tag Type in the App.vue File in the src Folder

tag属性指定当组件的模板被处理时,router-link元素应该被替换为li元素。如果保存更改并使用浏览器的 F12 开发工具来检查文档对象模型,您将看到以下元素:

...
<ol>
    <li class="">List</li>

    <li class="">Create</li>

</ol>
...

当你点击其中一个li元素时,导航发生,如图 23-3 所示。

img/465686_1_En_23_Fig3_HTML.jpg

图 23-3

更改导航元素类型

选择导航事件

默认的导航事件是click,这意味着当用户点击由router-link元素创建的元素时,导航被执行。event属性用于指定一个可选事件,它允许以不同的方式进行导航。在清单 23-6 中,我使用了event属性,这样当用户将鼠标指针移动到导航元素上时就会执行导航。

警告

用户希望当他们点击一个元素时会出现导航,因为这是大多数 web 应用的工作方式。小心使用event属性,因为您很容易混淆您的用户并产生意想不到的结果。

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <ol>
                    <router-link tag="li" event="mouseenter" to="/list">

                        List

                    </router-link>

                    <router-link tag="li" event="mouseenter" to="/create">

                        Create

                    </router-link>

                </ol>
            </div>
        </div>
        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'App',
        created() {
            this.$store.dispatch("getProductsAction");
        }
    }
</script>

Listing 23-6Specifying the Navigation Event in the App.vue File in the src Folder

当鼠标指针进入由 HTML 元素占据的浏览器窗口区域时,触发mouseenter事件,这意味着无需用户点击鼠标按钮就可以进行导航。

设计路由器链接元素的样式

当你对router-link元素应用样式时,重要的是要记住你是在对router-link被转换成的元素进行样式化,而不是对router-link元素本身。在清单 23-7 中,我为App组件添加了一个style属性,用于定义导航元素的样式。

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <ol>
                    <router-link tag="li" event="mouseenter" to="/list">
                        List
                    </router-link>
                    <router-link tag="li" event="mouseenter" to="/create">
                        Create
                     </router-link>
                </ol>
            </div>
        </div>
        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'App',
        created() {
            this.$store.dispatch("getProductsAction");
        }
    }
</script>

<style scoped>

    router-link { text-align: right; color: yellow; background-color: red; }

    li { text-align: left; color:blue; background-color: lightblue; }

</style>

Listing 23-7Styling Navigation Elements in the App.vue File in the src Folder

重要的是要记住,是浏览器在将router-link元素转换成由tag属性指定的元素类型之后评估样式选择器。出于这个原因,清单 23-7 中定义的第一个样式将不匹配任何元素,因为在组件的模板被处理后没有router-link元素可供选择。这是混淆的一个常见原因,尤其是在使用自动管理 CSS 样式的工具时。第二种样式的选择器匹配转换后的元素,并将应用于组件的内容,如图 23-4 所示。

小费

您可能需要重新加载浏览器才能看到如图 23-4 所示的新样式。

img/465686_1_En_23_Fig4_HTML.jpg

图 23-4

样式导航元素

响应活动路由

当当前 URL 匹配导航元素的目标时,Vue Router 包将该元素添加到router-link-activerouter-link-exact-active类中,这两个类可用于应用向用户提供反馈的样式。在清单 23-8 中,我定义了在选择器中使用这些类的样式。我还删除了事件属性,这样当用户点击时就会出现导航,我还添加了新的router-link元素,导航到/edit/edit/1URL。

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <ol>
                    <router-link tag="li" to="/list">List</router-link>

                    <router-link tag="li" to="/create">Create</router-link>

                    <router-link tag="li" to="/edit">Edit</router-link>

                    <router-link tag="li" to="/edit/1">Edit Kayak</router-link>

                </ol>
            </div>
        </div>
        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'App',
        created() {
            this.$store.dispatch("getProductsAction");
        }
    }
</script>

<style scoped>
    li { text-align: left; color:blue; background-color: lightblue; }
    .router-link-active { font-size: xx-large; }

    .router-link-exact-active { font-weight: bolder; }

</style>

Listing 23-8Styling the Active Navigation Element in the App.vue File in the src Folder

当路线发生变化时,导航元素会自动添加到类中或从类中移除。如果由to属性指定的目标与当前 URL 完全匹配,那么元素将被添加到router-link-exact-active类中,该类应用使元素文本加粗的样式。如果当前 URL 以由to属性指定的目标开始,则元素被添加到router-link-active类中。你可以通过点击编辑 Kayak 链接看到不同之处,这将导航到/edit/1网址。目标为/edit的编辑元素被添加到router-link-active类中,因为当前 URL 以其目标开始:/edit/1/edit开始。Edit Kayak 链接被添加到这两个类中,因为当前 URL 以其目标开始,并且与其目标完全匹配。结果是编辑元素以更大的文本显示,而编辑 Kayak 链接以同样加粗的更大文本显示,如图 23-5 所示。

img/465686_1_En_23_Fig5_HTML.jpg

图 23-5

响应活动的路由类别

使用router-link-active类进行部分 URL 匹配并不总是有用的,可以通过向router-link元素添加exact属性来禁用,如清单 23-9 所示。

...
<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <ol>
                    <router-link tag="li" to="/list">List</router-link>
                    <router-link tag="li" to="/create">Create</router-link>
                    <router-link tag="li" to="/edit" exact>Edit</router-link>

                    <router-link tag="li" to="/edit/1">Edit Kayak</router-link>
                </ol>
            </div>
        </div>
        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>
...

Listing 23-9Disabling Partial URL Matching in the App.vue File in the src Folder

无论分配给它的值是什么,exact属性都将生效,并且禁用部分匹配特性的是属性的存在,而不是它的值。当用户导航到/edit/1 URL 时,清单中应用的属性阻止编辑元素被添加到router-link-active类,如图 23-6 所示。

img/465686_1_En_23_Fig6_HTML.jpg

图 23-6

禁用部分 URL 映射

更改活动路线类别

当你使用一个 CSS 框架时,比如我在本书中使用的 Bootstrap 框架,你会发现经常有一些类被用来指示一个元素何时是活动的,这些类并不对应于 Vue 路由器使用的类的名称。active-classexact-active-class属性可以用来指定类的名称,当元素的目标与当前 URL 匹配时,应该将元素添加到这些类中。在清单 23-10 中,我用一组更传统的按钮元素替换了导航元素列表,并使用了active-class-exact属性来指定用于指示活动按钮的引导类的名称。我还删除了style元素,因为我不再需要定制的 CSS 样式。

小费

您可以通过使用路由配置对象中的linkActiveClasslinkExactActiveClass属性来更改用于全局指示路由的类,这意味着您不必在每个元素上指定这些类。

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <div class="btn-group">

                    <router-link tag="button" to="/list"

                                 exact-active-class="btn-info"

                                 class="btn btn-primary">

                        List

                    </router-link>

                    <router-link tag="button" to="/create"

                                 exact-active-class="btn-info"

                                 class="btn btn-primary">

                        Create

                    </router-link>

                    <router-link tag="button" to="/edit"

                                 exact-active-class="btn-info"

                                 class="btn btn-primary">

                        Edit

                    </router-link>

                    <router-link tag="button" to="/edit/1"

                                 exact-active-class="btn-info"

                                 class="btn btn-primary">

                        Edit Kayak

                    </router-link>

                </div>

            </div>
        </div>
        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'App',
        created() {
            this.$store.dispatch("getProductsAction");
        }
    }
</script>

Listing 23-10Specifying Active Class Names in the App.vue File in the src Folder

新的router-link元素都被赋给了btnbtn-primary类,这两个类是样式按钮的引导类,当它们表示活动路线时将被赋给btn-info类,效果如图 23-7 所示。

img/465686_1_En_23_Fig7_HTML.jpg

图 23-7

更改活动路线类别

创建嵌套路线

到目前为止,路由示例都假设应用只有一组要显示的组件,并且当选择其中一个组件时,它将总是显示相同的内容。在复杂的应用中,一个顶级组件可能需要显示不同的子组件,为了支持这一需求,Vue Router 包支持嵌套路由,也称为子路由

规划应用布局

使用嵌套路由时,理解您的目标很重要。对于示例应用,我将为用户提供顶级导航元素,允许用户在产品相关功能和设置应用首选项的组件之间进行选择。图 23-8 显示了我打算创建的应用结构。

img/465686_1_En_23_Fig8_HTML.jpg

图 23-8

应用的结构

决定应用将支持的 URL 集使得创建路由更加简单。我将在示例应用中使用的 URL 在表 23-4 中描述,并遵循我在早期示例中使用的相同基本方法。

表 23-4

示例应用的 URL

|

统一资源定位器

|

描述

| | --- | --- | | /products/table | 该 URL 将显示产品列表。 | | /products/create | 该 URL 将显示用于创建新产品的编辑器。 | | /products/edit/10 | 此 URL 将显示用于修改指定产品的编辑器。 | | /preferences | 此 URL 将显示首选项设置。 |

向项目中添加组件

为了创建我需要的结构,我需要向项目添加一个组件。首先,我在src/components文件夹中添加了一个名为Preferences.vue的文件,其内容如清单 23-11 所示。

<template>
    <div>
        <h4 class="bg-info text-white text-center p-2">Preferences</h4>
        <div class="form-check">
            <input class="form-check-input" type="checkbox"
                   v-bind:checked="primaryEdit" v-on:input="setPrimaryEdit">
            <label class="form-check-label">Primary Color for Edit Buttons</label>
        </div>
        <div class="form-check">
            <input class="form-check-input" type="checkbox"
                   v-bind:checked="dangerDelete" v-on:input="setDangerDelete">
            <label class="form-check-label">Danger Color for Delete Buttons</label>
        </div>
    </div>
</template>

<script>

    import { mapState } from "vuex";

    export default {
        computed: {
            ...mapState({
                primaryEdit: state => state.prefs.primaryEditButton,
                dangerDelete: state => state.prefs.dangerDeleteButton
            })
        },
        methods: {
            setPrimaryEdit() {
                this.$store.commit("prefs/setEditButtonColor", !this.primaryEdit);
            },
            setDangerDelete() {
                this.$store.commit("prefs/setDeleteButtonColor", !this.dangerDelete);
            }
        }
    }
</script>

Listing 23-11The Contents of the Preferences.vue File in the src/components Folder

这是将向用户显示首选项的组件。它使用复选框显示数据存储中两个状态属性的值,并在用户切换控件时更新这些值。这些是相同的数据存储状态属性,用于设置由ProductDisplay组件显示的编辑和删除按钮的颜色。

接下来,我在src/components文件夹中添加了一个名为Products.vue的文件,其内容如清单 23-12 所示。

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

Listing 23-12The Contents of the Products.vue file in the src/component Folder

这个组件只包含一个template元素,而这个元素又只包含一个router-view元素。这个组件将允许我显示产品列表或编辑器。

定义路线

组件就绪后,我可以定义应用的路由配置来实现表 23-4 中描述的 URL 集,如清单 23-13 所示。

import Vue from "vue";
import VueRouter from "vue-router";

import ProductDisplay from "../components/ProductDisplay";
import ProductEditor from "../components/ProductEditor";

import Preferences from "../components/Preferences";

import Products from "../components/Products";

Vue.use(VueRouter);

export default new VueRouter({
    mode: "history",
    routes: [
        { path: "/preferences", component: Preferences},

        { path: "/products", component: Products,

            children: [

                { name: "table", path: "list", component: ProductDisplay},

                { name: "editor", path: ":op(create|edit)/:id(\\d+)?",

                      component: ProductEditor},

                { path: "", redirect: "list" }

            ]

        },

        { path: "/edit/:id", redirect: to => `/products/edit/${to.params.id}`},

        { path: "*", redirect: "/products/list" }

    ]
})

Listing 23-13Defining Routes in the index.js File in the src/router Folder

这些路由包含大量信息,因此我将逐一展开,并解释它们如何构成我在本节开始时描述的结构。第一条路线遵循您在前面的示例中看到的格式,如下所示:

...
{ path: "/preferences", component: Preferences},
...

这个路由告诉 Vue Router 在 URL 为/preferences时显示Preferences组件。该路线中没有使用动态段、名称、重定向或其他特殊功能,所选组件将显示在App组件模板中定义的router-view元素中。

下一条路线更复杂,最好是逐步接近。第一部分很简单。

...
{ path: "/products", component: Products,
...

pathcomponent属性告诉 Vue Router 当 URL 为/products时应该显示Products组件。与前面的路线一样,Products组件将显示在App组件模板的router-view元素中。但是Product组件的模板还包含一个router-view元素,必须为其选择一个组件,这就是该路由定义的children属性的用途:

...
{ path: "/products", component: Products,
    children: [

        { name: "table", path: "list", component: ProductDisplay},

        { name: "editor", path: ":op(create|edit)/:id(\\d+)?",

            component: ProductEditor},

        { path: "", redirect: "list" }

    ]

},
...

children属性用于定义一组路由,这些路由将应用于Products组件模板中的router-view元素。每个子路由的path属性的值与其父路由的path相结合,以匹配一个 URL 并选择一个组件,以便为/products/list URL 选择ProductDisplay组件。子路由可以包括动态段和正则表达式,这可以在为/create/edit/idURL 选择ProductEditor组件的路由中看到。

children部分中的最后一个路由是一个将执行重定向的总括路由,这样任何以/products开头但与前两个路由不匹配的 URL 都将被重定向到/products/list。注意,这个路由的路径是一个空字符串,而不是一个星号,因为我想匹配没有这个段的值的 URL。

处理旧的 URL

当现有应用支持的 URL 集发生变化时,一定要确保更新组件中使用的 URL,或者在新旧 URL 之间创建重定向或别名。编辑功能以前通过/edit/:id路径访问,但现在通过/products/edit/:id访问。为了确保旧的 URL 仍然有效,我在清单 23-13 中定义了重定向路由。

...
{ path: "/edit/:id", redirect: to => `/products/edit/${to.params.id}`},
...

在第 22 章中,我使用一个固定的 URL 创建了一个重定向。这在这种情况下是行不通的,因为我需要将动态id段的值传递给新路线。如清单所示,重定向也可以表示为一个函数,它接收匹配的路由并返回重定向 URL。在这个例子中,重定向函数接收路由并组成重定向 URL,以便它包含id值。

创建导航元素

在清单 23-14 中,我用针对表 23-4 中描述的 URL 的按钮替换了App组件模板中的导航按钮。

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <div class="btn-group">
                    <router-link tag="button" to="/products" active-class="btn-info"

                            class="btn btn-primary">

                        Products

                    </router-link>

                    <router-link tag="button" to="/preferences"

                             active-class="btn-info" class="btn btn-primary">

                        Preferences

                    </router-link>

                </div>
            </div>
        </div>
        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'App',
        created() {
            this.$store.dispatch("getProductsAction");
        }
    }
</script>

Listing 23-14Navigating to the New URLs in the App.vue File in the src Folder

这些router-link元素将被转换成button元素,这些元素导航到/products/preferencesURL,并使用 Bootstrap btnbtn-primary类进行样式化,这些类在 Bootstrap 配色方案的原色中应用基本的按钮样式。

为了表明什么时候button代表活动路线,我将元素添加到了btn-info类,该类使用active-class属性为按钮应用了不同的引导颜色。

测试嵌套路由

支持嵌套的router-view元素所需的所有更改都已就绪。您可以通过导航到http://localhost:8080并使用ProductsPreferences按钮来更改App组件的模板中的router-view元素的内容,这将产生如图 23-9 所示的结果。

img/465686_1_En_23_Fig9_HTML.jpg

图 23-9

为顶级路由器视图元素选择组件

要查看嵌套的router-view元素,单击 Products 按钮,然后单击表格中显示的编辑按钮之一。Products组件中的router-view元素所显示的组件将变为显示编辑器,点击保存或取消按钮可以返回到表格视图,如图 23-10 所示。

img/465686_1_En_23_Fig10_HTML.jpg

图 23-10

为嵌套路由器视图元素选择组件

请注意,使用命名路由的router-link元素和代码会自动将使用新路由的 URL 作为目标。当我在清单 23-13 中定义路由时,我将在第 22 章中定义的名称应用于新配置中的相应路由。

...
{ path: "/products", component: Products,
    children: [
        { name: "table", path: "list", component: ProductDisplay},
        { name: "editor", path: ":op(create|edit)/:id(\\d+)?",
            component: ProductEditor},
        { path: "", redirect: "list" }
    ]
},
...

当使用命名路由的router-link元素被处理时,结果是一个指向指定路由的 URL,这意味着名称保持有效,即使它们所涉及的路由发生了变化。您可以在表格视图中显示的编辑按钮中看到这一点,这些按钮是用这个router-link元素创建的:

...
<router-link v-bind:to="{name: 'editor', params: { op: 'edit', id: p.id}}"

        v-bind:class="editClass" class="btn btn-sm">
    Edit
</router-link>
...

使用名称设置to属性,该名称指定编辑器路径。处理此元素时,结果是一个锚元素,其目标对应于命名的路由,如下所示:

...
<a href="/products/edit/1" class="btn btn-sm btn-secondary">
    Edit
</a>
...

使用命名路由可能需要笨拙的代码和 HTML,但结果可以是更灵活和更健壮的应用,该应用适应其路由配置的变化,而不需要其组件的相应变化。

使用命名路由器视图元素

一些组件需要在同一个模板中有多个router-view元素,这样就可以动态地选择两个或更多的子组件。当router-view元素在同一个模板中时,name属性用于区分它们,然后在路由中使用这些名称来选择将要显示的组件。

为了帮助演示这个特性,我通过在src/components文件夹中添加一个名为SideBySide.vue的文件来创建一个新组件,其内容如清单 23-15 所示。

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <h3 class="bg-secondary text-white text-center p-2">Left View</h3>
                <router-view name="left" class="border border-secondary p-2" />
            </div>
            <div class="col text-center m-2">
                <h3 class="bg-secondary text-white text-center p-2">Right View</h3>
                <router-view name="right" class="border border-secondary p-2" />
            </div>
        </div>
    </div>
</template>

Listing 23-15The Contents of the SideBySide.vue File in the src/components Folder

这个组件有一个包含两个router-view元素的模板元素,使用name属性区分这两个元素,一个命名为left,另一个命名为right。引导类和结构元素将向用户并排展示router-view元素的内容。为了瞄准新的router-view元素,我将添加对表 23-5 中描述的 URL 的支持。

表 23-5

命名路由器视图元素的 URL

|

统一资源定位器

|

描述

| | --- | --- | | /named/tableleft | 这个 URL 将在left元素中显示产品表,在right元素中显示编辑器。 | | /named/tableright | 这个 URL 将在right元素中显示产品表,在 lef t元素中显示编辑器。 |

为了定位这些 URL,我将清单 23-16 中所示的导航元素添加到顶级App组件的模板中。

<template>
    <div class="container-fluid">
        <div class="row">
            <div class="col text-center m-2">
                <div class="btn-group">
                    <router-link tag="button" to="/products"
                        active-class="btn-info" class="btn btn-primary">
                            Products
                    </router-link>
                    <router-link tag="button" to="/preferences"
                        active-class="btn-info" class="btn btn-primary">
                            Preferences
                    </router-link>
                    <router-link to="/named/tableleft" class="btn btn-primary"

                            active-class="btn-info">

                        Table Left

                    </router-link>

                    <router-link to="/named/tableright" class="btn btn-primary"

                            active-class="btn-info">

                        Table Right

                    </router-link>

                </div>
            </div>
        </div>
        <div class="row">
            <div class="col m-2">
                <router-view></router-view>
            </div>
        </div>
    </div>
</template>

<script>

    export default {
        name: 'App',
        created() {
            this.$store.dispatch("getProductsAction");
        }
    }
</script>

Listing 23-16Adding Navigation Elements in the App.vue File in the src Folder

为了完成对新 URL 的支持并以命名的router-view元素为目标,我创建了清单 23-17 中所示的路由。

import Vue from "vue";
import VueRouter from "vue-router";

import ProductDisplay from "../components/ProductDisplay";
import ProductEditor from "../components/ProductEditor";
import Preferences from "../components/Preferences";
import Products from "../components/Products";

import SideBySide from "../components/SideBySide";

Vue.use(VueRouter);

export default new VueRouter({
    mode: "history",
    routes: [
        { path: "/preferences", component: Preferences},
        { path: "/products", component: Products,
            children: [
                { name: "table", path: "list", component: ProductDisplay},
                { name: "editor", path: ":op(create|edit)/:id(\\d+)?",
                      component: ProductEditor},
                { path: "", redirect: "list" }
            ]
        },
        { path: "/edit/:id", redirect: to => `/products/edit/${to.params.id}`},

        { path: "/named", component: SideBySide,

            children:[

                {   path: "tableleft",

                    components: {

                        left: ProductDisplay,

                        right: ProductEditor

                    }

                },

                {   path: "tableright",

                        components: {

                        left: ProductEditor,

                        right: ProductDisplay

                    }

                }

            ]

        },

        { path: "*", redirect: "/products" }
    ]
})

Listing 23-17Adding Routes in the index.js File in the src/router Folder

当定义以命名的router-view元素为目标的路由时,使用components属性。该属性被赋予一个对象,其属性是router-view元素的名称,其值是应该显示的组件,如下所示:

...
{ path: "tableleft",
  components: {

      left: ProductDisplay,

      right: ProductEditor

  }

},
...

components属性告诉 Vue 路由器包在名为leftrouter-view元素中显示ProductComponent,在名为rightrouter-view元素中显示ProductEditor组件。

注意

以命名元素为目标的属性是components(复数),而不是在清单 23-17 中的其他路径中使用的component(单数)属性。

要查看结果,导航到http://localhost:8080并点击左侧表格和右侧表格按钮,这两个按钮指向清单 23-17 中定义的路线的 URL,产生如图 23-11 所示的结果。

img/465686_1_En_23_Fig11_HTML.jpg

图 23-11

使用命名路由器视图元素

摘要

在本章中,我描述了在 Vue.js 应用中使用 URL 路由时可用的一些高级功能。我解释了如何配置router-link元素以产生不同的 HTML 元素并响应不同的事件,以及如何设计导航元素的样式以向用户提供反馈。对于router-view元素,我向您展示了如何使用嵌套路由,以便应用可以包含多个元素,以及当这些元素在同一个模板中定义时如何命名它们。在下一章,我将描述高级 URL 路由特性。