嵌套路由
一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL 的片段通常对应于特定的嵌套组件结构,例如:
TypeScript
import {createRouter,createWebHistory,RouteRecordRaw} from "vue-router";
const routes:Array<RouteRecordRaw> = [
{
path: "/foo",
name: "footer",
component: () => import("../view/footer.vue"),
children: [
{
path: "",
component: () => import("../components/A.vue")
},
{
path: "user",
component: () => import("../components/B.vue")
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
使用:
TypeScript
<script setup lang="ts">
import {useRouter} from "vue-router";
const router = useRouter()
</script>
<template>
<router-view></router-view>
<button @click="router.push('/foo')">去A组件</button>
<button @click="router.push('/foo/user')">去B组件</button>
<div>我是footer组件</div>
</template>
<style scoped>
</style>
命名视图
命名视图可以在同一级(同一个组件)中展示更多的路由视图,而不是嵌套显示。 命名视图可以让一个组件中具有多个路由渲染出口,这对于一些特定的布局组件非常有用。 命名视图的概念非常类似于"具名插槽",并且视图的默认名称也是 default。
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s)
TypeScript
import {createRouter,createWebHistory,RouteRecordRaw} from "vue-router";
const routes:Array<RouteRecordRaw> = [
{
path: "/",
name: "footer",
components: {
default: () => import("../view/footer.vue"),
showB: () => import('../components/B.vue'),
showA: () => import("../components/A.vue")
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
TypeScript
<script setup lang='ts'>
</script>
<template>
<router-view></router-view>
<router-view name="showB"></router-view>
<router-view name="showA"></router-view>
</template>
<style scoped>
</style>
重定向-别名
1. 重定向 redirect
- 字符串形式配置,访问/ 重定向到 /user (地址栏显示/,内容为/user路由的内容)
TypeScript
const routes:Array<RouteRecordRaw> = [
{
path: "/",
name: "footer",
component: () => import("../view/footer.vue"),
redirect: "/user1",
children: [
{
path: '/user1',
component: () => import("../components/A.vue")
}
]
}
]
2.对象形式配置
redirect: {path: "/user1"},
3.函数模式(可以传参)
redirect(to) {
return {
path: "/user1",
query: {
name: 'jjs'
}
}
}
2. 别名-alias
当路径为/root时,就会访问/的内容
TypeScript
const routes:Array<RouteRecordRaw> = [
{
path: "/",
name: "footer",
component: () => import("../view/footer.vue"),
alias: ['/root'],
children: [
{
path: '',
component: () => import("../components/A.vue")
}
]
}
]
导航守卫
1. 全局前置导航守卫
router.beforeEach
TypeScript
router.beforeEach((to, form, next) => {
console.log(to, form);
next()
})
每个导航方法接受三个参数
TypeScript
to: Route, 即将要进入的目标 路由对象;
from: Route,当前导航正要离开的路由;
next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
案例:
登录校验,如果登录了就可到index页面,没有登录那么就先登录
TypeScript
<script setup lang="ts">
import { ref,reactive } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { useRouter } from 'vue-router'
const router = useRouter()
const ruleFormRef = ref<FormInstance>()
type User = {
userName: string,
password: string
}
const ruleForm = reactive<User>({
userName: '',
password: ''
})
const rules = reactive<FormRules>({
userName: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
]
})
function submitForm(FromEl:FormInstance | undefined) {
if(!FromEl) return
FromEl.validate((valid) => {
if (valid) {
localStorage.setItem("token", "1234567890")
router.push('/home')
} else {
console.log('error submit!')
}
})
}
</script>
<template>
<div>
<el-form
ref="ruleFormRef"
style="max-width: 600px"
:model="ruleForm"
status-icon
:rules="rules"
label-width="auto"
class="demo-ruleForm"
>
<el-form-item label="userName" prop="userName">
<el-input v-model="ruleForm.userName" type="text" autocomplete="off" />
</el-form-item>
<el-form-item label="password" prop="password">
<el-input v-model="ruleForm.password" type="password" autocomplete="off" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm(ruleFormRef)">Submit</el-button>
</el-form-item>
</el-form>
</div>
</template>
<style scoped>
</style>
2. 全局后置守卫
使用场景一般可以用来做loadingBar
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next
函数也不会改变导航本身:
案例
loadingBar
TypeScript
<script setup lang="ts">
import { ref } from 'vue'
const speed = ref(0)
const bar = ref<HTMLDivElement>()
let timer = ref(0)
function startLoading() {
let dom:HTMLElement = bar.value as HTMLElement
timer.value = window.requestAnimationFrame(function fn() {
speed.value += 1
dom.style.width = speed.value + '%'
if (speed.value >= 90) {
speed.value = 0
window.cancelAnimationFrame(timer.value)
} else {
timer.value = window.requestAnimationFrame(fn)
}
})
}
function endLoading() {
speed.value = 100
let dom = bar.value as HTMLElement
dom.style.width = speed.value + '%'
timer.value = setTimeout(() => {
dom.style.height = 0 + 'px'
clearTimeout(timer.value)
},500)
}
defineExpose({
startLoading,
endLoading
})
</script>
<template>
<div class="loading-bar">
<div ref="bar" class="bar"></div>
</div>
</template>
<style scoped>
.loading-bar {
position: fixed;
top: 0;
width: 100%;
height: 2px;
}
.loading-bar .bar {
height: inherit;
width: 0;
background-color: skyblue;
}
</style>
bar.ts
TypeScript
import LoadingBar from "./LoadingBar.vue";
import { createVNode,render } from "vue";
const Vnode = createVNode(LoadingBar);
render(Vnode,document.body);
export default Vnode.component?.exposed;
router
TypeScript
router.beforeEach((to, _, next) => {
// 路由拦截
if (to.path === "/" || localStorage.getItem("token")) {
next();
exposed?.startLoading()
} else {
next("/");
}
})
router.afterEach(() => {
exposed?.endLoading()
})