【Vue全家桶】详解Vue Router(一)

【Vue全家桶】Vue Router详解(一)

Vue系列文章目录

内容 参考链接
Vue(一) 【Vue全家桶】邂逅Vue、Vue的多种引入方式
Vue(二) 【Vue全家桶】声明式编程、MVVM
Vue(三) 【Vue全家桶】Options API_
Vue(四) 【Vue全家桶】带你全面了解通过Vue CLI初始化Vue项目
Vue(五) 【Vue全家桶】组件系列(一)—组件开发基础
Vue(六) 【Vue全家桶】组件系列(二)—组件通信(props、$emit、事件总线、Provide、Inject)
Vue(七) 【Vue全家桶】详解Vue Router(一)

前言

到底什么是路由?简单来说,路由就是URL到函数的映射。并且路由这个概念最早是出现在后端的,因为早期的网页都是服务端渲染的,比如:JSP,PHP,ASP等语言,都是直接返回渲染好的html给客户端显示。

一、前端路由的发展历程

web的发展主要经历了这样一些阶段:

  • 后端路由
  • 前后端分离
  • 前端路由

1.1 后端路由

早期的网站开发整个HTML页面是由服务器来渲染的.

  • 服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示.

但是, 一个网站, 这么多页面服务器如何处理呢?

  • 一个页面有自己对应的网址, 也就是URL;
  • URL会发送到服务器, 服务器会通过正则对该URL进行匹配, 并且最后交给一个Controller进行处理;
  • Controller进行各种处理, 最终生成HTML或者数据, 返回给前端.

上面的这种操作, 就是后端路由:

  • 当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户端.
  • 这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化.

后端路由的缺点:

  • 一种情况是整个页面的模块由后端人员来编写和维护的;
  • 另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码;
  • 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情;

1.2 前后端分离

前端渲染的理解

  • 每次请求涉及到的静态资源都会从静态资源服务器获取,这些资源包括HTML+CSS+JS,然后在前端对这些请求回来的资源进行渲染;
  • 需要注意的是,客户端的每一次请求,都会从静态资源服务器请求文件
  • 同时可以看到,和之前的后端路由不同,这时后端只是负责提供API了;

前后端分离阶段:

  • 随着Ajax的出现,它允许人们在不刷新页面的情况下发起请求,有了前后端分离的开发模式;
  • 与之共生的,还有不刷新页面即可更新页面内容
  • 在这种背景下,出现了SPA(单页面应用)
  • 后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中;
  • 这样做最大的优点就是前后端责任的清晰,后端专注于数据上,前端专注于交互和可视化上;
  • 并且当移动端(iOS/Android)出现后,后端不需要进行任何处理,依然使用之前的一套API即可;

SPA极大地提升了用户体验,它允许页面在不刷新的情况下更新页面内容,使内容的切换更加流畅。但是在 SPA 诞生之初,人们并没有考虑到“定位”这个问题——在内容切换前后,页面的 URL 都是一样的,这就带来了两个问题:

  • SPA 其实并不知道当前的页面“进展到了哪一步”。可能在一个站点下经过了反复的“前进”才终于唤出了某一块内容,但是此时只要刷新一下页面,一切就会被清零,必须重复之前的操作、才可以重新对内容进行定位——SPA 并不会“记住”你的操作。
  • 由于有且仅有一个 URL 给页面做映射,这对 SEO 也不够友好,搜索引擎无法收集全面的信息

1.3 前端路由

为了解决这个问题,前端路由出现了

  • 前端路由可以帮助我们在仅有一个页面的情况下,“记住”用户当前走到了哪一步——为 SPA 中的各个视图匹配一个唯一标识;
  • 这意味着用户前进、后退触发的新内容,都会映射到不同的 URL 上去;
  • 此时即便他刷新页面,因为当前的 URL 可以标识出他所处的位置,因此内容也不会丢失;

那么如何实现这个目的呢?首先要解决两个问题

  • 当用户刷新页面时,浏览器会默认根据当前 URL 对资源进行重新定位(发送请求)。这个动作对 SPA 是不必要的,因为我们的 SPA 作为单页面,无论如何也只会有一个资源与之对应。此时若走正常的请求-刷新流程,反而会使用户的前进后退操作无法被记录。

    单页面应用对服务端来说,就是一个URL、一套资源,那么如何做到用“不同的URL”来映射不同的视图内容呢?

从这两个问题来看,服务端已经完全救不了这个场景了。所以要靠咱们前端自力更生,不然怎么叫“前端路由”呢?作为前端,可以提供这样的解决思路:

  • 拦截用户的刷新操作,避免服务端盲目响应、返回不符合预期的资源内容。把刷新这个动作完全放到前端逻辑里消化掉。
  • 感知 URL 的变化。这里不是说要改造 URL、凭空制造出 N 个 URL 来。而是说 URL 还是那个 URL,只不过我们可以给它做一些微小的处理——这些处理并不会影响 URL 本身的性质,不会影响服务器对它的识别,只有我们前端感知的到。一旦我们感知到了,我们就根据这些变化、用 JS 去给它生成不同的内容。

二、 前端路由的实现

2.1 基于hash

简介: hash模式是开发中默认的模式,它的URL带着一个#,例如:www.abc.com/#/vue,它的hash值就是#/vue

特点:hash值会出现在URL里面,但是不会出现在HTTP请求中,对后端完全没有影响。所以改变hash值,不会重新加载页面。这种模式的浏览器支持度很好,低版本的IE浏览器也支持这种模式。hash路由被称为是前端路由,已经成为SPA(单页面应用)的标配。

2.2 基于History API

简介: history模式的URL中没有#,它使用的是传统的路由分发模式,即用户在输入一个URL时,服务器会接收这个请求,并解析这个URL,然后做出相应的逻辑处理。

特点: 当使用history模式时,URL就像这样:abc.com/user/id。相比hash模式更加好看。但是,history模式需要后台配置支持。如果后台没有正确配置,访问时会返回404。

API: history api可以分为两大部分,切换历史状态和修改历史状态:

  • 修改历史状态:包括了 HTML5 History Interface 中新增的 pushState()replaceState() 方法,这两个方法应用于浏览器的历史记录栈,提供了对历史记录进行修改的功能。只是当他们进行修改时,虽然修改了url,但浏览器不会立即向后端发送请求。如果要做到改变url但又不刷新页面的效果,就需要前端用上这两个API。
  • 切换历史状态: 包括forward()back()go()三个方法,对应浏览器的前进,后退,跳转操作。

虽然history模式丢弃了丑陋的#。但是,它也有自己的缺点,就是在刷新页面的时候,如果没有相应的路由或资源,就会刷出404来。

2.3 两种模式对比

hash history
有 # 号 没有 # 号
能够兼容到IE8 只能兼容到IE10
实际的url之前使⽤哈希字符,这部分url不会 发送到服务器,不需要在服务器层面上进行任何处理 每访问⼀个页面都需要服务器进行路由匹配⽣成 html ⽂件再发送响应给浏览器,消耗服务器大量资源
刷新不会存在 404 问题 浏览器直接访问嵌套路由时,会报 404 问题。
不需要服务器任何配置 需要在服务器配置⼀个回调路由

三、认识Vue Router

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。功能包括:

  • 嵌套路由映射
  • 动态路由选择
  • 模块化、基于组件的路由配置
  • 路由参数、查询、通配符
  • 展示由 Vue.js 的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活 CSS 类的链接
  • HTML5 history 模式或 hash 模式
  • 可定制的滚动行为
  • URL 的正确编码

3.1 安装

直接下载/CDN:https://unpkg.com/vue-router@4

npmnpm install vue-router@4

3.2 基本使用

当加入 Vue Router 时,我们需要做的就是将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们

使用vue-router的步骤:

  1. 创建路由需要映射的组件(打算显示的页面);
  2. 通过createRouter创建路由对象,并且传入routes和history模式;
    • 配置路由映射: 组件和路径映射关系的routes数组;
    • 创建基于hash或者history的模式;
  3. 使用app注册路由对象(use方法);
  4. 路由使用: 通过<router-link><router-view>
  • 使用一个自定义组件 router-link 来创建链接
  • router-view 将显示与 url 对应的组件

基本使用流程

import {
    
     createRouter, createWebHashHistory } from "vue-router"

// 导入创建的组件
import Home from '../views/Home.vue'
import About from '../views/Aobut.vue'

// 配置路由的映射
const routes = [
  {
    
     path: '/home', component: Home },
  {
    
     path: '/about', component: About },
]

// 创建router对象
const router = createRouter({
    
    
  // `routes: routes` 的缩写
  routes,
  history: createWebHashHistory()
})

export default router
<template>
  <div class="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
  </div>

</template>
import router from './06_路由/router'
createApp(App).use(router).mount('#app')

3.3 router-link

router-link事实上有很多属性可以配置

  • to
    • 类型RouteLocationRaw
    • 表示目标路由的链接。当被点击后,内部会立刻把 to 的值传到 router.push(),所以这个值可以是一个 string 或者是描述目标位置的对象
  • replace
    • 类型boolean
    • 默认值false
    • 设置 replace 属性的话,当点击时,会调用 router.replace(),而不是 router.push(),所以导航后不会留下历史记录。
  • active-class
    • 类型string
    • 默认值"router-link-active"
    • 链接激活时,应用于渲染的 <a> 的 class。
  • exact-active-class
    • 类型string
    • 默认值"router-link-exact-active"
    • 链接精准激活时,应用于渲染的 <a> 的 class。

3.4 路由的其他属性

  • name属性:路由记录独一无二的名称;
  • meta属性:自定义的数据

四、动态路由匹配

4.1 动态路由基本匹配

很多时候,我们需要将给定匹配模式的路由映射到同一个组件

  • 例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但用户 ID 不同。
  • 在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数
import User from '../views/User.vue'

// 配置路由的映射
const routes = [
  // 动态字段以冒号开始
  {
    
     path: '/user/:id', component: User }
]

现在像 /user/johnny/user/jolyne 这样的 URL 都会映射到同一个路由。

路径参数用冒号 : 表示。

那么在User中如何获取到对应的值呢

  • 在template中,直接通过 $route.params获取值;
  • 在生命周期中,通过 this.$route.params获取值;
  • 在setup中,我们要使用 vue-router库给我们提供的一个hook useRoute
    • 该Hook会返回一个Route对象,对象中保存着当前路由相关的值;
<h2>User: {
   
   { $route.params.id }}</h2>
<script setup>
import { useRoute } from 'vue-router';

const route = useRoute()
console.log(route.params.id);
</script>

4.2 捕获所有路由或 404 Not found 路由

对于哪些没有匹配到的路由,我们通常会匹配到固定的某个页面

  • 比如NotFound的错误页面中,这个时候我们可编写一个动态路由用于匹配所有的页面;
const routes = [
  // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
  {
    
     path: '/:pathMatch(.*)', name: 'NotFound', component: NotFound }
]
  • 我们可以通过 $route.params.pathMatch获取到传入的参数:

    <h2>Not Found: {
         
         { $route.params.pathMatch }}</h2>
    
  • path: '/:pathMatch(.*)'解析如下:

在这里插入图片描述

  • path: '/:pathMatch(.*)*'解析如下:

在这里插入图片描述

五、嵌套路由

什么是路由的嵌套呢

  • 目前我们匹配的Home、About、User等都属于第一层路由,我们在它们之间可以来回进行切换;

但是呢,我们Home页面本身,也可能会在多个组件之间来回切换

  • 比如Home中包括Product、Message,它们可以在Home内部来回切换;
  • 这个时候我们就需要使用嵌套路由,在Home中也使用 router-view 来占位之后需要渲染的组件
// 导入创建的组件
import Home from '../views/Home.vue'

// 配置路由的映射
const routes = [
  {
    
     path: '/home', component: Home }
]

// 创建router对象
const router = createRouter({
    
    
  // `routes: routes` 的缩写
  routes,
  history: createWebHashHistory()
})
<router-link to="/home">首页</router-link>
<router-view></router-view>

这里的 <router-view> 是一个顶层的 router-view。它渲染顶层路由匹配的组件。同样地,一个被渲染的组件也可以包含自己嵌套的 <router-view>。例如,如果我们在 Home 组件的模板内添加一个 <router-view>

<template>
  <div class="home">
    <h2>Home Page</h2>
    <router-link to="/home/product">商品</router-link>
    <router-view></router-view>
  </div>
</template>

要将组件渲染到这个嵌套的 router-view 中,我们需要在路由中配置 children

const routes = [
  {
    
     path: '/home', 
    component: Home,
    children: [
      {
    
    
      path: 'product',
      component: Product
      }
    ]
 }
]

注意,以 / 开头的嵌套路径将被视为根路径。这允许你利用组件嵌套,而不必使用嵌套的 URL。

猜你喜欢

转载自blog.csdn.net/qq_53664443/article/details/129758549