Vue Router 是 Vue.js 的官方路由器。通过使用 Vue Router,你可以构建一个包含多个页面的应用程序。它可以样多个页面之间流畅地跳转,而无需每次移动到另一个页面时都要重新加载整个页面。Vue Router 路由是使用 Vue.js 构建单页应用项目的必备库。官网地址https://router.vuejs.org/zh/introduction.html
由一个 HTML 页面组成的应用程序称为单页应用程序 (SPA)。在由多个 HTML 文件组成的应用程序的情况下,使用浏览器功能移动页面,但在 SPA 中,通过使用 JavaScript 技术仅更新页面中的一部分内容来实现页面移动。
在vue项目中使用路由功能都需要先导入vue-router组件。
npm install vue-router@4
导入成功后会在项目package.json 文件中看到 “vue-router”: "^4.1.6"内容。
"dependencies": {
"vue": "^3.2.41",
"vue-router": "^4.1.6"
},
SPA 是单页应用程序的缩写。
通常在一个页面内搜索或导航到另一个页面时,屏幕会同步刷新。
第一章 Vue3项目创建 1 Vue CLI 创建vue项目
第一章 Vue3项目创建 2 使用 Webpack 5 搭建 vue项目
第一章 Vue3项目创建 3 Vite 创建 vue项目
第二章 Vue3 基础语法指令
第三章 Vue Router路由器的使用
第四章 VUE常用 UI 库 1 ( element-plus,Ant ,naiveui,ArcoDesign)
第四章 VUE常用 UI 库 2 ( ailwind 后台框架)
创建一个简单的路由项目,需要以下五项基本设置。
项目结构
zht-vite-vue
|---node_modules
|---index.html //运行html
|---src //代码源文件
| |--zht //组件目录
| | |---zht.vue //模块代码
| |--main.js //入口文件
| |--App.vue //模板代码
| |--router.js //路由控制器
|----package.json //配置文件
1 创建路由配置文件router.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './zht/zht.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/zht',
name: 'zht',
component:Home
},
{
path: '/about',
name: 'about',
component: () => import('../src/zht/zht.vue')
}
]
})
export default router
2 zht.vue页面
在zht目录中创建一个被访问页面zht.vue。
<script setup>
script>
<template>
<h1>路由访问到我了zht页面h1>
template>
3 将路由注册到实例中(main.js)
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
4 页面router-view 与 router-link 使用(App.vue)
<script setup>
script>
<template>
<h1>Vue 3 路由h1>
//路由跳转连接
<router-link to="/zht">zhtrouter-link>
<br>
<router-link to="/about">aboutrouter-link>
//路由内容显示地址 将路由中的组件注册到App 组件
<RouterView />
template>
<header>
<nav>
<a href="/zht">Homea>
<a href="/about">Abouta>
nav>
header>
Vue路由Router示意图
路由在加载页面的时候会分为两种模式,一种是正常模式,一种是延迟加载,开发者可以根据自己项目需要来选择适合自己项目的模式。两种模式主要的区别就在有在路由类中引入的方法上。
1 普通加载
通过import 导入的vue组件,在将导入组件的引用以参数的方式设置给路由类中对应的路径对象中的component属性。这种方式称谓普通加载。
import { createRouter, createWebHistory } from 'vue-router'
import zht1 from './zht/zht.vue'
import zht2 from './zht/zht.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/zht1',
name: 'zht1',
component:zht1
},
{
path: '/zht2',
name: 'zht2',
component:zht2
}
]
})
export default router
项目启动后,使用浏览器开发人员工具的网络选项卡中可以看到zht.vue页面已经和路由组件一起加载到浏览器内存中。我们看到了当浏览器进入路由控制页面时候,那些路由器类中被设置成普通加载的页面,都会同时被加载到浏览器内存中。
由于已经在浏览器内存中下载了 zht.vue 文件,因此页面上路由路径跳转到 /zht页面时无需额外下载,直接使用到zht.vue模板内容。
运行 npm run build 命令后,我们来查看生产环境中的构建出来的代码是否有什么变化。运行构建后可以看到在 dist/assets 文件夹下打包了一个 index.XXX.js 文件。稍后我们与延迟加载后的构建文件进行比较,大家就能看到区别了。
2 延迟加载
通过component: () => 的加载vue组件的方式为延迟加载方式。
import { createRouter, createWebHistory } from 'vue-router'
import zht1 from './zht/zht.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/zht1',
name: 'zht1',
component:zht1
},
{
path: '/zht2',
name: 'zht2',
//延迟加载lazy-loaded
component:()=>import('./zht/zht2.vue'),
}
]
})
export default router
浏览器开发人员工具中的网络选项卡中我们没有看到zht2.vue模板被加载进入浏览器缓存中。
当我们在页面中点击路由连接,进入zht2.vue页面中,这个时候浏览器才加载zht2.vue模板页面到浏览器缓存中。
当执行npm run build 命令创建生产环境的构建文件时,在 dist/assets 文件夹可以看到zht2.XXX.js 与index.XXX.js 文件是分开创建的,这与之前的普通加载不同,之前普通加载模式都是将打包代码创建在一个构建文件中。
这样创建多个构建文件称为代码拆分。通过将其分成多个 JavaScript 文件而不是单个 JavaScript 文件,我们可以减少首次访问时的下载构建文件大小。由于只有在有访问权限的情况下才会下载相关的JavaScript文件,所以不需要下载那些我们没有使用到的JavaScript文件。
静态路由是url静态路径例如:/(route) 或/about不能传参数值,这样的路径在开发和使用的时候就很多问题无法解决,没有办法进行页面之间的参数传递。这个时候需要使用到能在url路径中传递参数的功能,我们将有这种的路由称为动态路由。
1 创建一个列表集合
在App.vue文件中将写入下面的代码内容,将反应函数中的集合属性映射到路由路径中作为参数,将它传递到接收页面中去。在这里我们将users列表中的id属性赋值到路由路径中去,在接收页面中获得到这个id信息。
<script setup>
import { ref, onMounted } from 'vue';
const users = ref([
{ id: 1, name: 'zht', email: '[email protected]', admin: true },
{ id: 2, name: 'kaimi', email: '[email protected]', admin: false },
{ id: 3, name: 'fun', email: '[email protected]', admin: false },
]);
script>
<template>
<h1>Vue 3 路由h1>
<h2>Users列表h2>
<ul>
<li v-for="user in users" :kye="user.id">
<RouterLink :to="`/zht2/${user.id}`">{{ user.name }}RouterLink>
li>
ul>
<RouterView />
template>
2 配置动态路由(router.js)
import { createRouter, createWebHistory } from 'vue-router'
import zht1 from './zht/zht.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/zht1',
name: 'zht1',
component:zht1
},
{
path: '/zht2/:id',-------------------------------------------- ➊
name: 'zht2',
component:()=>import('./zht/zht2.vue'),
}
]
})
export default router
在动态变化的 id 前设置一个 :(冒号),动态路由参数都是在url后加冒号加参数(/zht2/:id)表示动态传递参数给路由。
3 业务页面
在项目中的zht目录下创建zht2.vue文件,在文件中写入下面的代码。
<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
alert(route.params);
script>
<template>
<h2>User内容h2>
<p>User Id: {{ $route.params.id }}p>
template>
我们如何获得url中包含的id值,这就需要使用到useRoute 路由的全局变量这个类。通过路由的 useRoute对象的params中来获得id值。
userRoute对象需要在vue-router包中引入
const route = useRoute(); 获得路由器引用对象
route.params 获得路由中的参数集合
route.params .参数名称 获得路径中的参数
在模板中可以使用 $route 对象直接访问到 params 获得路由中的参数集合。例如 $route 对象的 params.id。
假设我们要更换一个路径中的参数名称,需要在路由类中找到它对应的路径配置对象,在这个对象中将参数名称修改为要更改的参数名称。我们将参数名称id改为userId,在路由获得时候对应的名称也需要改变。
import { createRouter, createWebHistory } from 'vue-router'
import zht1 from './zht/zht.vue'
const router = createRouter({
{
path: '/zht2/:userId',---|
name: 'zht2', |
} |
] |
}) |
// 迁移页面zht2.vue中获得这个值 |
const route = useRoute(); |
const user = ref([]); |
const id = parseInt(route.params.userId);
我修改一下路由业务页面加入信息查询显示功能。
返回
User信息 获得id:{{id}}
- User Id: {{ user.id }}
- User Name: {{ user.name }}
- User Email: {{ user.email }}
加入返回功能按钮。单击后退链接返回到用户列表,并确认单击不同的用户名会将您带到所单击用户的详细信息页面。动态路由配置现已完成。
这个时候在使用的时候大家会发现问题,当第一次访问页面的时候页面内容会出变化,当再次访问页面的时候页面中的内容就不会在发生任何变化了。只有点击一次返回将路由内容清空,在点击列表中的内容,路由页面中的元素才会在发生变化。
为什么页面内容不更新呢?
这是因为每次介入新的页面中都会重用了UserView组件,但是程序又没有执行该组件的卸载过程,同时也没有执行框架生命周期钩子的挂载过程(onMounted)。
为了能使/zht2/1和/zht2/2都会更新页面,我们需要监听id值的变化,以便在params的id值更新时进行数据获取过程,需要做吧。使用watch函数可以监控id值的更新。
import { ref,onMounted,watch } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
onMounted(async () => {
watch(route, () => {
const id = parseInt(route.params.id);
user.value=users[id-1];
});
});
通过将路由对象指定监听的参数,当点击 第一个链接将在控制台中显示 {id:‘1’} ,点击 第二 链接将显示 {id:‘2’} 。可以看到 watch 检测到 id 更新。
<script setup>
import { ref,onMounted,watch } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const user = ref([]);
const users = [
{ id: 1, name: 'zht', email: '[email protected]', admin: true },
{ id: 2, name: 'kaimi', email: '[email protected]', admin: false },
{ id: 3, name: 'fun', email: '[email protected]', admin: false },
];
//监听路由值变化
onMounted(async () => {
watch(route, () => {
const id = parseInt(route.params.id);
user.value=users[id-1];
});
});
script>
<template>
<RouterLink to="/zht2">返回RouterLink>
<h2>User信息 获得id:{{id}}h2>
<ul>
<li>User Id: {{ user.id }}li>
<li>User Name: {{ user.name }}li>
<li>User Email: {{ user.email }}li>
ul>
template>
RouterLink 设置
还有一种方法是在 RouterLink 组件上设置监听键值,来达到和上边一样的效果。
<script setup>
import { ref, onMounted } from 'vue';
const users = ref([
{ id: 1, name: 'zht', email: '[email protected]', admin: true },
{ id: 2, name: 'kaimi', email: '[email protected]', admin: false },
{ id: 3, name: 'fun', email: '[email protected]', admin: false },
]);
script>
<template>
<h1>Vue 3 路由h1>
<router-link to="/zht1">zhtrouter-link>
<br>
<router-link to="/zht2">aboutrouter-link>
<h2>Users列表h2>
<ul>
<li v-for="user in users" :kye="user.id">
<RouterLink :to="`/zht2/${user.id}`">{{ user.name }}RouterLink>
li>
ul>
//监听路由参数
<RouterView :key="$route.params.id" />
template>
zht2.vue
<script setup>
import { ref} from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const user = ref([]);
const users = [
{ id: 1, name: 'zht', email: '[email protected]', admin: true },
{ id: 2, name: 'kaimi', email: '[email protected]', admin: false },
{ id: 3, name: 'fun', email: '[email protected]', admin: false },
];
const id = parseInt(route.params.id);
user.value=users[id-1];
script>
<template>
<RouterLink to="/zht2">返回RouterLink>
<h2>User信息 获得id:{{id}}h2>
<ul>
<li>User Id: {{ user.id }}li>
<li>User Name: {{ user.name }}li>
<li>User Email: {{ user.email }}li>
ul>
template>
通过路由名字获得路由
除了 path
之外,你还可以为任何路由提供 name
。URL 是在 RouterLink 组件的 to 属性中设置的,但是您可以使用路由中设置的 name 值来代替 URL。除了path和component之外,每条路由还有一个name属性,所以使用这个值来设置链接。
运行动态路由设置的 /user/:id 页面的链接时,通过设置 params 属性来设定路由动态参数。
<RouterLink :to="{ name: 'zht1' }">zht页面RouterLink>
<RouterLink :to="{ name: 'zht2', params: { id: 1 } }">User1RouterLink>
<RouterView />
props 是一个特殊的关键字,它是路由属性中一种注册组件。路由中只能使用动态得url传递参数,满足不了一些复杂的业务参数传递。这个时候我们可以使用props 组件来完成一些复杂的参数传递功能。
例如 在动态路由中,URL中包含的动态值(id)需要使用useRouter和route对象获取的id的值,但是我们要使用props来获取id值。当 props
设置为 true
时,route.params
将被设置为props组件。
{
path: '/zht2/:id',
name: 'zht2',
component:()=>import('./zht/zht2.vue'),
// route.params设置成props组件可以使用
props: true,
}
UserView 组件使用 defineProps 来接收props中的参数内容。接收到的 props中参数变量被转化成为一个对象,因此可以通过这个转化对象 props.id 获取动态路由中的id值。
<script setup>
import { ref,onMounted} from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const user = ref([]);
//props 参数转换
const props = defineProps({
id: String,
});
onMounted(() => {
const id = parseInt(props.id);
user.value=users[id-1];
});
const users = [
{ id: 1, name: 'zht', email: '[email protected]', admin: true },
{ id: 2, name: 'kaimi', email: '[email protected]', admin: false },
{ id: 3, name: 'fun', email: '[email protected]', admin: false },
];
script>
<template>
<RouterLink to="/zht2">返回RouterLink>
<h2>User信息 获得id:{{id}}h2>
<ul>
<li>User Id: {{ user.id }}li>
<li>User Name: {{ user.name }}li>
<li>User Email: {{ user.email }}li>
ul>
template>
函数模式与路由参数绑定
在router.js中设置props传递的参数可以被定义成json对象。其中属性(id) 可以与路由动态参数进行绑定,也可以定义成固定参数。
{
path: '/zht2/:id', <--------------------|
name: 'zht2', |
component:()=>import('./zht/zht2.vue'), |
props: (route) => ({ |
id: route.params.id, <--------------|
name: 'zht'
}),
}
在UserView 组将中使用defineProps转化参数对象。
const props = defineProps({
name: String,
id: String,
});
<script setup>
import { ref,onMounted} from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute();
const user = ref([]);
//转化props 参数对象 id:{{props.id}} 名字:{{props.name}}
const props = defineProps({
name: String,
id: String,
});
onMounted(() => {
const id = parseInt(props.id);
user.value=users[id-1];
});
const users = [
{ id: 1, name: 'zht', email: '[email protected]', admin: true },
{ id: 2, name: 'kaimi', email: '[email protected]', admin: false },
{ id: 3, name: 'fun', email: '[email protected]', admin: false },
];
script>
<template>
<RouterLink to="/zht2">返回RouterLink>
<h2>User信息 获得id:{{props.id}} 名字:{{props.name}}h2>
<ul>
<li>User Id: {{ user.id }}li>
<li>User Name: {{ user.name }}li>
<li>User Email: {{ user.email }}li>
ul>
template>
很多时候页面的UI是由多层组件嵌套组成的。我们可以使用路由的嵌套功能,通过嵌套使UserView组件相互组合在一起。嵌套时,在路由设置中使用“children”,这样可以确定路由组件的父子关系,以便创建它们的层次结构。
{
path: '/zht2/:id',
name: 'zht2',
component:()=>import('./zht/zht2.vue'),
props: (route) => ({
id: route.params.id,
name: 'zht'
}),
//子路由组件
children: [
{
path: 'zht3',
component:()=>import('./zht/zht3.vue'),
},
],
}
创建zht3.vue文件,写入以下内容。
<script setup>
script>
<template>
<h1>Zht3页面h1>
template>
设置URL /zht2/:id/zht3来访问页面。在浏览中可以看到显示父子页面。
<template>
<h1>Vue 3 路由h1>
<router-link to="/zht1">zhtrouter-link>
<br>
<router-link to="/zht2">aboutrouter-link>
<h2>Users列表h2>
<ul>
<li v-for="user in users" :kye="user.id">
//路由访问方式
<RouterLink :to="`/zht2/${user.id}/zht3`">{{ user.name }}RouterLink>
li>
ul>
<RouterView :key="$route.params.id" />
template>
zht2.vue中加入RouterView 组件,用于显示子页面内容。
<template>
<RouterLink to="/zht2">返回RouterLink>
<h2>User信息 获得id:{{props.id}} 名字:{{props.name}}h2>
<ul>
<li>User Id: {{ user.id }}li>
<li>User Name: {{ user.name }}li>
<li>User Email: {{ user.email }}li>
ul>
<RouterView />
template>
除了使用
创建 a 标签来定义导航链接。还可以使用 router 的实例方法,通过编写代码来实现路由器组件的页面移动功能。
在代码中使用router对象的push方法,通过设置其中的 path 属性来找到要移动的页面组件。通过事件来触发页面有移动功能。
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const onpage=()=>{
router.push({ path: '/zht1',
query: {
name: 'zht',
id: 18
} })
}
script>
<template>
<h1>Vue 3 路由h1>
<button @click="onpage">zht页面button>
<button @click="$router.push({ path: '/zht1' })">事件button>
<RouterView />
template>
除了push方法,还有back方法和go方法。back 方法允许您返回到您来到该页面之前查看过的页面。go方法可以设置一个数字作为参数,-1表示上一页,-2表示后退两页。go 方法可以前进也可以后退。如果返回一次页面后想返回原页面,可以通过设置go(1)返回原页面。
<button @click="$router.back()">返回button>
<button @click="$router.go(-1)">返回button>
<button @click="$router.go(1)">前进button>
访问不存在的页面
当路由出现错误的时候,所有页面跳转到错误页面。
import { createRouter, createWebHistory } from 'vue-router'
import zht1 from './zht/zht.vue'
import errer from './zht/errer.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/zht1',
name: 'zht1',
component:zht1
},
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: errer }
]
})
export default router
创建一个错误信息页面
<script setup>
script>
<template>
<h1>我的错误信息h1>
template>
在页面转发方法中给一个没有的路由路径zht5,我们在浏览中点击按钮,看到浏览器转发到错误页面。
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
function onpage(){
router.push({ path: '/zht5',
query: {
name: 'zht',
id: 18
} })
}
const id=1;
script>
<template>
<h1>Vue 3 路由h1>
<button @click="onpage()">zht页面错误页面button>
<RouterView />
template>
重定向redirect方法
在路由中设置redirect属性将路由中的路径设置为重定向。
import { createRouter, createWebHistory } from 'vue-router'
import zht1 from './zht/zht.vue'
import errer from './zht/errer.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/zht1',
name: 'zht1',
component:zht1
},
{
path: '/home',
redirect: '/zht1',
}
]
})
export default router
也可以使用redirect 名字的方法来重定向到/zht1路由组件上。
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/zht1',
name: 'zht1',
component:zht1
},
{
path: '/home',
redirect: { name: 'zht1' },
},
Navigation Guard是一个功能,主要用于根据是否完成认证来限制可访问的路由。
导航守卫有几种类型,例如为整个路由配置的 Global Guards,为单个路由配置的 Per-Route Guards,以及为每个组件配置的 In-Component Guards。基本用法是一样的,只是设置位置不同。对于那些第一次学习路由的人来说,Navigation guards 乍一看可能会让人望而生畏,所以让我们用一个简单的例子来看看它是什么样子的。
Global Before Guards,顾名思义,就是应用于整个路由的导航守卫。
使用由 createRouter 函数创建的路由器实例的 beforeEach 方法 (router.beforeEach) 进行设置。
beforeEach 有两个参数,一个是to,一个是from。由于to和from也是任何navigation guard中都会用到的对象,所以我们检查to和from各自有什么值下面的代码只是将to和from的值输出到控制台,对导航没有影响。
to
: 即将要进入的目标from
: 当前导航正要离开的路由const router = createRouter({ ... })
router.beforeEach((to, from) => {
console.log('to:', to);
console.log('from:', from);
return true;
})
false
: 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from
路由对应的地址。