
vue3——集成路由Vue Router
1. 集成路由Vue Router
1.1. 使用
1.1.1. 安装Vue Router
在项目的根目录下,运行以下命令安装与Vue 3兼容的Vue Router最新版本。
npm install vue-router@4
1.1.2. 创建路由配置
创建router.js文件或在router目录下创建index.js文件来配置路由,代码如下。
// router/index.js
import { createRouter, createWebHashHistory } from 'vue-router';
// 引入组件
import Home from '../views/Home.vue';
import About from '../views/About.vue';
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
上述代码中,首先导入了必要的方法和组件:
[插图] 从vue-router包中导入createRouter和createWebHashHistory方法,createRouter用于创建路由器实例,createWebHashHistory提供了基于哈希的路由历史管理。
[插图] 引入Home.vue和About.vue作为路由页面组件,这些组件用于不同路由的页面内容。
然后,定义路由数组配置映射:
[插图] 定义名为routes的数组,表示应用的路由结构,其中每个对象包含path和component属性,path定义URL路径,component指定对应的Vue组件。
[插图] 当访问根路径“/”时,渲染Home组件;访问“/about”时,渲染About组件。
最后,创建和导出路由器实例:
[插图] 使用createRouter创建路由器,配置为哈希模式(createWebHashHistory)。在URL中,路由变化将通过#标识表示。
[插图] 使用export default router导出创建的路由器实例,允许在应用的其他部分,如main.js,引入和使用这个路由器。
1.1.3. 引入路由到主文件
在main.js文件中引入并使用路由器。
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');
1.1.4. 创建示例组件
为了演示路由的工作,需要创建至少两个组件来表示不同的页面。例如,创建Home.vue,代码如下。
<!-- views/Home.vue -->
<template>
<div>
<h1>主页面</h1>
<p>欢迎访问主页面.</p>
</div>
</template>
About.vue代码如下。
<!-- views/About.vue -->
<template>
<div>
<h1>关于页面</h1>
<p>欢迎访问关于页面.</p>
</div>
</template>
1.1.5. 添加路由视图和链接
在App.vue或其他根组件中,使用<router-view>和<router-link>。
<!-- App.vue -->
<template>
<div>
<nav>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
</nav>
<router-view ></router-view>
</div>
</template>
上述代码中使用了Vue Router的两个核心组件:<router-link>和<router-view>。以下是它们的详细解释。
[插图] <router-link>用于创建导航链接,to属性定义了链接的目标路由。例如,to="/"表示链接到根路径(即主页);to="/about"表示链接到/about路径(即关于页面)。
[插图] <router-view>是实现单页应用的关键,用于定义内容区域,显示当前路由对应的组件内容,它可以根据URL变化,渲染与路由匹配的组件。
1.2. 路由参数和查询字符串
1.2.1. 路由参数
路由参数是创建动态路由的有效工具。动态路由允许通过变化的URL部分匹配多个路由,这对于需要根据特定数据(如ID)显示不同内容的页面非常有用。
假设有一个博客应用,需要为每篇博客文章创建一个详情页面,这里不需要为每篇文章创建独立路由,可以使用一个动态路由来处理所有文章的详情显示,路由配置如下。
// router/index.js
import { createRouter, createWebHashHistory } from 'vue-router';
import BlogPost from '../views/BlogPost.vue';
const routes = [
// 动态路径参数以冒号":"开头
{ path: '/post/:id', component: BlogPost },
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
在上面的例子中,:id作为动态路径参数,匹配任何传入的值,当访问如/post/1或/post/2等路径时,都会渲染BlogPost组件。
通过使用动态路由,可以为每篇博客文章生成一个唯一的URL,同时只需一个组件来展示所有文章的内容。这种方法大大简化了路由的配置。
当使用动态路由时,路由参数(如上例中的id)将会被传递到组件中,可以通过useRoute函数来访问。
例如,在BlogPost组件中使用useRoute函数从路由参数中提取id:
<template>
<div>
<h1>博客ID: {{ postId }}</h1>
</div>
</template>
<script setup>
import { useRoute } from 'vue-router';
const route = useRoute();
// 获取路由参数id
const postId = route.params.id;
</script>
在上述代码中,useRoute是Vue Router的组合式API,它提供了对当前路由对象的访问。从这个路由对象中,可以直接访问params属性以获取动态路由参数,如id,然后可以使用这个id来获取或处理相应的文章数据。
1.2.2. 查询参数
查询字符串是URL的一部分,用于传递非层级数据。它们通常位于URL的“?”后面,以键-值对的形式出现。
const route = useRoute();
const searchKeyword = computed(() => route.query.keyword);
</script>
总的来说,路由参数适用于定义页面主要内容的数据,通常是必需的,并且是URL路径的一部分。查询字符串则用于传递辅助性质的、可选的信息,不会改变页面的基本结构或内容。
1.3. 嵌套路由
Vue Router的嵌套路由功能允许在单个路由下创建子路由结构,这对于构建具有多层次页面结构的应用尤其有用。例如,一个有多个子页面或子部分的页面。
在嵌套路由中,每个路由可以有自己的子路由,子路由可以进一步包含自己的子路由,形成一个层次结构。这样做的好处是可以保持应用的UI和URL结构的一致性,同时保持各个组件的独立性和可复用性。
假设一个博客应用,主页有多个部分,如博客列表和关于页面,可以设置一个根路由/对应主页组件,Home组件包含子路由,如/about和/blog。
首先创建子组件文件About.vue和BlogList.vue,然后在路由配置中设置嵌套路由,路由配置如下。
// router/index.js
import { createRouter, createWebHashHistory } from 'vue-router';
import Home from '../views/Home.vue';
import About from '../views/About.vue';
import BlogList from '../views/BlogList.vue';
const routes = [
{
path: '/',
component: Home,
children: [
{
path: 'about',
component: About
},
{
path: 'blog',
component: BlogList
}
]
}
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
在这个配置中,Home组件是根路由的组件,它有两个子路由:about和blog。这意味着当用户访问/about或/blog时,Home组件将被加载,并且根据URL的不同,相应的子组件(About或BlogList)也会在Home组件内部的<router-view>中被渲染。
在根路由的配置中,使用children数组定义了嵌套路由。这意味着在Home组件内部,可以用自己的视图区域来显示不同的子组件。具体的子路由如下。
在这个配置中,Home组件扮演着“布局”或“框架”角色,它会一直显示,而About和BlogList组件将根据当前路由动态显示在Home组件内部的<router-view>中。这就是嵌套路由的核心概念,即允许在父路由组件中嵌套显示子路由组件。
在Home.vue组件中,需要添加一个<router-view>元素作为子组件的挂载点:
<template>
<div>
<h1>主页</h1>
<router-view></router-view> <!-- 子路由将在这里渲染 -->
</div>
</template>
当访问/about时,Home.vue组件将被加载,并且About.vue将在Home.vue中的<router-view>中被渲染。同样,当访问/blog时,BlogList.vue组件将被加载到同一个位置。
1.4. 编程式导航
Vue Router的编程式导航是一种使用代码控制路由跳转的方法,相较于声明式导航(如使用<router-link>),它提供了更灵活的方式来处理应用的导航行为。
1.4.1. router.push()方法
router.push()方法用于在历史记录中添加一个新的记录,类似于单击一个<router-link>,当调用router.push()方法时,会将新路由添加到历史堆栈中,用户可以使用浏览器的后退按钮返回之前的路由。
router.push()方法接收一个字符串或路由描述对象。例如:
// 使用字符串
router.push('/about');
// 使用路由描述对象
router.push({ path: '/about' });
// 传递查询参数和哈希
router.push({ path: '/about', query: { ref: '123' }, hash: '#section' });
1.4.2. router.replace()方法
router.replace()方法与router.push()类似,但它不会向历史记录栈中添加新记录。这意味着用户无法使用浏览器的后退按钮返回之前的页面。这在某些情景下非常有用,如在登录后将用户重定向到另一个页面,而不希望用户返回到登录页。代码如下。
router.replace('/home');
1.5. 路由守卫和导航保护
1.5.1. 路由守卫
路由守卫是Vue Router提供的一种机制,用于在路由发生变化时执行一些操作,如验证用户权限、记录页面访问日志、改变页面状态等,典型的应用场景如下。
[插图] 验证用户权限:检查用户是否有权访问特定路由。
[插图] 数据加载和清理:在路由跳转前后进行。
[插图] 防止用户离开未保存更改的页面:如表单编辑页面。
Vue Router提供了以下三种类型的路由守卫来控制导航行为。
[插图] 全局守卫:对所有路由有效地守卫。
[插图] 路由独享守卫:仅对某个特定路由有效。
[插图] 组件级守卫:仅在特定组件中使用。
1.5.1.1. 全局守卫
全局守卫是对所有路由生效的守卫,Vue Router提供了用于在不同阶段控制路由行为的方法,这些方法可以再创建Vue Router实例后直接调用,其中:
[插图] beforeEach():在路由进入之前全局调用。
[插图] beforeResolve():在路由解析(包括组件加载)之后,进入守卫之前调用。
[插图] afterEach():在路由进入后调用,不会接收next函数,也不会更改导航本身。
在三个方法中,beforeEach()方法应用的最为广泛,它会在路由跳转发生之前被调用。这是设置权限和认证逻辑的理想地点,示例用法如下。
// 创建router实例
const router = createRouter({ … })
router.beforeEach((to, from, next) => {
// 检查路由是否需要用户认证
if (to.meta.requiresAuth && !isUserLoggedIn()) {
// 如果用户未登录,则重定向到登录页面
next({ path: '/login' });
} else {
// 如果认证通过或路由不需要认证,则正常跳转
next();
}
});
上述代码中的beforeEach()方法接收一个函数作为参数,该函数又接收三个参数:to、from和next,以下是这三个参数的解释。
[插图] to:这是一个路由对象,代表即将进入的目标路由。通过to可以访问即将导航到的路由的信息,如路由的路径、查询参数等。
[插图] from:这是另一个路由对象,代表当前正离开的路由。它提供了当前路由的类似信息,可以用于比较即将进入的路由和当前路由之间的差异,或者是执行离开路由的特定逻辑。
[插图] next:这是一个必须调用的函数,调用next会将控制权交给路由守卫链中的下一个守卫。这就像是在一个接力赛中传递接力棒,只有当每个守卫都运行了next,整个路由跳转过程才会继续进行,如果在路由守卫中没有调用next,当前的路由跳转将被中断,URL将保持不变。这提供了在路由跳转发生之前进行检查和条件判断的能力。
给next函数传递不同的参数会产生不同的效果,其中:
[插图] 调用next():进行正常的路由跳转。
[插图] 调用next(false):取消当前的导航,URL会重置到from路由对应的地址。
[插图] 调用next(路由地址):重定向到一个不同的地址。
在这段代码中,beforeEach守卫首先检查目标路由(to)是否具有meta.requiresAuth属性,该属性用来标识访问该路由是否需要用户认证。如果用户未登录(!isUserLoggedIn()返回true),则通过next({ path: '/login' })将用户重定向到登录页面。如果用户已经登录,或者目标路由不需要认证,则通过调用next()进行正常的路由跳转。
1.5.1.2. 路由独享守卫
路由独享守卫是针对特定路由配置的守卫,只在定义它们的路由上生效,并可以通过beforeEnter钩子函数来实现,示例如下。
const routes = [
{
path: '/secure',
component: SecureComponent,
beforeEnter: (to, from, next) => {
if (!isUserAuthorized()) {
next('/login');
} else {
next();
}
}
}
];
上述示例中,在访问'/secure'路由之前,beforeEnter守卫会被调用,可以用于执行权限验证等操作。
1.5.1.3. 组件内守卫
组件内守卫直接在Vue组件内部定义,用于处理与组件本身相关的路由生命周期事件。这些守卫适用于处理组件创建、更新或销毁时的逻辑,可以通过onBeforeRouteEnter、onBeforeRouteUpdate和onBeforeRouteLeave这些组合式API钩子来定义这些守卫。
以下是使用组件内守卫的示例。
(1)onBeforeRouteEnter:在路由导航被确认之前,且在组件实例被创建之前调用。
<script setup>
import { onBeforeRouteEnter } from 'vue-router';
onBeforeRouteEnter((to, from, next) => {
if (!isUserAuthorized()) {
next('/login');
} else {
next();
}
});
</script>
(2)onBeforeRouteUpdate:在当前路由改变,但是该组件被复用时调用。
<script setup>
import { onBeforeRouteUpdate } from 'vue-router';
onBeforeRouteUpdate((to, from, next) => {
// 可以在这里处理路由参数或查询的改变
next();
});
</script>
(3)onBeforeRouteLeave:在导航离开该组件的对应路由时调用。
<script setup>
import { onBeforeRouteLeave } from 'vue-router';
onBeforeRouteLeave((to, from, next) => {
const answer = window.confirm('确定要离开吗?');
if (!answer) {
next(false);
} else {
next();
}
});
</script>
这些守卫允许在组件级别处理路由变化,非常适合管理组件内部状态或在用户离开页面前确认保存更改等场景。