定义
客户端路由的作用是在单页面应用中将浏览器的url和用户看的内容关联起来,当切换页面时,url会同步修改,但不用从服务器重新加载。 vueRouter基于vue组件建立,可以通过配置路由,告诉vueRouter不同的url展示的组件。
创建路由器实例
- createRouter()
import {createRouter, createWebHistory} from 'vue-router'
const routes = [ {path: '/', component: Home}, {path: '/about', component: About}]
const router = createRouter({
history: createWebHistory()
routes,
})
// 创建路由实例后,需要当作插件注册到app中
create(App).use(router).mount('#app);
- use()需要在mount之前使用。use的作用:
- 全局注册RouterView, RouterLink
- 全局添加route
- 启用useRouter, useRoute函数
- 触发路由器解析初始路由
- route: 当前路由
- router: 路由实例,包含所有路由。
动态路由
- 动态参数使用
:表示, 需要使用watch监听动态参数的变化,因为页面会被复用,复用时不会再次调用onMounted方法。
const routes = [{
path: '/user/:userId',
component: UserComponents
}]
- 在路径中使用正则
// 传数字优先匹配第一个路由,顺序不重要。
const route = [
{ path: '/:userId(\\d+)', }, // userId为数字
{ path: '/:userName',} // userName为其他
]
- 重复的参数,参数名后加
+表示一个或多个,加*表示0个或多个,这样的参数获取和传递时为数组
const route = [
{path: '/:name+'}, // 匹配/one/two/three
{path: '/:name*'}, // 匹配/,/one,/one/two
{path: '/:name(\\d+)+'} // 与正则合用
]
sensitive和strict- /user 可以匹配/user/,/User路由
- 设置sensitive为true,大小写敏感,则不能匹配/User
- 设置strict为true,严格模式,则不能匹配/user/
const route = [
{path: '/user', sensitive: true, strict: true}
]
?表示可选参数
const route = [
{ path: '/user/:id?' }
]
路由的嵌套使用
- 可以在组件中嵌套,该组件所在路由的子路由组件将展示在这里。
const routes = [{
path: '/user/:id',
children: [
{path: 'profile', component: ProfileComponent},
{path: 'post', component: PostComponent}
],
},
]
- 以
/开始表示根路径,因此子组件的path不需要添加/,url不必嵌套 - 如果想访问/user/1路由,则需要设置一个path为空的子路由
const routes = [{
path: '/user/:id',
children: [
{path: '', component: UserComponent}
]
}]
命名路由
- 可以给路由设置name属性,跳转时直接根据name跳转,不依据url
const routes = [{ path: '/user/:id', name: 'user', comoponent: User, children: [{name: 'profile', path: 'profile', component: Profile}]
}]
<router-link :to={name: "user", params: {id: "lily"}></router-link>
编程式路由
- 使用
router.push(),参数可以是字符串,或者对象 - 使用对象时,path和params不能同时出现
router.push('/user/lily'); // url
router.push({
name: 'user',
params: {id: 'lily'}
});
router.push({
path: '/user/lily',
query: {age: 12}
})
router.push({
path: '/user',
params: {id: 'lily'}
}) // 此时会跳转到/user
router.replace(),替换当前路由,不会在history中添加新的记录。- 用法同push,也可在push中增加一个参数:
replace: true
router.replace('/user/lily');
router.push({name: 'user', params: {id: 'lily'}, replace: true})
横跨历史
router.go,前进多少,后退多少
router.go(1); // 向前移动一条记录,同router.forward()
router.go(-1); // 向后移动一条记录,同router.back()
router.go(100); // 静默失败
router.go(-100); // 静默失败
命名视图
- 一个组件中有多个router-view时,对视图进行命名
<router-view class="view left-sidebar" name="LeftSidebar" />
<router-view class="view main-content" />
<router-view class="view right-sidebar" name="RightSidebar" />
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
components: {
default: Home,
// LeftSidebar: LeftSidebar 的缩写
LeftSidebar,
// 它们与 `<router-view>` 上的 `name` 属性匹配
RightSidebar,
},
},
],
})
- 嵌套的命名视图
/settings/emails /settings/profile
+-----------------------------------+ +------------------------------+
| UserSettings | | UserSettings |
| +-----+-------------------------+ | | +-----+--------------------+ |
| | Nav | UserEmailsSubscriptions | | +------------> | | Nav | UserProfile | |
| | +-------------------------+ | | | +--------------------+ |
| | | | | | | | UserProfilePreview | |
| +-----+-------------------------+ | | +-----+--------------------+ |
+-----------------------------------+ +------------------------------+
// userSetting.vue
<div>
<nav></nav>
<router-view></router-view>
<router-view name="helper"></router-view>
</div>
// router
const routes = [{
path: '/settings',
component: UserSettings
children: [
{path: 'emails', component: UserEmailsSubscriptions},
{path: 'profile', component: {default: UserProfile, helper: UserProfilePreview }},
]
}]
重定向
redirect重定向到其他页面
const routes = [
{path: '/home', redirect: '/'}, // 使用url
{path: '/home', redirect: {name: 'homePage'}}, // 使用name
// 使用方法 /search/name => /search?q=name
{path: '/search/name', redirect: to => { return {path: '/search', query: {q: to.params.name}}}}
]
- 重定向不会触发/home的路由守卫
别名
- alias,可以通过设置别名,让两个地址指向同一个页面
const routes = [
{path: '/', component: Home, alias: '/home'},
{path: '/user/:id', component: UserLayout children: [
// /user/12
// /user/12/profile
// /12 3个url指向同一个页面
{path: 'profile', component: Profile, alias: ['', '/:id'] }
]}
]
将路由参数转为props
- 可以设置
props参数将路由参数转为组件的props
const routes = [
{path: '/user/:id', component: User, props: true}, // 直接将id作为props传递给组件,组件需在props中定义id。
{path: '/user/:id', component: {default: User, sidebar: Sidebar}, props: {default: true, sidebar: true}}, // 多个视图的需要分别给每个组件设置。
{path: '/user', component: User, props: {name: 'lily'}}, // 将props原样传给组件的props
{path: '/user', component: User, props: to => {return {q: to.query.name}}}, // 将{q: ''}传给props
]
路由守卫
- 全局前置守卫router.beforeEach((to, from) => {return ...})
- 返回
false,取消导航 - 返回
undefined,true则导航有效,并调用下一个守卫。 - 返回
路由地址,相当于调用router.push(),路由格式同router.push - 如果有意外,会抛出error并取消导航。
- 返回
- 全局解析守卫router.beforeResolve((to, from) => {}),在导航被确认之前,所有组件内守卫和异步路由组件被解析之后调用。
- 在此处是获取数据或执行其他操作的理想位置(如用户无法进入页面时需要取消的操作)。
- 全局后置守卫router.afterEach((to, from) => {}),该守卫无法改变导航,用于分析,更改页面标题,声明页面等辅助功能。
- 在守卫内的全局注入
const router = createRouter(...);
router.privide('info', 'hhhh');
router.beforeEach(to => {
const info = inject('info')
})
- 路由独享的守卫
const routes = [{
path: '/user',
component: User,
beforeEach: (to, from) => {...}
}]
此时的守卫只会在进入路由时触发,params,query,hash改变的时候都不会触发。
- 组件内守卫
- 可用的组件内守卫
- onBeforeRouteEnter:不能调用this,此时this未被创建。可以在next中访问实例。
<script setup> beforeRouteEnter(to, from, next) { next(vm => { // 通过vm访问实例 }) } </script>- onBeforeRouteUpdate:组件复用时调用,例如:/user/1 => /user/2。没有next参数,this可以直接调用。
- onBeforeRouteLeave:导航离开组件时调用,没有next参数。return false;取消离开。
- 可用的组件内守卫
- 导航守卫流程
- 导航被触发。
- 在失活的组件里调用
onRouteLeave - 调用全局的
beforeEach - 在重用的组件里调用
onRouteUpdate - 在路由配置里调用
beforeEnter - 解析异步路由
- 在被激活的组件中调用
onBeforeEnter - 调用全局的
beforeResolve - 导航被确认
- 调用全局的
afterEach - 触发dom更新
- 调用onBeforeEnter中传给next的回调。
- 路由元信息
meta,通过meta对象可以添加任何你需要的数据。在导航守卫和路由地址中都可以获取。
const routes = [{
path: '/user',
component: User,
meta: {isAuth: true, name: "用户信息"}
}]
router-view 插槽
- 给页面切换增加动画效果(transition)或保存状态(keep-alive)
<router-view v-slot={Component, route}>
<transition :name="route.meta.transition || 'fade'">
<component :is="Component">
</transition>
</router-view>
<router-view v-slot={Component}>
<keep-alive>
<component :is="Component">
</keep-alive>
</router-view>
滚动行为
- 当切换到一个浏览过的页面时,页面自动滚动到上一次的位置,只有在执行
history.pushState时才调用(感觉使用场景不多)。 - 注意: 这个功能只在支持 history.pushState 的浏览器中可用。
const router = createRouter({
history: createWebHashHistory(),
routes: [...],
scrollBehavior: (to, from, savedPosition) => {
// return 期望滚到的位置
if (to.hash) {
return {
el: to.hash,
behavior: 'smooth'
}
}
}
})
路由懒加载(动态导入组件)
const User = () => import('...');
const routes = [
{path: '/user', component: User},
{path: '/user', component: () => import('...')}
]
- 如果使用
babel,需要添加插件 @babel/plugin-syntax-dynamic-import,此插件已包含在@babel/preset-env7.8.0及以上版本
类型化路由
- 使用[unplugin-vue-router]插件,根据约定好的目录结构和命名规则,将指定目录下的vue文件自动生成路由配置,不用手动编写。
其他
- [vite-plugin-vue-layouts - npm] 该插件通常与vue-router一起使用
- 一个常见的需求是,同模块的若干页面需要使用同一种布局,比如俱乐部相关的页面的顶部需要展示俱乐部的名称,其他页面顶部需要展示网站名称。 通常实现的方法是,将俱乐部的名称和网站名称定义成公共组件,在每个页面都书写置顶的布局,并引入响应的公共组件。 显然这是非常低效的,通过全局布局 vite-plugin-vue-layouts 可以帮你解决这个麻烦!