AI 摘要

文章系统梳理 Vue 路由:先对比前后端路由,强调 SPA 用 hash 或 history 模式免刷新跳转;详解 vue-router 配置、router-link/view 使用;再讲 query、params、嵌套、命名路由/视图、编程式导航及路由守卫,附完整示例代码与动图,覆盖单页应用导航核心实践。

初识路由

路由概述

程序开发中的路由分为后端路由和前端路由。

后端路由通过用户请求的 URL 分发到具体的处理程序,浏览器每次跳转到不同的 URL,都会重新访问服务器。

前端路由是一种在单页应用(SPA)中管理页面导航的技术。其主要作用是根据用户的操作(如点击链接)动态地加载和显示不同的页面内容,而无需重新加载整个页面。

单页面应用的核心思想之一,就是更新局部的视图,而不重新请求页面。

对于单页面应用来说,主要通过 URL 中的 hash 符号(# 号)来实现不同页面之前的切换。

前端路由在访问一个新页面的时候,仅仅只是变换了 hash 值,没有和服务端交互,不存在网络延迟,提升了用户体验。

vue-router 初体验

vue-router 是 Vue 官方推出的路由管理器,主要用于管理 URL,实现 URL 和组件的对应,通过 URL 进行组件之间的切换。

vue-router 3.x 文档:https://v3.router.vuejs.org/zh/

vue-router 的使用:

  • 新建 HTML 文件,并使用 script 标签引入 vue.js、vue-router.js。
  • 创建路由对象,配置一组路由。
  • 实例化 Vue 实例,并将路由对象传递给 Vue 实例。
  • 在 app 盒子中,使用 router-link 标签创建路由跳转按钮、使用 router-view 标签定义视图占位符。

示例:vue-router 初体验

入口页面(index.html):

<div id="app">
    <!--
        router-link:相当于a标签
        to:相当于a标签的href属性
        tag:把a替换成xxx
     -->
    <router-link to="/login" tag="button">登录</router-link>
    <router-link to="/register">注册</router-link>

    <!-- router-view:充当视图占位符 -->
    <router-view></router-view>
</div>

<script src="vue.js"></script>
<script src="vue-router.js"></script>
<script>
    // 定义路由对象
    // router路由管理者
    let router = new VueRouter({
        // routes:一组路由
        routes: [
            // {}:一条路由
            {
                path: '/login', // 路由地址
                component: {    // 组件对象,不是组件名称
                    template: '<h3>登录页面</h3>'
                }
            },
            {
                path: '/register',
                component: {
                    template: '<h3>注册页面</h3>'
                }
            }
        ]
    })

    let vm = new Vue({
        el: '#app',
        data: {},
        // 将路由对象传递给Vue实例
        // router属性:路由对象
        router: router
    })
</script>

示例效果:


路由模式

使用 vue-router 实现前端路由时,提供了两种模式。

  • hash:vue-router 的默认模式,URL 中会包含 # 号。hash 模式利用了 window 监听 onhashchange 事件来实现的。
  • history:URL 中不会包含 # 号。history 模式利用了 window.history.pushState 方法来实现的。

路由模式配置的基本用法:

let router = new VueRouter({
    mode: 'history',
    // ...
})

路由对象属性

路由对象表示当前激活的路由的状态信息,包含了当前 URL 解析得到的信息,还有 URL 匹配到的路由记录。路由对象是不可变的,每次成功地导航后都会产生一个新的对象。

this.$route 表示当前的路由对象。

路由对象的基本属性:

名称描述
$route.path路由的路径
$route.queryURL 的查询参数,是一个 {key: value} 对象
$route.params路由跳转携带参数,是一个 {key: value} 对象
$route.hash在 histoty 模式下获取当前路由的 hash 值,带 # 号
$route.fullPath完成解析后的 URL,包含查询参数和 hash 的完整路径
$route.name路由的名称

路由传参

query 传参

通过 query 方式传递参数,类似于 GET 请求,在页面跳转的时候,可以在地址栏看到请求参数。


示例:query 传参

入口页面(index.html):

<div id="app">
    <router-link to="/user?id=77&name=多仔">登录</router-link>
    <router-view></router-view>
</div>

<script>
    let router = new VueRouter({
        routes: [
            {
                path: '/user',
                component: {
                    template: '<h3>欢迎您,{{this.$route.query.name}}</h3>',
                    created() {
                        console.log(this.$route)
                    }
                }
            }
        ]
    })

    let vm = new Vue({
        el: '#app',
        router: router
    })
</script>

示例效果:


params 传参

使用 params 方式传递参数,类似于 POST 请求和 RESTFul 风格,将参数放在路径中或直接隐藏。


示例:params 传参

入口页面(index.html):

<div id="app">
    <router-link to="/user/77/多仔">登录</router-link>
    <router-view></router-view>
</div>

<script>
    let router = new VueRouter({
        routes: [
            {
                // 使用:xx指定参数占位符
                path: '/user/:id/:name',
                component: {
                    template: '<h3>欢迎您,{{this.$route.params.name}}</h3>',
                    created() {
                        console.log(this.$route)
                    }
                }
            }
        ]
    })

    let vm = new Vue({
        el: '#app',
        data: {},
        router: router
    })
</script>

示例效果:


嵌套路由

嵌套路由概述

嵌套路由就是在一个路由中嵌套一组子路由。


示例:嵌套路由

入口页面(index.html):

<style>
    ul, li, h1 {
        padding: 0;
        margin: 0;
        list-style: none;
    }
    #app {
        width: 100%;
        display: flex;
        flex-direction: row;
    }
    ul {
        width: 200px;
        flex-direction: column;
        color: #fff;
    }
    li {
        flex: 1;
        background: #000;
        margin:5px auto;
        text-align: center;
        line-height: 30px;
    }
    .about-detail {
        flex:1;
        margin-left: 30px;
    }
    .about-detail h1{
        font-size: 24px;
        color: blue;
    }
</style>

<div id="app">
    <ul>
        <router-link to="/about" tag="li">关于公司</router-link>
        <router-link to="/contact" tag="li">联系我们</router-link>
    </ul>
    <router-view></router-view>
</div>

<!-- 关于公司-template -->
<template id="about-template">
    <div class="about-detail">
        <h1>北京xx科技有限公司简介</h1>
        <router-link to="/about/detail">公司简介</router-link> |
        <router-link to="/about/governance">公司治理</router-link>
        <router-view></router-view>
    </div>
</template>

<!-- 联系我们-template -->
<template id="contact-template">
    <div class="about-detail">
        <h1>联系我们</h1>
        <p>公司位于北京市海淀区中关村科技园内,主营业务包括餐饮娱乐、服装设计等</p>
    </div>
</template>

<!-- 公司简介-template -->
<template id="detail-template">
    <div>
        <p>xx是全球领先... ...</p>
    </div>
</template>

<!-- 公司治理-template -->
<template id="governance-template">
    <div>
        <p>公司坚持以客户为中心、以奋斗者为本... ...</p>
    </div>
</template>

<script>
    let router = new VueRouter({
        routes: [
            {
                // 关于公司
                path: '/about',
                component: {
                    template: '#about-template'
                },
                // “关于公司”路由的子路由列表
                children: [
                    {
                        // 公司治理
                        // 子路由的path属性前不能带有/
                        // 子路由地址 = 父路由 + "/" + 子路由
                        path: 'governance',
                        component: {
                            template: '#governance-template'
                        },
                    },
                    {
                        // 公司简介
                        path: 'detail',
                        component: {
                            template: '#detail-template'
                        },
                    }
                ]
            },
            {
                // 联系我们
                path: '/contact',
                component: {
                    template: '#contact-template'
                }
            }
        ]
    })

    let vm = new Vue({
        el: '#app',
        router: router
    })
</script>

示例效果:


命名路由

命名路由概述

vue-router 提供了一种隐式的引用路径,即命名路由。

在创建路由时,给路由指定 name 名称,链接路由跳转时,通过路由的名称取代路由地址。


示例:命名路由

入口页面(index.html):

<div id="app">
    <!--
        :to:为router-link绑定要跳转的路由对象的信息
        name:路由名称
        params:要使用params方式传递的参数
    -->
    <router-link :to="{name: 'user', params: {name: '多仔'}}">登录</router-link>
    <router-view></router-view>
</div>

<script>
    let router = new VueRouter({
        routes: [
            {
                path: '/user',
                name: 'user',   // 指定路由名称
                component: {
                    template: '<h3>欢迎您,{{this.$route.params.name}}</h3>'
                }
            }
        ]
    })

    let vm = new Vue({
        el: '#app',
        router: router
    })
</script>

示例效果:


命名视图

命名视图概述

vue-router 提供了命名视图,可以通过命名视图来在同一个路由中渲染多个组件。


示例:命名视图

入口页面(index.html):

<div id="app">
    <!--
        name:为视图占位符取名字
        视图占位符默认的名字为default
    -->
    <router-view name="default"></router-view>
    <router-view name="main"></router-view>
</div>

<script>
    let router = new VueRouter({
        routes: [
            {
                path: '/',
                components: {
                    // 该路由对应多个组件
                    // 视图名称: 组件对象
                    'default': {
                        template: '<h3>default</h3>'
                    },
                    'main': {
                        template: '<h3>main</h3>'
                    }
                }
            }
        ]
    })

    let vm = new Vue({
        el: '#app',
        router: router
    })
</script>

示例效果:


编程式导航

router.push

router.push 方法用于实现路由跳转,路由跳转时会在 history 中添加一条新的记录。

router.push 的基本用法:

router.push(跳转路径)
router.push({path: 跳转路径})
router.push({path: 跳转路径, query: query传参参数对象})
router.push({name: 跳转路由名称, params: params传参参数对象})

示例:router.push

入口页面(index.html):

<div id="app">
    <button @click="goStart1">跳转1</button>
    <button @click="goStart2">跳转2</button>
    <router-view></router-view>
</div>

<script>
    let router = new VueRouter({
        routes: [
            {
                path: '/user',
                name: 'user',
                component: {
                    template: '<div><p>用户名query:{{this.$route.query.name}}</p><p>用户名params:{{this.$route.params.name}}</p></div>'
                }
            }
        ]
    })

    let vm = new Vue({
        el: '#app',
        router: router,
        methods: {
            goStart1 () {
                // 使用path + query传递参数
                this.$router.push({ path: '/user', query: { name: 'admin' } })
            },
            goStart2 () {
                // 使用name + params传递参数
                this.$router.push({ name: 'user', params: { name: 'admin' } })
            }
        }
    })
</script>

示例效果:


router.replace

router.replace 方法用于实现路由替换,路由替换时不会在 history 中添加一条新的记录。

router.replace 的基本用法:

router.replace(替换路径或替换的路由对象)

router.go

router.go 方法用于实现路由的前进和后退,类似于 history.go、history.forward、history.back 等方法。

router.go 的基本用法:

// 路由前进
router.go(1)

// 路由后退
router.go(-1)

// 路由刷新
router.go(0)

路由守卫

全局前置守卫

全局前置守卫在路由跳转之前进行拦截,执行操作。

全局前置守卫是最常用的导航守卫,它主要作用于登录验证,获取用户权限信息等场景。

全局前置守卫的基本用法:

router.beforeEach((to, from, next) => {
    // to: 即将进入的目标路由对象
    // from: 正在离开的路由对象
    // next: 路由下一步操作的函数
    // next(): 直接跳转到目标路由
    // next('/path'): 跳转到一个不同的地址
 
    // ...
})

示例:全局前置守卫

入口页面(index.html):

router.beforeEach((to, from, next) => {
    if (用户未登录) {
        // 重定向到登录页面
        next({ path: '/login' })
    } else {
        // 正常跳转
        next()
    }
});

全局后置守卫

全局后置守卫在路由跳转之前进行拦截,执行操作。

全局后置守卫主要作用于滚动条回调、更新页面标题等场景。

全局后置守卫的基本用法:

router.afterEach((to, from) => {
    // ...
})

示例:全局后置守卫

入口页面(index.html):

router.afterEach((to, from) => {
    window.scrollTo(0, 0);
})