Vue3——Router4教程(小满版本)

Vue3——Router4教程(小满版本)
SilverCatVue3——Router4教程(小满版本)
Vue 路由允许我们通过不同的 URL 访问不同的内容。通过 Vue 可以实现多视图的单页 Web 应用
路由导读
1-2.1、什么是路由?
一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
key 为路径, value 可能是 function 或 component
1-2.2、路由分类
1、后端路由:
1)理解:value 是 function, 用于处理客户端提交的请求。
2)工作过程:服务器接收到一个请求时, 根据请求路径找到匹配的函数 来处理请求, 返回响应数据。
2、前端路由:
1)理解:value 是 component,用于展示页面内容。
2)工作过程:当浏览器的路径改变时, 对应的组件就会显示。
第一章节(上)——入门(安装及配置)
1. 安装 vue-router
npm i vue-router --save |
2. 配置 vue-router
新建 router 文件夹
路径:/src/router
新建 index.js 文件
路径: /src/router/index.js
- 步骤
- 在
router文件夹下的index.js中我们进行引入路由,然后创建一个路由器,在路由器中配置路由 - 然后要在
main.js中进行引入router并进行使用 - 在App.vue中使用
进行存放路由,这个是一个容器(内置组件,可以写在任何位置) - 有了容器我们就可以往里面放东西了,还记得我们在index.js配置的路由信息吗?
- 使用
, to决定了我们要跳向哪个位置,to里面填写的内容就是我们填在路由器中的路由的path。通过点击触发path路径然后激活component进行路由跳转。请注意这这个router-link是一个a标签
- 使用
- RouteRecordRaw属性通过源码可以看出联合了
RouteRecordSingleView,RouteRecordMultipleViews,RouteRecordRedirect这三个类型,在继续往下,我们可以看到很多个配置,其中path与component是必填的
//index.js文件 |
第一章节(下)——路由模式
我们平时在路径中有看到/#/的就是哈希模式(hash),这个哈希不是算法
//Vue2 mode history Vue3 createWebHistory(历史),兼容性较差一点,但路径不显示# |
这个/#/是因为哈希模式是通过
location.hash去匹配的,我们在控制台中可以打印出’#/‘的结果如果在控制台中修改
location.hash的话,页面中也会进行实时跳转通过监听window上的一个属性
hashchange我们可以看到跳转之间的变化(哈希模式)window.addEventListener('hashchange',(e)=>{
console.log(e)
})
//哈希原理:通过监听hashchange我们可以看到HashChangeEvent这个属性里面的
//newURL与oldURL,其实也就是新地址跟旧地址(此地址其实就是路由跳转的变化)
在上面我们演示了哈希的原理,接下来我们看另外的一个常用的,也就是路径中不带#号的
History的原理,是通过popstate来看的
window.addEventListener('popstate',(e)=>{ |
第二章——命名路由-编程式导航
作用:可以简化路由的跳转。
不借助
<router-link>实现路由跳转,让路由跳转更加灵活 除了使用
<router-link>创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。在路由器中的路由中进行使用,name:”xxx,爱叫什么叫什么,你叫yupi也无所谓,英文”
使用name命名路由后,我们第一件事情就是去使用了对应路由的地方进行修改
- 比如我们原本是
,现在就不是路径了,现在得变成名字了=> - 注意点:to前面多了冒号,是变成响应式的标记,然后写法变为对象形式
- 比如我们原本是
稍微提一嘴的a标签(这个router-link会自动转化为a标签,但是跟你直接使用a标签是不一样的,使用router-link的才会是单页面,如果直接使用a标签的话,那其实实际感官上是会抖动一下,然后会刷新页面)
<a href="/reg">rrr</a>
没错啦,上面都是铺垫,接下来才是编程式的路由导航写法
命名路由
除了 path 之外,你还可以为任何路由提供 name。这有以下优点:
- 没有硬编码的 URL
params的自动编码 / 解码。- 防止你在 url 中出现打字错误。
- 绕过路径排序(如显示一个)
步骤
引入useRouter
import {useRouter} from 'vue-router'
使用router
const router = useRouter()
字符串用法
//import { useRouter } from 'vue-router'
//const router = useRouter()
//请注意,这个toPage是一个按钮,在页面中使用@click进行绑定
const toPage = () => {
router.push('/reg')
}
//TS写法
const toPage = (url:string) => {
router.push(url)//这种写法的话就需要自己在上面@click="toPage('这里传入路径')",进行配置了
}对象模式
import { useRouter } from 'vue-router'
const router = useRouter()
const toPage = () => {
router.push({//对象形式的写法
path: '/reg'//path是路径,这里一样是写死了,可以将参数传进来
})
}命名式写法
import { useRouter } from 'vue-router'
const router = useRouter()
const toPage = () => {
router.push({
name: 'Reg'//这里需要注意,在前面设置的也需要进行修改,不能够再传入url了,因为我们这里是命名式写法,需要传入命名(此处是写死的做法,下面是传值式的写法)
})
}
const toPage = (Name:string) => {//TS写法,不用TS写法的话写个Name就行了
router.push({//一样是对象形式写法
name: Name//传值式写法
})
}
第三章——历史记录
<router-link>的replace属性
- 作用:控制路由跳转时操作浏览器历史记录的模式
- 浏览器的历史记录有两种写入方式:分别为
push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push - 如何开启
replace模式:<router-link replace .......>News</router-link> - replace官方的解释就是替换当前的记录,可以简单的理解为无痕模式。你无法回退到之前的页面中,你做出的每一个指令都是没有回头路的
router-link使用方法
<router-link replace to="/">Login</router-link>
<router-link replace style="margin-left:10px" to="/reg">Reg</router-link>编程式导航
<button @click="toPage('/')">Login</button>
<button @click="toPage('/reg')">Reg</button>
import { useRouter } from 'vue-router'
const router = useRouter()
const toPage = (url: string) => {
router.replace(url)//与router-link的区别就是,编程式导航的replace隐藏在js里面
}补充小知识点:
为什么有router-link了,还需要有编程式导航?
因为router-link最终会转化为a标签,而当你其实并不是需要a标签的时候,比如上方的例子是按钮,那如何实现?那就得靠路由导航了,路由导航就是没有router-link,所有的逻辑都写在了js里面(想要跳转到哪个页面就靠方法参数传值到js部分来决定),然后在html页面中进行调用
注意点:push是对象式的写法(命名路由),而replace不是噢
go与back
横跨历史
该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步
<button @click="next">前进</button> |
const next = () => { |
第四章——(路由传参)
Query 路由传参
编程式导航 使用 router push 或者 replace 的时候 改为对象形式新增 query 必须传入一个对象
使用前步骤(复习)
- 引入useRouter,在vue3中需要使用的都要针对性的引入
- 使用(我更愿意称为激活使用),const router = useRouter(),这样后面进行使用的时候就直接router.xxx。本质上其实就是调用useRouter了
query是写在push里面的,且只能接受一个对象
//子组件login.vue
const toDetail = (item:Item) =>{
router.push({
path:'/reg',
query:item//item是一个对象
})
}
//通过url路径,我们可以看到信息其实已经传过来了//子组件reg.vue
//<template></template>部分
<template>
<div>价格:{{route.query.name}}</div> //我们通过这种方式取到了query中的值
</template>
//引入路由,注意啦,这是路由不是路由器
import {useRoute} from 'vue-router'
const route = useRoute()
Params路由传参
编程式导航 使用 router push 或者 replace 的时候 改为对象形式并且只能使用
name,path是无效的,然后传入 params
- 使用Params来进行传参的话,不能使用path而是使用name。与此同时,query要变化为params,因为我们已经换频道了,现在是params专场,记得在html页面(或者说是template部分)要将
route.query.name啥的换成route.pamas.name
const toDetail = (item: Item) => { |
注意点:
- pamas的内容是存在内存当中的,这个也是与query传参的一个不同之处,但由此也会造成一个问题,那就是我们一但刷新,这个值是会丢失的
为了解决这个值会丢失的问题,我们延伸出了第三种方式 => 动态路由传参
动态路由传参
很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个
User组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数路径参数 用冒号
:表示。当一个路由被匹配时,它的 params 的值将在每个组件
步骤:
修改router.push中的params
params:item//虽然item本身也是对象,但是换成对象形式写会更好
//修改后
params:{
id:item.id//这里取名定义的要跟下面 代码块1中的`动态路由参数`部分的 path对应上(取名是指id,而不是item.id)
}进一步改造
import { useRoute } from 'vue-router';
import { data } from './list.json'
const route = useRoute()
//我们从data中获取值
data.find(v=>v.id === route.params.id)//直接找find,会返回一个对象
//这个时候提示"此条件将始终返回false",因为类型"number"和string|string[]没有重叠
//我们知道他的类型是RouteParams,所以我们要进入源码进行寻找原因了,此时进度条在8分08秒
//我们在源码中找到RouteParams,看到他的类型是RouteParamValue|RouteParamValue[]
//RouteParamValue类型是等于string的,数字的话,三个等于号的情况下将永远不会等于字符串。所以我们这里使用Number
const item = data.find(v=>v.id === Number(route.params.id))//在data中找我们需要的东西此时需要同步进行改变的部分
//改变前
//<div>品牌:{{ route.params?.name }}</div>
//<div>价格:{{ route.params?.price }}</div>
//<div>ID:{{ route.params?.id }}</div>
//改变后
<div>品牌:{{ item?.name }}</div> //记得要加上问号,这是可选的意思,不然要是没有name岂不是要爆错,所以要是找不到就不要了,给自己留个余地
<div>价格:{{ item?.price }}</div>
<div>ID:{{ item?.id }}</div>
//代码块1 |
find知识点补充:
find 函数基本格式:
let obj=this.list.find(item=>item.code===val)
首先在这里我们要知道的两个基础知识
一、find 是一个查找函数。
二、箭头函数find(item=>item.code===val)相当于find(item){item.code===val}其中
list是数组,this.list.find()是指在list数组中找某样东西,item是find()函数的寻找某样东西的根据,也可以说是id或者是主键。后面item.code===val是查找这样东西的条件,只有这个item.code完全等于val的时候,才算是找到,才能赋值给obj
//举例
//定义一个数组,有两条数据
companyOptions: [
{
label: '饿了么',
value: 0,
},
{
label: '美团',
value: 1,
},
]
//需要找到value为1,label为美团的数组
let obj=this.companyOptions.find(item=>item.value===1)
小总结
二者的区别
- query 传参配置的是 path,而 params 传参配置的是 name,在 params 中配置 path 无效
- query 在路由配置不需要设置参数,而 params 必须设置
- query 传递的参数会显示在地址栏中
- params 传参刷新会无效,但是 query 会保存传递过来的值,刷新不变;
- 路由配置
第五章——嵌套路由
一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL 的片段通常对应于特定的嵌套组件结构
在原有基础上的变化
我们原本是路由器里面放路由,而嵌套路由就是在路由器里的路由里面继续放路由的一个过程。所以他叫嵌套路由
写法上是在路由里面的
children属性,英文翻译过来就是孩子们的意思,其实也就是子路由(嵌套路由)的意思注意点:
- 你在路由里面继续放路由了,那此时这个二级路由记得要给他们找个容器进行显示
- 你要通过
跳转的时候,路径上不能直接写二级路由的path,要先写上一级路由的path然后/二级路由的path。因为他这个寻找逻辑是一层层寻找的,先找到一级路由再继续往下寻找(直接写二级路由的话,在一级路由肯定是找不到的,就会噶了) children配置只是另一个路由数组,就像routes本身一样。因此,你可以根据自己的需要,不断地嵌套视图
const routes: Array<RouteRecordRaw> = [
{
path: "/user",//一级路由
component: () => import('../components/footer.vue'),
children: [//注意看,嵌套路由的写法是数组里面装对象的形式
{
path: "",//嵌套路由1号(也叫做二级路由)
name: "Login",
component: () => import('../components/login.vue')
},
{
path: "reg",//嵌套路由二号
name: "Reg",
component: () => import('../components/reg.vue')
}
]
},
]
第六章——命名视图
default:默认的意思,这个具体是什么意思呢?就是说你默认显示出来的就是这个default
</router-view name=”名字,看你在components是怎么去命名的”>,就可以具体显示你想要的页面(你可以定义多个view也就是视图),跳转的时候同时显示多个视图的内容
命名视图可以在同一级(同一个组件)中展示更多的路由视图,而不是嵌套显示。 命名视图可以让一个组件中具有多个路由渲染出口,这对于一些特定的布局组件非常有用。 命名视图的概念非常类似于 “具名插槽”,并且视图的默认名称也是
default
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' |
对应 Router-view 通过 name 对应组件
<div> |
- 注意事项
- 这个是components而不是component,因为通过这个多加一个s的,我们可以一次性跳转到多个路由视图中,需要在template中去额外定义多的路由视图,并且通过你在components中取的名字来进行绑定,比如在上面代码块中,我们使用了header跟content这两个名字,那在使用的时候就需要name绑定这两个名字
第七章——重定向-别名
重定向 - redirect
字符串形式配置,访问 / 重定向到 /user (地址栏显示 /, 内容为 /user 路由的内容)
他的属性名就是redirect:
三次书写形式
字符串的写法:
const routes: Array<RouteRecordRaw> = [ { path:'/', component:()=> import('../components/root.vue'), redirect:'/user1',//写法上的区别在这里,这redirect是字符串的写法 children:[ { path:'/user1', components:{ default:()=> import('../components/A.vue') } }, { path:'/user2', components:{ bbb:()=> import('../components/B.vue'), ccc:()=> import('../components/C.vue') } } ] } ]
- 对象的写法:
- ```js
const routes: Array<RouteRecordRaw> = [
{
path: '/',
component: () => import('../components/root.vue'),
redirect: { path: '/user1' },//这里是对象形式的写法,path也会自动对应到下方children的path(子路由)中
children: [
{
path: '/user1',
components: {
default: () => import('../components/A.vue')
}
},
{
path: '/user2',
components: {
bbb: () => import('../components/B.vue'),
ccc: () => import('../components/C.vue')
}
}
]
}
]
函数模式(可以传参)=> 回调形式
const routes: Array<RouteRecordRaw> = [ { path: '/', component: () => import('../components/root.vue'), redirect: (to) => {//写法是以to的形式,把to括起来的这个括号可加可不加 return {//必须要返回值,也就是return path: '/user1', query: to.query//由于这里to没有传值过来,我们可以暂时自己写一个 //query:{name:"小余"},这个name=小余会在URL,也就是路径中体现出来 } }, children: [ { path: '/user1', components: { default: () => import('../components/A.vue') } }, { path: '/user2', components: { bbb: () => import('../components/B.vue'), ccc: () => import('../components/C.vue') } } ] } ]
## 别名 alias
> **将 `/` 别名为 `/`**root**,意味着当用户访问 `/`**root **时,URL 仍然是 `/user`,但会被匹配为用户正在访问 `/`**
>
> 通俗的说就是给路由取多个名字,但访问的组件都是同一个(比如我们都知道鲁迅跟周树人,这是两个名字,但其实都是指同一个人)
```js
const routes: Array<RouteRecordRaw> = [
{
path: '/',//我就是下面说的path路径
component: () => import('../components/root.vue'),//访问/root,/root2,/root3都是展示这个组件
alias:["/root","/root2","/root3"],//三个名字,或者说是上面这个path路径的外号或者说是别名
children: [
{
path: 'user1',
components: {
default: () => import('../components/A.vue')
}
},
{
path: 'user2',
components: {
bbb: () => import('../components/B.vue'),
ccc: () => import('../components/C.vue')
}
}
]
}
]
第八章——导航守卫-前置守卫
全局前置守卫
router.beforeEach
小满称呼:中间件
//所有跳转、后退都会走这个函数 |
小满在这里做的是一个登录的校验,使用了组件库这里都不进行说明
小满在这里使用了一个@别名,在这里对这个进行一个解释
在vite.config.ts文件下,代码如下 export default defineConfig({ plugins:[vue(),vueJsx()], resolve:{ alias:{ '@':fileURLToPath(new URL('./src',import.meta.url)), } } })
- 这样的作用是对你的文件起了一个别名,此处的@的别名对应的是./src。从上面的路径中我们也可以看到
### resolve.alias
在组件之间相互引用时,可能是下面这样的:
```js
import Hello from '../src.components/Hello';
其中的路径是相对于当前页面的。 但是如果嵌套等更为复杂,那么写起来会比较麻烦。但是如果我们通过这样的配置:
resolve: { |
其中 vue$ 表示引入 vue,就可以像下面这么写:
import Vue from 'vue' |
另外,对于 @pages 和 @components 我们就可以直接引用了,而省去了一大堆的复杂应用,另外通过 @可以消除歧义。如下所示:
import Hello from '@components/Hello'; |
值得注意的时: 在 webpack.config.js 中我们不能使用../ 以及./ 这种形式的路径方式,而是通过 path.join 和 __dirname 这种形式来表示路径,否则会报错。
另外: 在组件中,我们会引用一些静态文件,即 static 下的文件, 这时我们就不能用 alias 下的配置了,而必须使用一般的配置方式。
我没找到vite的,根据webpack触类旁通
const rules = reactive({ |
每个守卫方法接收三个参数:
to: Route, 即将要进入的目标 路由对象; |
案例 权限判断
const whileList = ['/']//白名单 |
呃,这个使用组件库的过程不好写笔记,建议自己看看视频
全局后置守卫(内容较深,后续回来补笔记)
使用场景一般可以用来做 loadingBar
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受
next函数也不会改变导航本身:和前置路由守卫相比为什么没有next功能呢?那是因为前置路由守卫是看大门前面的,而后置路由守卫则是在大门的后面。前面的保安如果都放行进来了,那后面的保安还要再确认一遍就太过繁复也没有必要啦
router.afterEach((to,from)=>{ |
loadingBar 组件
<template> |
main.ts
import loadingBar from './components/loadingBar.vue' |
上述牵扯到的知识点补充:
计时器一直是javascriptz动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样
才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms
而setTimeout和setIntervall的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器U线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行
requestAnimationFrame.采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果
requestAnimationFrame 比起 setTimeout、setInterval 的优势主要有两点:
1、requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒 60 帧。
2、在隐藏或不可见的元素中,requestAnimationFrame 将不会进行重绘或回流,这当然就意味着更少的的 cpu,gpu 和内存使用量。
第九章——路由元信息
路由元信息
通过路由记录的
meta属性可以定义路由的元信息。使用路由元信息可以在路由中附加自定义的数据,例如:
- 权限校验标识。
- 路由组件的过渡名称。
- 路由组件持久化缓存 (keep-alive) 的相关配置。
- 标题名称
有时我们想将一些信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的
meta属性来实现,并且它可以在路由地址和导航守卫上都被访问到。定义路由的时候你可以这样配置meta字段
我们可以在导航守卫或者是路由对象中访问路由的元信息数据。
const router = createRouter({ |
使用 TS 扩展
如果不使用扩展 将会是 unknow 类型
declare module 'vue-router' { |
如何获取元信息
在组件中可以通过 this.$route.meta 来获取
在路由中获取:
router.beforeEach((to, from, next) => { |
- 通过这类方法我们可以在配置文件中通过router.beforeEach,也就是前置路由守卫中进行设置
- 前置路由守卫有三个参数分别为
to,from,next。我们这里使用到to,意思是将要去哪里的意思,我们通过to.meta.title拿到了我们在routers中对应的路由中配置的mate中的title,然后将这个值赋值给document.title - 产生的效果就是能够在路由跳转后,网页的标题也随之发生改变,变成对应路由的信息(比如我们设定好的登录和首页)
- 前置路由守卫有三个参数分别为
第十章——路由过渡动效
过渡动效
想要在你的路径组件上使用转场,并对导航进行动画处理,你需要使用 v-slot API:
使用组件库的时候记得先安装(npm安装方式:npm install animate.css)后import引入
<router-view #default="{route,Component}">//#default,像这种使用井号的是简写方式 |
上面的用法会对所有的路由使用相同的过渡。如果你想让每个路由的组件有不同的过渡,你可以将元信息和动态的
name结合在一起,放在<transition>上:
declare module 'vue-router'{
interface RouteMeta {
title:string,
transition:string,
}
}
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
component: () => import('@/views/Login.vue'),
meta:{
title:"登录页面",
transition:"animate__fadeInUp",//动画组件库
}
},
{
path: '/index',
component: () => import('@/views/Index.vue'),
meta:{
title:"首页!!!",
transition:"animate__bounceIn",
}
}
]
})
第十一章——滚动行为
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。vue-router 可以自定义路由切换时页面如何滚动。
当创建一个 Router 实例,你可以提供一个
scrollBehavior方法
const router = createRouter({ |
const router = createRouter({ |
scrollBehavior 方法接收 to 和 from 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进 / 后退 按钮触发) 时才可用。
scrollBehavior 返回滚动位置的对象信息,长这样:
- { left: number, top: number }
const router = createRouter({ |
第十二章——动态路由
我们一般使用动态路由都是后台会返回一个路由表前端通过调接口拿到后处理 (后端处理路由)
主要使用的方法就是 router.addRoute
调用接口需要安装axios,命令:npm install axios -S
import引入axios
添加路由
动态路由主要通过两个函数实现。router.addRoute() 和 router.removeRoute()。它们只注册一个新的路由,也就是说,如果新增加的路由与当前位置相匹配,就需要你用 router.push() 或 router.replace() 来手动导航,才能显示该新路由
router.addRoute({ path: '/about', component: About }) |
删除路由
有几个不同的方法来删除现有的路由:
通过添加一个名称冲突的路由。如果添加与现有途径名称相同的途径,会先删除路由,再添加路由:
router.addRoute({ path: '/about', name: 'about', component: About })
// 这将会删除之前已经添加的路由,因为他们具有相同的名字且名字必须是唯一的
router.addRoute({ path: '/other', name: 'about', component: Other })通过调用
router.addRoute()返回的回调:const removeRoute = router.addRoute(routeRecord)
removeRoute() // 删除路由如果存在的话- 当路由没有名称时,这很有用。
通过使用
router.removeRoute()按名称删除路由:router.addRoute({ path: '/about', name: 'about', component: About })
// 删除路由
router.removeRoute('about')需要注意的是,如果你想使用这个功能,但又想避免名字的冲突,可以在路由中使用
Symbol作为名字。
当路由被删除时,所有的别名和子路由也会被同时删除
查看现有路由
Vue Router 提供了两个功能来查看现有的路由:
- router.hasRoute():检查路由是否存在。
- router.getRoutes():获取一个包含所有路由记录的数组。
案例
前端代码
注意一个事项 vite 在使用动态路由的时候无法使用别名 @ 必须使用相对路径
const initRouter = async () => { |
后端代码 nodejs express
import express, { Express, Request, Response } from 'express' |




