07路由

1. 前端路由的概念与原理

1.1 什么是路由

路由(router) 就是对应关系。

1.2 SPA 与前端路由

SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。

此时,不同组件之间的切换需要通过前端路由来实现。

结论:在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成!

1.3 什么是前端路由

通俗易懂的概念:Hash 地址与组件之间的对应关系。

也就是,Hash 地址发生变化时,就会跳转到其对应的组件。

1.4 前端路由的工作方式

  • 用户点击了页面上的路由链接

  • 导致了 URL 地址栏中的 Hash 值发生了变化

  • 前端路由监听了到 Hash 地址的变化

  • 前端路由把当前 Hash 地址对应的组件渲染都浏览器中

结论:前端路由,指的是 Hash 地址与组件之间的对应关系

1.5 实现简易的前端路由

通过 <component> 标签的 is 属性动态渲染组件。示例代码如下:

html 复制代码
<template>
  <div id="app">
    <h1>主页</h1>
    <component :is="comName"></component>
  </div>
</template>

<script>
import LeftView from '@/components/LeftView.vue'
import RightView from '@/components/RightView.vue'

export default {
  components: {
    LeftView,
    RightView
  },
  data () {
    return {
      comName: RightView
    }
  }
}
</script>

App.vue 组件中,为 <a> 链接添加对应的 hash 值:

html 复制代码
<a href="#/left">Left</a>
<hr>
<a href="#/right">Right</a>

created 生命周期函数中,监听浏览器地址栏中 hash 地址的变化,动态切换要展示的组件的名称:

js 复制代码
created () {
  window.onhashchange = () => {
    switch (location.hash) {
      case '#/left':
        this.comName = 'LeftView'
        break
      case '#/right':
        this.comName = 'RightView'
    }
  }
}

注意:

  • window 后面跟的函数必须为箭头函数,箭头函数的 this 指向为 vue 实例,function 函数的 this 指向为 window
  • location.hash 可以获取到 URL 中的锚点部分,即:URL# 符号及其后面的部分

关于动态组件,具体可以到我之前的文章 06动态组件插槽自定义指令 了解。

2. vue-router 的基本用法

2.1 什么是 vue-router

vue-routervue.js 官方给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。

vue-router 的官方文档地址:router.vuejs.org/zh/

2.2 vue-router 安装和配置的步骤

  • 安装 vue-router

    bash 复制代码
    npm i vue-router@3.5.2 -S

    也可以在创建 vue 项目时勾选 vue-router 选项。

  • 创建路由模块

    src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码:

    js 复制代码
    // 1. 导入 vue 和 vueRouter 模块
    import vue from 'vue'
    import VueRouter from 'vue-router'
    
    // 2. 将 vueRouter 作为 vue 的插件
    vue.use(VueRouter)
    
    // 3. 创建路由实例对象
    const router = new VueRouter()
    
    // 4. 对外共享
    export default router
  • 导入并挂载路由模块

    src/main.js 入口文件中,导入并挂载路由模块。示例代码如下:

    js 复制代码
    import Vue from 'vue'
    import App from './App.vue'
    
    // 1. 导入路由
    import router from '@/router'
    
    new Vue({
      render: h => h(App),
      router: router
      // 键和值相同,可以简化只写一个 router
    }).$mount('#app')

    问题: 导入路由时,为什么我们只导入到文件夹,没导入其 index.js 文件?

    回答: 导入时会自动查找其文件夹是否有 index.js 文件,有的话就自动导入 index.js 文件。当然,路径写完整也是没有问题的。

  • 声明路由链接和占位符

    App.vue 组件中,使用 vue-router 提供的 <router-link><router-view> 声明路由链接和占位符:

    html 复制代码
    <template>
      <div id="app">
        <h1>主页</h1>
        <!-- 1. 定义路由链接 -->
        <router-link to="/left">Left</router-link>
        <hr>
        <router-link to="/right">Right</router-link>
        
        <!-- 2. 定义路由的占位符 -->
        <router-view></router-view>
      </div>
    </template>

    注意:

    • <router-link> 相当于 <a>
    • <router-link>to 属性 相当于 <a>href 属性,注意 to 属性地址直接从 / 开始写
    • <router-view> 相当于动态组件的 <component>,同样起到占位效果
  • 声明路由的匹配规则

    src/router/index.js 路由模块中,通过 routes 数组声明路由的匹配规则。示例代码如下:

    js 复制代码
    import vue from 'vue'
    import VueRouter from 'vue-router'
    
    // 1. 导入使用的组件
    import Left from '@/components/LeftView.vue'
    import Right from '@/components/RightView.vue'
    
    vue.use(VueRouter)
    
    // 2. 创建路由实例对象
    const router = new VueRouter(
      {
        // 3. 匹配规则
        routes: [
          { path: '/left', component: Left },
          { path: '/right', component: Right }
        ]
      }
    )
    
    export default router

    注意:匹配规则就是跳转到 path 所在的地址,就展示 component 组件内容

3. vue-router 的常见用法

3.1 路由重定向

路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。常用在根路径显示首页组件。

通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:

js 复制代码
const router = new VueRouter(
  {
    // 3. 匹配规则
    routes: [
      // 当用户访问 / 时,通过 redirect 属性跳转到 /right 对应的路由规则
      { path: '/', redirect: '/right' },
      { path: '/left', component: Left },
      { path: '/right', component: Right }
    ]
  }
)

我们有个疑惑,正常匹配不就行了吗,为什么要有重定向?或者说重定向和正常匹配的差别在哪里?

正常匹配:

js 复制代码
{ path: '/', component: Right }

重定向:

js 复制代码
{ path: '/', redirect: '/right' }

两者都能实现在 URL 输入 / 的情况下,显示 Right 组件。

不同的是:

  • 正常匹配,在 URL 输入 / ,跳转到 / 路径,直接显示 Right 组件

  • 重定向,在 URL 输入 / ,无法跳转到 / 路径,跳转的是重定向写的路径 /right,所以会先找该路径的匹配规则,即 { path: '/right', component: Right },然后再根据该规则显示 Right 组件

3.2 嵌套路由

3.2.1 定义

通过路由实现组件的嵌套展示,叫做嵌套路由。

我们刚才的路由,大概就是下图这个结构,点击父级路由链接显示其模板内容:

而嵌套路由,大概就是下图这个结构,父级模板内容中又有子级路由链接,点击子级路由链接显示子级模板内容:

3.2.2 实现

  • 声明子路由链接和子路由占位符

    LeftView.vue 组件中,声明 movie1、movie2、movie3 的子路由链接以及子路由占位符。示例代码如下:

    html 复制代码
    <div class="left-content">
      <h1>左组件</h1>
      <router-link to="/left/movie1">电影1</router-link>
      <router-link to="/left/movie2">电影2</router-link>
      <router-link to="/left/movie3">电影3</router-link>
      <router-view></router-view>
    </div>
  • 通过 children 属性声明子路由规则

    src/router/index.js 路由模块中,导入需要的组件,并使用 children 属性声明子路由规则:

    js 复制代码
    // 3. 匹配规则
    routes: [
      // 当用户访问 / 时,通过 redirect 属性跳转到 /right 对应的路由规则
      { path: '/', redirect: '/right' },
      {
        path: '/left',
        component: Left,
        children: [
          { path: 'movie1', component: Movie1 },
          { path: 'movie2', component: Movie2 },
          { path: 'movie3', component: Movie3 }
        ]
      },
      { path: '/right', component: Right }
    ]

    注意:

    • LeftView.vue 组件,定义的子组件链接,path 需要写全
    • 而在路由规则里面,chidren 数组中的 path ,可以和父组件的 path 拼接,所以 children 数组里的 path 会更简洁

3.3 动态路由

3.3.1 提出的背景

路由链接:

html 复制代码
<router-link to="/movie/1">电影1</router-link>
<router-link to="/movie/2">电影2</router-link>
<router-link to="/movie/3">电影3</router-link>

匹配规则:

js 复制代码
{ path: '/movie/1', component: Movie },
{ path: '/movie/2', component: Movie },
{ path: '/movie/3', component: Movie }

缺点:这样写虽然可以,但路由规则的复用性差。所以,我们提出了动态路由。

3.3.2 动态路由的概念

动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。

vue-router 中使用 : 来定义路由的参数项。

路由规则合并前:

js 复制代码
{ path: '/movie/1', component: Movie },
{ path: '/movie/2', component: Movie },
{ path: '/movie/3', component: Movie }

路由规则合并后:

js 复制代码
// 动态路由中,其 : 后面为动态参数
{ path: '/movie/:id', component: Movie }

将多个规则合为一个,提高了路由规则的复用性。

3.3.3 $route.params 参数对象

在动态路由渲染出来的组件中,可以使用 this.$route.params 对象访问到动态匹配的参数值对象。

如果要使用我们刚刚的动态参数 id ,可以这样用:

html 复制代码
<h3>这是 movie {{this.$route.params.id}}</h3>

并且 this 可以省略:

html 复制代码
<h3>这是 movie {{$route.params.id}}</h3>

3.3.4 使用 props 接收路由参数

为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。示例代码如下:

匹配规则:

js 复制代码
{ path: 'movie/:id', component: Movie, props: true }

使用其参数的组件:

html 复制代码
<template>
  <div class="movie1">
    <h3>这是 movie {{id}}</h3>
  </div>
</template>

<script>
export default {
  props: ['id']
}
</script>

3.4 声明式导航 & 编程式导航

3.4.1 定义

在浏览器中,点击链接实现导航的方式,叫做声明式导航。例如:

  • 普通网页中点击 <a> 链接、vue 项目中点击 <router-link> 都属于声明式导航

在浏览器中,调用 API 方法实现导航的方式,叫做编程式导航。例如:

  • 普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航

3.4.2 vue-router 中的编程式导航 API

vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是:

  • this.$router.push('hash 地址')
  • this.$router.replace('hash 地址')
  • this.$router.go(数值 n)

1. this.$router.push('hash 地址')

调用 this.$router.push() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面。示例代码如下:

html 复制代码
<template>
  <div id="app">
    <button @click="gotoMovie">跳转到电影 2</button>
  </div>
</template>

<script>

export default {
  methods: {
    gotoMovie () {
      this.$router.push('./left/movie/2')
    }
  }
}

注意:这个定义跳转的按钮只能点一次,点两次 URL 会叠加

2. this.$router.replace('hash 地址')

调用 this.$router.replace() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面。

html 复制代码
<template>
  <div id="app">
    <button @click="gotoMovie">跳转到电影 2</button>
  </div>
</template>

<script>

export default {
  methods: {
    gotoMovie () {
      this.$router.replace('./left/movie/2')
    }
  }
}

pushreplace 的区别:

  • push 会增加一条历史记录
  • replace 不会增加历史记录,而是替换掉当前的历史记录

但两者都不能重复按两次,否则 URL 会叠加。

3. this.$router.go(数值 n)

调用 this.$router.go() 方法,可以在浏览历史中前进和后退。示例代码如下:

html 复制代码
<template>
  <div id="app">
    <h1>主页</h1>
    <button @click="goBack">后退</button>
  </div>
</template>

<script>

export default {
  methods: {
    goBack () {
      this.$router.go(-1)
    }
  }
}
</script>

4. $router.go 的简化用法

在实际开发中,一般只会前进和后退一层页面。因此 vue-router 提供了如下两个便捷方法:

  • $router.back():在历史记录中,后退到上一个页面

  • $router.forward():在历史记录中,前进到下一个页面

3.5 导航守卫

3.5.1 工作原理

无导航守卫时:

导航守卫可以控制路由的访问权限。示意图如下:

其实相当于一个中间件,可以进行检验。

3.5.2 全局前置守卫

每次发生路由的导航跳转时,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制:

js 复制代码
// 创建路由实例对象
const router = new VueRouter({ ... })

// 调用实例对象的 beforeEach 方法,即可声明 "全局前置守卫"
// 每次路由跳转时,就会触发 fn 这个回调函数
router.beforeEach(fn)

全局前置守卫的回调函数中接收 3 个形参,格式为:

js 复制代码
router.beforeEach((to, from, next) => {
    // 回调函数提
})

参数说明:

  • to:将要访问的路由信息对象
  • from:将要离开的路由信息对象
  • next:函数,调用 next() 表示放行,允许这次路由导航

3.5.3 next 函数的 3 种调用方式

参考示意图,分析 next 函数的 3 种调用方式最终导致的结果:

  • 当前用户拥有后台主页的访问权限,直接放行:next()

  • 当前用户没有后台主页的访问权限,强制其跳转到登录页面:next('/login')

  • 当前用户没有后台主页的访问权限,不允许跳转到后台主页:next(false)

没有访问权限,有强制跳转到其他页面返回原来页面两种方案,这里代码写的是强制跳转其他页面的方案。

前两种情况代码编写,大致如下:

js 复制代码
router.beforeEach(function(to, from, next) {
    if (to.path == '/main'){
      const token = localStorage.getItem('token')
      if (token) {
        next() // 访问的是后台主页,且有 token 的值
      } else {
        next('/login') // 访问后台主页,但是没有 token 的值(这里采用直接跳转的方案)
      }
  } else {
    next() // 访问的不是后台主页,直接放行
  }
})

本文主要学习 黑马程序员Vue全套视频教程,从vue2.0到vue3.0一套全覆盖,前端学习核心框架教程

如有错误,敬请指正,欢迎交流🤝,谢谢♪(・ω・)ノ

相关推荐
神夜大侠1 分钟前
VUE 实现公告无缝循环滚动
前端·javascript·vue.js
明辉光焱3 分钟前
【Electron】Electron Forge如何支持Element plus?
前端·javascript·vue.js·electron·node.js
柯南二号36 分钟前
HarmonyOS ArkTS 下拉列表组件
前端·javascript·数据库·harmonyos·arkts
wyy729338 分钟前
v-html 富文本中图片使用element-ui image-viewer组件实现预览,并且阻止滚动条
前端·ui·html
前端郭德纲1 小时前
ES6的Iterator 和 for...of 循环
前端·ecmascript·es6
王解1 小时前
【模块化大作战】Webpack如何搞定CommonJS与ES6混战(3)
前端·webpack·es6
欲游山河十万里1 小时前
(02)ES6教程——Map、Set、Reflect、Proxy、字符串、数值、对象、数组、函数
前端·ecmascript·es6
明辉光焱1 小时前
【ES6】ES6中,如何实现桥接模式?
前端·javascript·es6·桥接模式
PyAIGCMaster1 小时前
python环境中,敏感数据的存储与读取问题解决方案
服务器·前端·python
baozhengw1 小时前
UniAPP快速入门教程(一)
前端·uni-app