海边的小溪鱼

vuePress-theme-reco 海边的小溪鱼    2017 - 2023
海边的小溪鱼 海边的小溪鱼

Choose mode

  • dark
  • auto
  • light
首页
分类
  • CSS
  • HTTP
  • Axios
  • jQuery
  • NodeJS
  • JavaScript
  • Vue2
  • Server
  • Vue3
标签
时间轴
更多
  • Gitee (opens new window)
Getee (opens new window)
author-avatar

海边的小溪鱼

28

文章

14

标签

首页
分类
  • CSS
  • HTTP
  • Axios
  • jQuery
  • NodeJS
  • JavaScript
  • Vue2
  • Server
  • Vue3
标签
时间轴
更多
  • Gitee (opens new window)
Getee (opens new window)

路由(vue-router)

vuePress-theme-reco 海边的小溪鱼    2017 - 2023

路由(vue-router)

海边的小溪鱼 2021-06-24 vue-router

路由的安装和配置-路由的基本使用-路由导航

# 路由的概念与原理

概念:Hash地址(path)与 组件(component) 之间的对应关系(不同地址,展示不同页面;不同页面显示不同地址)

前端路由的工作方式:
(1)用户点击页面上不同的路由链接,会导致 URL 地址栏中 的 Hash 值发生变化
(2)前端路由监听到 Hash 地址的变化,会把当前 Hash 地址对应的组件 渲染到浏览器中
前端路由底层实现原理:监听 浏览器的 onhashchange 事件(当 Hash 值发生改变时触发这个事件)

手动模拟前端路由
hash 值:URL 的锚部分 (以 '#' 号为开始),如:http://xxx.com/#/home,其中 #/home 就是哈希值
onhashchange 事件:当 哈希值 发生改变时触发
通过设置Location 对象 (opens new window) 的 location.hash (opens new window) 或 location.href (opens new window) 修改/获取 hash 值

<template>
    <div class="app-conuainer">
    	<h1>App 根组件</h1>
        
        <!-- 1. 点击页面上不同的路由链接,URL地址栏中的 Hash 值会发生变化 -->
        <a href="#/home">首页</a>
        <a href="#/movie">电影</a>
        <a href="#/about">关于</a>
        
        <!-- 2. 使用动态组件,按需展示相应的组件(默认展示 Home 组件) -->
        <components :is="comName"></components>
          
        <!-- 3. 监听到 Hash 值的变化,就让当前 Hash 地址对应的组件 展示出来 -->
        
    </div>
</template>

<script>
import Home from "@/components/Home.vue"
import Movie from "@/components/Movie.vue"
import About from "@/components/About.vue"

export default {
    name: "App", // 给当前组件取个名称
    data() {
        return {
            // 在动态组件的位置,要展示的组件名字,值必须是字符串
            comName: "Home"
        }
    },
    // 3. 监听 hash 值的变化(实际开发中不需要自己写路由,vue会帮我们自动创建路由)
    // 生命周期函数(组件已经创建)
    created() {
        // 只要当前 APP 组件一被创建,就立即监听 window 对象的 onhashchange 事件
        // onhashchange 事件:当 Hash 值发生改变时触发
        window.onhashchange = () => {
            console.log("监听到了 hash 地址的变化", location.hash)
            // 使用 switch 分支语句,来匹配 Hash 值,谁匹配执行谁
            switch (location.hash) {
                case "#/home":
                    this.comName = "Home"
                    break
                case "#/movie":
                    this.comName = "Movie"
                    break
                case "#/about":
                    this.comName = "About"
                    break
            }
        }
    }
    // 注册组件
    components: {
        Home,
        Movie,
        About
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 1. vue-router 安装和配置

注意:在 vue-cli 创建项目的时候,如果勾选了 vue-router,那么 webpack 会自动帮我们安装并配置路由

(1). 安装 vue-router 包:npm i vue-router@3.5.2 -S
(2). 创建路由模块:在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码:

// src/router/index.js 就是当前项目的路由模块

// 1.导入 Vue 和 VueRouter 包
import Vue from "vue"
import VueRouter from "vue-router"

// 2. 调用 Vue.use() 函数,把 VueRouter 安装为 Vue 的插件【Vue.use()函数,用于给项目安装插件】
Vue.use(VueRouter)

// 3. 创建路由的实例对象
const router = new VueRouter()

// 4. 向外共享路由的实例对象
export default router
1
2
3
4
5
6
7
8
9
10
11
12
13
14

(3). 在 main.js 入口文件中,导入并挂载路由模块

// main.js 入口文件

import router from "@/router"

new Vue({
    render: h => h(App),
    router // 挂载路由
}).$mount('#app')
1
2
3
4
5
6
7
8

# 2. Vue-router 的基本使用

注意

(1). 设置路由链接:<router-link to="/about"></router-link>
(2). 声明 <router-view></router-view> 来给相应的组件预留位置
(3). 在路由模块(src/router/index.js)中,导入并配置需要展示的组件(把相应的组件展示到预留的位置) avatar

<template>
    <div id="app">
        <!-- 1. 设置路由链接:使用 router-link 来代替 a 标签 -->
        <router-link to="/about">关于</router-link>
        
        <!-- router-view 是一个占位符,作用:把 "路由链接" 对应的 "组件" 展示到这个位置 -->
        <router-view></router-view>
    </div>
</template>
1
2
3
4
5
6
7
8
9
// src/router/index.js

// 1. 导入需要的组件
import About from "@/components/About.vue"

// 创建路由的实例对象
const router = new VueRouter({
    // 2. routes 是一个数组,作用:定义 "hash 地址" 与 "组件" 之间的对应关系
    routes: [
        // 路由规则:path 是路由链接, component 是对应的组件
        { path: "/about", component: About },
    ]
})
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3. 动态路由匹配

动态路由

(1). 把 Hash 地址中可变的部分,做为动态的参数项
(2). 在 vue-router 中使用英文的冒号(:参数项名称)来定义路由的参数项,参数项名称可自定义
(3). 动态路由,可以提高路由规则的复用性 avatar

/* 
动态路由匹配:将 路由地址(Hash地址)中,可变的部分做为"参数项"
    使冒号 :参数项名称 来表示动态的参数项,参数项名字自定义
    把某种模式匹配到的所有路由,都映射到同个组件(如:/about/1、/about/2等,都映射到相同的路由)
获取动态参数的两种方法:
1. this.$route.params.id(this可以省略)
2. index.js路由模块中开启props传参,组件中使用 props 来接收
*/

// 这里有3个路由链接,必须要写3条路由规则吗?
// 答案:no,动态路由可以把多条路由规则合并成一个规则,提高路由规则的复用性
<router-link to="/about/1">关于1</router-link>
<router-link to="/about/2">关于2</router-link>
<!-- 在Hash地址中, / 后面的参数项,叫做"路径参数", ?号后面的参数向叫做"查询参数" -->
<router-link to="/about/3?name=zs age=20">关于3</router-link>

<script>
export default {
    methods: {
        showThis() {
            console.log(this);
            console.log("路由的参数对象:",this.$route);
            console.log("在路由的参数对象中,path是部分路径,fullPath是完整地址,queyr是查询参数");
            console.log("路由的导航对象:",this.$router);
        }
    },
    // 用于接收动态参数值
    props: ["id"]
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// index.js 就是当前项目的路由模块

// { path: "/movie/1", component: Movie }
// { path: "/movie/2", component: Movie }
// { path: "/movie/3", component: Movie }

// 动态路由:可以提高路由规则的复用性(将以上3条路由规则,合并成一条)
{ path: "/about/:id", component: About, props: true }
// :id 表示动态的参数项:把路由链接可变的部分做为动态的参数项,参数项名称可自定义
// props:true 表示开启props传参,让父组件可以接收到动态参数值
1
2
3
4
5
6
7
8
9
10

# 4. 嵌套路由

嵌套路由:路由里面嵌套路由(子路由规则),使用 children 属性声明子路由规则

<template>
    <div class="movie-conuainer">
        <h3>Movie 组件</h3>
        <!-- 1. 设置子路由链接 -->
        <!-- <router-link to="/movie">动作片</router-link> 默认子路由的链接 -->
        <router-link to="/movie/tab1">动作片</router-link>
        <router-link to="/movie/tab2">爱情片</router-link>
        <router-link to="/movie/tab3">喜剧片</router-link>

        <!-- 2. 把 "路由链接" 对应的 "组件" 展示到这个位置 -->
        <router-view></router-view>
        
        <!-- 3. 在 src/router/index.js 路由模块中,导入需要的组件,并配置子路由规则 -->
    </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 导入组件(用于给 Movie 组件配置子路由)
import Tab1 from "@/components/tabs/tab1.vue"
import Tab2 from "@/components/tabs/tab2.vue"
import Tab3 from "@/components/tabs/tab3.vue"

// 创建路由的实例对象
const router = new VueRouter({
    routes: [
        // 重定向路由规则:访问域名,默认展示首页组件
        { path: "/", redirect: "/home" },
        // 路由规则:path 是路由链接, component 是对应的组件
        { path: "/home", component: Home },
        { path: "/movie", component: Movie },
        
        // 嵌套路由:路由里面嵌套路由
        { path: "/about", component: About,
         	// 使用 children 属性,为 about 路由,添加3条子路由规则
         	children: [
                { path: "tab1", component: Tab1 },
                { path: "tab2", component: Tab2 },
                { path: "tab3", component: Tab3 },
                // 默认子路由:path 的值为空,相当于路由重定向(默认展示Tab1组件)
                // { path: "", component: Tab1 }
            ],
            // 重定向的子路由规则,访问 xxx.com/movie 则是 xxx.com/movie/tab1
            redirect: "/movie/tab1"
        }
    ]
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 5. 编程式导航

声明式导航:通过点击链接实现导航的方式,叫做声明式导航
编程式导航:通过调用 API 方法跳转到新页面的方式,属于编程式导航

/* 
声明式导航:通过点击链接实现导航的方式,叫做声明式导航
编程式导航:通过调用 API 方法跳转到新页面的方式,属于编程式导航

vue-router 提供了许多编程式导航的 API,其中最常见的导航 API 如下:
1. this.$router.push("Hash地址")
    跳转到指定 hash 地址,并增加一条历史记录(浏览器中会留下历史记录)
2. this.$router.replace("Hash地址")
    跳转到指定 hash 地址,并替换掉当前的历史记录
3. this.$router.go(n)
    可以在浏览历史中前进和后退,前进/后退到之前的浏览页面
    n 表示数值(正数/负数):1 表示前进;-1 表示后退
4. 前进/后退另一种写法
    前进:this.$router.forward() 
    后退:this.$router.back()
以上 API 方法,可以写到 methods 方法里面(需加this),也可以直接写在行内中(不加this)
*/

<!-- 1. 声明式导航:通过点击链接,实现页面切换 -->
<a href="#/movie/tab1">电影1</a>
<router-link to="/movie/tab2">电影2</router-link> 

<!-- 2. 编程式导航:通过 调用 API 方法,实现跳转页面-->
<button @click='$router.push("/movie/tab1")'>通过 push 跳转到"movie/tab1"页面</button>
<button @click='$router.replace("/movie/tab2")'>通过 replace 跳转到"movie/tab2"页面</button>
<button @click="$router.go(1)">前进</button>
<button @click="$router.go(-1)">后退</button>
<!-- 前进/后退另一种写法 -->
<button @click="$router.forward()">forward 前进</button>
<button @click="$router.back()">back 后退</button>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 6. 路由导航守卫

导航守卫:用于控制路由的访问权限,用户是否有权限从A页面跳转到B页面(如:未登录情况下,不能直接访问到后台) image

前置导航守卫(全局)

(1). 定义全局守卫:在 index.js 路由模块中声明
(2). 在每次发生路由的导航跳转时,都会触发全局前置守卫
(3). 在全局前置守卫中,程序员可以对每个路由进行访问权限的控制
image

// Home 组件

// 如:用户点击路由链接,希望前往后台页面
<router-link to="/main">后台页面</router-link>
1
2
3
4
// src/router/index.js 当前项目的路由模块

// 1.导入 Vue 和 VueRouter 包
import Vue from "vue"
import VueRouter from "vue-router"

// 2. 调用 Vue.use() 函数,把 VueRouter 安装为 Vue 的插件【Vue.use()函数,用于给项目安装插件】
Vue.use(VueRouter)

// 3. 创建路由的实例对象
const router = new VueRouter()

// 为 router 实例对象,声明全局前置导航守卫
// 只要发生了路由的跳转,都会触发 beforEach 指定的 function 回调函数
router.beforEach(function(to, from, next){
    // to 是 当前所处页面 的 路由信息对象
    console.log(to)
    // from 是 前一个页面的路由信息对象(来自哪里)
    console.log(from)
    // next 是一个函数,用于控制用户的访问权限(next函数有三种调用方式如下)
    next() // 表示放行,允许用户访问任何页面
    next("/login") // 表示没有访问权限,强制其跳转到登录页面
    next(false) // 表示没有访问权限,强制其停留在当前所处页面
})
// 4. 向外共享路由的实例对象
export default router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 7. 设置页面标题(额外小知识)

当路由链接发生改变时(切换页面时),让浏览器显示相应页面的title

  1. 在路由规则中添加 meta 属性:meta: { title: "首页" }
// src/router/index.js 路由模块

// 路由规则
{ path: "/", component: Home, meta: { title: "首页" } }, // meta 用于给页面添加 title
{ path: "/about", component: About, meta: { title: "关于" } }
1
2
3
4
5
  1. 在 main.js 入口文件中,添加
// main.js 入口文件

router.afterEach((to, from, next) => {
  // 切换页面时,滚动到顶部
  window.scrollTo(0,0)
  /* 路由发生变化修改页面title */
  if (to.meta.title) {
    document.title = to.meta.title
  }
})
1
2
3
4
5
6
7
8
9
10

# 8. 路由传参(实现页面跳转)

参考:路由传参(实现页面跳转)

帮助我们改善此页面! (opens new window)