一、基本使用
1.路由模式
在 Vue 中,有两种主要的路由模式:哈希模式(hash)和历史模式(history)。
1.哈希模式(hash)
1.工作原理
- 在哈希模式下,URL 中的哈希(#)部分会被浏览器用来模拟页面的不同状态,而不会向服务器发送请求。
- 例如,当访问
http://example.com/#/home
时,浏览器实际上是在加载http://example.com/
,但 Vue Router 会根据哈希值#/home
来确定要显示的组件。 - 当用户在页面上导航时,哈希值会发生变化,但浏览器不会重新加载整个页面,而是由 Vue Router 根据新的哈希值来切换组件。
2.优缺点:
- 兼容性好:哈希模式在所有浏览器中都能正常工作,包括不支持 HTML5 History API 的旧版本浏览器。
- 无需服务器配置:由于哈希值的变化不会触发服务器请求,所以在使用哈希模式时,不需要对服务器进行特殊配置来处理不同的路由。
- 缺点: URL 不太美观:哈希值会出现在 URL 中,使得 URL 看起来不太干净和美观。
2.历史模式(history)
1.工作原理
- 历史模式利用 HTML5 的 History API 来管理 URL 和页面状态。
- 当使用历史模式时,URL 看起来就像普通的 URL,没有哈希值。例如,
http://example.com/home
。 - 当用户在页面上导航时,Vue Router 会通过 History API 来修改浏览器的历史记录,而不是使用哈希值。这样可以实现更美观的 URL 和更自然的用户体验。
2.优缺点
- URL 美观:没有哈希值,看起来更像传统的网站 URL。
- 更好的用户体验:用户可以像在传统网站上一样使用浏览器的前进、后退按钮,并且可以将页面添加到书签,而不会出现哈希值相关的问题。
- 缺点:服务器配置要求:由于历史模式下的 URL 变化会被浏览器视为真正的 URL 变化,所以需要服务器进行相应的配置,以确保当用户直接访问某个特定的 URL 时,服务器能够正确地返回对应的页面内容。否则,用户在刷新页面或者直接访问某个路由时,可能会出现 404 错误。
2.VueRouter-动态参数路由
在 Vue Router 中,动态参数路由允许你在 URL 中定义参数,这些参数可以在组件中被访问和使用。以下是关于 Vue Router 动态参数路由的详细介绍:
1.定义动态参数路由
在路由配置中,可以使用冒号(:)来定义动态参数。例如:
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/user/:id'
component: UserComponent
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
在上面的例子中,:id
是一个动态参数,它可以匹配任何值。当用户访问 /user/123
时,id
参数的值将是 123
。
2.在组件中访问动态参数
在组件中,可以通过 $route.params
对象来访问动态参数。
<template>
<div>
<h2>User id: {{ $route.params.id }}</h2>
</div>
</template>
<script>
export default {
name: 'UserComponent'
}
</script>
在上面的组件中,$route.params.id
将显示当前路由中的 id
参数的值。
3.动态参数的类型
默认情况下,动态参数被视为字符串。但是,你可以通过在路由配置中添加正则表达式来限制参数的类型。例如:
const routes = [
{
path: '/user/:id(\\d+)',
component: UserComponet
}
]
在上面的例子中,:id
参数只能是一个或多个数字。如果用户访问 /user/abc
,将会出现 404 错误,因为参数不匹配规定的类型。
4.导航到动态参数路由
可以使用 router.push
方法来导航到动态参数路由,并传递参数值。例如:
// 在某一个组件的方法中
this.$router.push({path: `/user/${userId}`})
在上面的例子中,userId
是一个变量,它的值将被作为 id
参数传递给 /user/:id
路由。
5.监听路由参数变化
当路由参数发生变化时,可以使用 watch
来监听 $route.params
的变化。例如:
<template>
<div>
<h2>User ID: {{ $route.params.id }}</h2>
</div>
</template>
<script>
export default {
name: 'UserComponent',
watch: {
'$route.params.id': {
immediate: true,
handler(newId, oldId) {
// 处理参数变化的逻辑
},
},
},
};
</script>
在上面的例子中,当 id
参数发生变化时,handler
函数将被调用。
3.VueRouter-路由的匹配语法
在 Vue Router 中,路由的匹配语法用于定义路由的路径规则,以确定哪些 URL 应该匹配特定的路由。以下是关于 Vue Router 路由匹配语法的详细介绍:
1.基本路径匹配
1.简单路径
路由路径可以是一个简单的字符串,表示一个固定的 URL 路径。例如:
const routes = [
{
path: '/home',
component: HomeComponent,
},
];
当用户访问 /home
时,这个路由将被匹配,并且 HomeComponent
将被渲染。
2.动态参数
使用冒号(:)来定义动态参数。动态参数可以在 URL 中匹配任意值,并在组件中通过 $route.params
访问。例如:
const routes = [
{
path: '/user/:id',
component: UserComponent,
},
];
当用户访问 /user/123
时,id
参数的值将是 123
,可以在 UserComponent
中通过 $route.params.id
访问。
2.可选参数和通配符
1.可选参数:使用括号和问号(?:)来定义可选参数。可选参数在 URL 中可以出现也可以不出现。例如:
const routes = [
{
path: '/profile/:username?',
component: ProfileComponent,
},
];
- 用户可以访问
/profile
或/profile/john
,在ProfileComponent
中可以通过$route.params.username
访问用户名参数,如果没有提供用户名,则该参数为undefined
。
2.通配符:使用星号(*)来定义通配符路由。通配符路由可以匹配任何未被其他路由匹配的 URL。例如:
const routes = [
{
path: '/404',
component: NotFoundComponent,
},
{
path: '*',
component: NotFoundComponent,
},
];
- 如果用户访问的 URL 没有被其他路由匹配,将显示
NotFoundComponent
3.正则表达式匹配
使用正则表达式来限制动态参数的类型或定义更复杂的路径规则。例如:
const routes = [
{
path: '/user/:id(\\d+)',
component: UserComponent,
},
];
在这个例子中,:id
参数只能是一个或多个数字。如果用户访问 /user/abc
,将会出现 404 错误,因为参数不匹配规定的类型。
4.命名路由和路由别名
1.命名路由:可以为路由命名,以便在导航时更容易引用。例如:
const routes = [
{
path: '/home',
name: 'home',
component: HomeComponent,
},
];
可以使用 router.push({ name: 'home' })
来导航到这个路由。
2.路由别名:可以为路由定义别名,以便在不同的 URL 下访问同一个路由。例如:
const routes = [
{
path: '/home',
component: HomeComponent,
alias: '/dashboard',
},
];
- 用户可以访问
/home
或/dashboard
来访问HomeComponent
。
4.VueRouter-嵌套路由
在 Vue Router 中,嵌套路由允许你在一个父路由下定义子路由,从而构建出具有层次结构的页面布局。以下是关于 Vue Router 嵌套路由的详细介绍:
一、定义嵌套路由
- 首先,在父路由的组件中使用
<router-view>
标签来渲染子路由的内容。例如:<template> <div> <h1>Parent Component</h1> <router-view></router-view> </div> </template> <script> export default { name: 'ParentComponent', }; </script>
- 然后,在路由配置中定义父路由和子路由。例如:
import { createRouter, createWebHistory } from 'vue-router'; import ParentComponent from './ParentComponent.vue'; import ChildComponent from './ChildComponent.vue'; const routes = [ { path: '/parent', component: ParentComponent, children: [ { path: 'child', component: ChildComponent, }, ], }, ]; const router = createRouter({ history: createWebHistory(), routes, });
在上面的例子中,当用户访问
/parent/child
时,ParentComponent
将被渲染,并且在ParentComponent
的<router-view>
中会渲染ChildComponent
。
二、嵌套路由的路径
-
子路由的路径是相对于父路由的路径。例如,如果父路由的路径是
/parent
,子路由的路径是child
,那么完整的路径就是/parent/child
。 -
可以在子路由的路径中使用动态参数和其他路由匹配语法,就像在顶级路由中一样。例如:
const routes = [ { path: '/parent', component: ParentComponent, children: [ { path: 'child/:id', component: ChildComponent, }, ], }, ];
在上面的例子中,当用户访问
/parent/child/123
时,id
参数的值将是123
,可以在ChildComponent
中通过$route.params.id
访问。
三、多层嵌套路由
可以在子路由中继续定义孙路由,从而实现多层嵌套。例如
const routes = [
{
path: '/parent',
component: ParentComponent,
children: [
{
path: 'child',
component: ChildComponent,
children: [
{
path: 'grandchild',
component: GrandchildComponent,
},
],
},
],
},
];
在上面的例子中,当用户访问 /parent/child/grandchild
时,ParentComponent
、ChildComponent
和 GrandchildComponent
将依次被渲染,每个组件中的 <router-view-view>
用于渲染下一级的子路由内容。
5.VueRouter-编程式导航
在 Vue Router 中,前端编程式导航允许你通过代码来触发路由的切换,而不是通过传统的链接点击。以下是关于 Vue Router 前端编程式导航的详细介绍:
一、使用router.push
进行导航
router.push
方法接受一个路由地址或一个描述路由的对象作为参数,并将导航到对应的路由。例如:// 导航到一个字符串路径 this.$router.push('/home'); // 导航到一个命名路由 this.$router.push({ name: 'user', params: { id: 123 } }); // 带查询参数,结果是 /register?plan=private this.$router.push({ path: '/register', query: { plan: 'private' } });
- 如果导航失败(例如,目标路由不存在),会抛出一个错误。你可以通过捕获这个错误来处理导航失败的情况。例如:
try { this.$router.push('/non-existent-route'); } catch (error) { console.log('Navigation failed:', error); }
二、使用router.replace
进行替换导航
router.replace
方法与router.push
类似,但它不会在历史记录中添加新的记录,而是替换当前的历史记录项。例如:this.$router.replace('/home');
- 这在某些情况下很有用,例如,当你希望在不影响用户后退按钮行为的情况下导航到另一个页面。
三、使用router.go
进行历史导航
router.go
方法接受一个整数作为参数,表示在历史记录中前进或后退的步数。例如:// 向前一步,类似于浏览器的前进按钮 this.$router.go(1); // 向后一步,类似于浏览器的后退按钮 this.$router.go(-1);
四、在组件中使用编程式导航
- 在 Vue 组件中,可以通过
this.$router
访问路由实例,并使用上述方法进行编程式导航。例如:<template> <div> <button @click="goToHome">Go to Home</button> </div> </template> <script> export default { methods: { goToHome() { this.$router.push('/home'); }, }, }; </script>
- 还可以在生命周期钩子、方法或计算属性中使用编程式导航,根据应用的逻辑来触发路由的切换。
- 前端编程式导航提供了更大的灵活性,可以根据应用的具体需求动态地控制路由的切换,实现更加复杂的用户交互和页面导航逻辑。
6.VueRouter-命名路由
在 Vue Router 中,命名路由可以为路由赋予一个名称,通过这个名称可以更方便地进行路由导航,而不需要记住具体的路径字符串。以下是关于 Vue Router 命名路由的详细介绍:
一、定义命名路由
- 在路由配置中,可以使用
name
属性为路由指定一个名称。例如:import { createRouter, createWebHistory } from 'vue-router'; import HomeComponent from './HomeComponent.vue'; import AboutComponent from './AboutComponent.vue'; const routes = [ { path: '/', name: 'home', component: HomeComponent, }, { path: '/about', name: 'about', component: AboutComponent, }, ]; const router = createRouter({ history: createWebHistory(), routes, });
在上面的例子中,为两个路由分别赋予了名称 home
和 about
。
二、使用命名路由进行导航
- 可以使用
router.push
、router.replace
等方法,并传入一个包含name
属性的对象来进行命名路由的导航。例如:// 使用命名路由进行导航 this.$router.push({ name: 'about' });
- 也可以传递参数给命名路由。例如:
// 带有参数的命名路由导航 this.$router.push({ name: 'user', params: { id: 123 } });
在目标路由的组件中,可以通过
$route.params
获取传递的参数。
三、命名路由的优势
-
提高可读性:使用命名路由可以使代码更具可读性,特别是在复杂的应用中,通过名称可以清楚地知道要导航到哪个页面,而不需要记住具体的路径字符串。
-
易于维护:如果路由的路径发生变化,只需要在路由配置中修改路径,而不需要在所有使用该路由的地方进行修改。使用命名路由可以保持代码的稳定性。
-
动态生成路由链接:在某些情况下,可能需要动态生成路由链接。使用命名路由可以更容易地实现这一点,而不需要手动拼接路径字符串。例如:
<template> <div> <router-link :to="{ name: 'user', params: { id: userId } }">User Profile</router-link> </div> </template> <script> export default { data() { return { userId: 123, }; }, }; </script>
在上面的例子中,根据
userId
动态生成了一个指向用户 profile 页面的路由链接。
7.VueRouter-重定向和别名
在 Vue Router 中,重定向(redirect)和别名(alias)是两个非常有用的功能,可以用来实现灵活的路由配置和用户体验优化。
一、重定向(redirect)
-
定义重定向:
- 在路由配置中,可以使用
redirect
属性来定义重定向。例如:const routes = [ { path: '/old-path', redirect: '/new-path', }, { path: '/new-path', component: NewComponent, }, ];
- 在上面的例子中,当用户访问
/old-path
时,会被自动重定向到/new-path
,并渲染NewComponent
。
-
重定向到命名路由:
-
可以将重定向目标设置为命名路由。例如:
const routes = [ { path: '/old-path', redirect: { name: 'newRoute' }, }, { path: '/new-path', name: 'newRoute', component: NewComponent, }, ];
-
-
函数式重定向:
-
redirect
属性也可以接受一个函数,该函数接收当前路由对象作为参数,并返回重定向的目标。例如:const routes = [ { path: '/login', redirect: to => { // 如果用户已登录,重定向到首页 if (isLoggedIn()) { return '/home'; } // 否则,保持在登录页面 return '/login'; }, }, ];
-
二、别名(alias)
1.定义别名:
- 别名允许一个路由匹配多个 URL。在路由配置中,可以使用
alias
属性来定义别名。例如:const routes = [ { path: '/home', component: HomeComponent, alias: '/dashboard', }, ];
- 在上面的例子中,用户访问
/home
或/dashboard
都会渲染HomeComponent
。
2.多个别名
- 可以为一个路由设置多个别名。例如:
const routes = [ { path: '/home', component: HomeComponent, alias: ['/dashboard', '/start'], }, ];
3.别名与重定向的区别
- 重定向会实际改变浏览器的 URL,而别名只是让一个路由匹配多个 URL,但浏览器地址栏中的 URL 不会改变。
- 重定向和别名在 Vue Router 中可以帮助你实现灵活的路由配置,提高用户体验,并且可以根据应用的需求进行动态的路由调整。
8.手写VueRouter
class VueRouter {
constructor (options) {
this.routes = options.routes
this.mode = options.mode || 'hash'
this.history = []
this.currentRoute = null
// 根据模式初始化路由监听
if (this.mode === 'hash') {
window.addEventListener('hashchange', this.onHashChange.bind(this))
} else if (this.mode === 'history') {
window.addEventListener('popstate', this.onHistoryChange.bind(this))
}
this.init()
}
init () {
if (this.mode === 'hash') {
const hash = window.location.hash.slice(1) || '/'
this.currentRoute = this.matchRoute(hash)
} else if (this.mode === 'history') {
const path = window.location.pathname
this.currentRoute = this.matchRoute(path)
}
}
matchRoute (path) {
for (const route of this.routes) {
if (route.path === path || route.path === '*') {
return route
}
}
return null
}
onHashChange () {
const hash = window.location.hash.slice(1)
this.currentRoute = this.matchRoute(hash)
this.emit('routeChange', this.currentRoute)
}
onHistoryChange () {
const path = window.location.pathname
this.currentRoute = this.matchRoute(path)
this.emit('routeChange', this.currentRoute)
}
push(path) {
if (this.mode === 'hash') {
window.location.hash = `#${hash}`
} else if (this.mode === 'history') {
window.history.pushState(null,null,path)
this.currentRoute = this.matchRoute(path)
this.emit('routeChange', this.currentRoute)
}
}
emit (event, ...args) {
if (this.listeners && this.listeners[event]) {
this.listeners[event].forEach(listener => listener(...args));
}
}
on (event, listener) {
if (!this.listeners) {
this.listeners = {}
}
if (!this.listeners[event]) {
this.listeners[event] = []
}
this.listeners[event].push(listener)
}
}
使用示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="app"></div>
<script>
// 定义两个组件
const HomeComponent = { template: '<div>Home</div>' };
const AboutComponent = { template: '<div>About</div>' };
// 使用 VueRouter
const router = new VueRouter({
mode: 'hash',
routes: [
{ path: '/', component: HomeComponent },
{ path: '/about', component: AboutComponent },
],
});
// 创建 Vue 实例并挂载
const app = {
template: `
<div>
<nav>
<ul>
<li><a href="#/" @click.prevent="goHome">Home</a></li>
<li><a href="#/about" @click.prevent="goAbout">About</a></li>
</ul>
</nav>
<router-view></router-view>
</div>
`,
methods: {
goHome() {
router.push('/');
},
goAbout() {
router.push('/about');
},
},
};
const vm = new Vue(app).$mount('#app');
router.on('routeChange', route => {
console.log('Route changed to:', route);
});
</script>
</body>
</html>
这个手写的 Vue Router 实现了基本的路由功能,包括根据不同模式监听 URL 变化、匹配路由、导航等。但这只是一个简单的示例,实际的 Vue Router 有更多的功能和优化。
二、进阶使用
1.导航守卫在
Vue Router 中,导航守卫是一种用于控制路由导航的机制。它允许你在路由切换的不同阶段执行特定的逻辑,例如验证用户身份、权限控制等。以下是关于 Vue Router 导航守卫的详细介绍:
1、全局前置守卫(beforeEach)
定义全局前置守卫:
- 使用
router.beforeEach
方法来定义一个全局前置守卫。这个守卫会在每次路由切换之前被调用。例如:const router = new VueRouter({ //...路由配置 }); router.beforeEach((to, from, next) => { // 在这里执行逻辑 // to 是目标路由对象 // from 是源路由对象 // next 是一个函数,用于控制导航流程 if (to.meta.requiresAuth &&!isLoggedIn()) { // 如果目标路由需要认证且用户未登录,重定向到登录页面 next('/login'); } else { // 否则继续导航 next(); } });
守卫函数参数:
to
:目标路由对象,包含目标路由的信息,如路径、参数、查询参数等。from
:源路由对象,包含源路由的信息。next
:一个函数,用于控制导航流程。必须调用next()
才能继续导航。如果不调用next()
,导航将被阻止。
2、全局后置守卫(afterEach)
定义全局后置守卫:
- 使用
router.afterEach
方法来定义一个全局后置守卫。这个守卫会在每次路由切换之后被调用。例如:router.afterEach((to, from) => { // 在这里执行逻辑,例如记录页面访问日志 console.log(`从 ${from.path} 导航到 ${to.path}`); });
守卫函数参数:
to
:目标路由对象。from
:源路由对象。
3、路由独享守卫(beforeEnter)
定义路由独享守卫:
- 在路由配置中,可以使用
beforeEnter
属性来定义路由独享守卫。例如:const routes = [ { path: '/admin', component: AdminComponent, meta: { requiresAuth: true }, beforeEnter: (to, from, next) => { // 在这里执行逻辑,例如检查用户是否具有管理员权限 if (isAdmin()) { next(); } else { next('/'); } }, }, ];
- 守卫函数参数与全局前置守卫相同。
4、组件内守卫
beforeRouteEnter
:
- 在组件内,可以使用
beforeRouteEnter
导航守卫在路由进入该组件之前被调用。例如:<template> <div>Protected Component</div> </template> <script> export default { beforeRouteEnter(to, from, next) { // 在这里执行逻辑,例如检查用户是否有权限访问该组件 if (hasPermission()) { next(); } else { next('/'); } }, }; </script>
beforeRouteUpdate
:
- 当路由参数变化时,这个守卫会被调用。例如:
<template> <div>User Profile</div> </template> <script> export default { name: 'UserProfile', data() { return { user: null, }; }, beforeRouteUpdate(to, from, next) { // 当路由参数变化时,重新获取用户数据 fetchUser(to.params.id).then(user => { this.user = user; next(); }); }, }; </script>
beforeRouteLeave:
- 在离开当前路由之前被调用。例如:
<template> <div>Edit Form</div> </template> <script> export default { data() { return { formData: {}, }; }, beforeRouteLeave(to, from, next) { // 在这里可以提示用户保存未保存的更改 if (hasUnsavedChanges()) { const confirmLeave = window.confirm('你有未保存的更改,确定要离开吗?'); if (confirmLeave) { next(); } else { next(false); } } else { next(); } }, }; </script>
导航守卫在 Vue Router 中提供了强大的控制能力,可以根据不同的应用需求实现各种路由导航的控制逻辑。