【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(一) |
文章目录
- 【Vue全家桶】Vue Router详解(一)
- 前言
- 一、前端路由的发展历程
- 1.1 后端路由
- 1.2 前后端分离
- 1.3 前端路由
- 二、 前端路由的实现
- 2.1 基于hash
- 2.2 基于History API
- 2.3 两种模式对比
- 三、认识Vue Router
- 3.1 安装
- 3.2 基本使用
- 3.3 router-link
- 3.4 路由的其他属性
- 四、动态路由匹配
- 4.1 动态路由基本匹配
- 4.2 捕获所有路由或 404 Not found 路由
- 五、嵌套路由
前言
到底什么是路由?简单来说,路由就是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
npm:npm install vue-router@4
3.2 基本使用
当加入 Vue Router 时,我们需要做的就是将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们
使用vue-router的步骤:
- 创建路由需要映射的组件(打算显示的页面);
- 通过createRouter创建路由对象,并且传入routes和history模式;
- 配置路由映射: 组件和路径映射关系的routes数组;
- 创建基于hash或者history的模式;
- 使用app注册路由对象(use方法);
- 路由使用: 通过
<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。