Vue Router
Vue Router4 匹配 Vue3;Vue Router3 匹配 Vue2。
Vue Router 是 Vue.js 官方的路由管理器。Vue Router 基于路由和组件的映射关系,监听页面路径的变化,渲染对应的组件。
安装:
npm install vue-router
。
基本使用:
Vue Router4 的基本使用:
// src/router/index.js
import {createRouter, createWebHashHistory} from 'vue-router'import Home from '../components/Home.vue'
import About from '../components/About.vue'// 1. 创建路由对象
const router = createRouter({// 配置路由映射关系,一个路径对应一个组件routes: [{path: '/', redirect: '/home'}, // 如果路径是 /,重定向到 /home {path: '/home', component: Home},{path: '/about', component: About}],// 配置采用的模式history: createWebHashHistory(),
})export default router
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'const app = createApp(App)
// 2. 注册路由对象
app.use(router)
app.mount('#app')
// 可以使用 Vue Router 提供的 <router-link> 组件实现路径跳转
// src/App.vue。
<template><!-- 3. 使用 Vue Router 提供的 <router-link> 组件实现路径跳转 --><router-link to="/home">首页</router-link><router-link to="/about">关于</router-link><!-- 4. 路径匹配到的组件将会显示在 <router-view> 这个占位组件处 --><router-view></router-view>
</template><script setup>
</script><style scoped>
</style> // 也可以通过代码逻辑实现路径跳转。
// src/App.vue
<template><div><span @click="handleHomeNav">首页</span><span @click="handleAboutNav">关于</span></div><!-- 4. 路径匹配到的组件将会显示在 <router-view> 这个占位组件处 --><router-view></router-view>
</template><script setup>
// 3. 通过代码逻辑实现路径跳转
import { useRouter } from 'vue-router'
const router = useRouter()
const handleHomeNav = () => {router.push('/home')
}
const handleAboutNav = () => {router.push({path: '/about'})
}
</script><style scoped>
</style>
Vue Router3 的基本使用:
import Home form '../componnets/Home .vue'
import About form '../componnets/About .vue'// 1. 创建 router 实例
const router = new VueRouter({// 传入 routes 配置定义路由routes: [{ path: 'home', component: Home },{ path: '/about', component: About }]
})
// 2. 通过 router 配置参数注入路由
const app = new Vue({router
}).$mount('#app')
<div id="app"><p><!-- 3. 使用 router-link 组件来导航,通过传入 to 属性指定链接 --><router-link to="/home">Home</router-link><router-link to="/about">About </router-link></p><!-- 4. router-view 是路由出口,路由匹配到的组件将渲染在这里 --><router-view></router-view>
</div>
路由对象 router:
路由对象 router 的属性:
- history:配置路由采用的模式。属性值有两个,createWebHashHistory 是 hash 模式,createWebHistory 是 history 模式。
- routes:配置路由映射关系。
路由对象 router 的方法:
addRoute(parentName, route)
:动态添加路由。其中的 parentName 就是配置路由映射关系 route 中独一无二的 name 属性,parentName 是可选的,可以通过 parentName 指定父级路由的名称来添加嵌套的子路由。removeRoute(name)
:删除路由。其中的 name 就是配置路由映射关系 route 中独一无二的 name 属性。hasRoute()
:检查某个路由是否存在。getRoutes()
:获取所有路由记录。返回值是一个数组。push()
:跳转到指定路径。replace()
:替换掉当前路径。forward()
:路径前进一步。back()
:路径后退一步。go()
:路径前进或者后退指定步数。
路由 route:
路由 route 的配置属性:
- path:路径。
- component:路径匹配时要渲染的组件。
- redirect:路径匹配时要重定向的路径。
- name:独一无二的路由名称。
- children:嵌套路由。
<router-link>
组件:
<router-link>
:用于创建导航链接。<router-link>
的属性有:
- to:用于指定要跳转的路径。属性值是一个字符串或者对象。
<router-link to="/home">首页</router-link> <router-link to="{path: '/home'}">首页</router-link>
- replace:设置 replace 属性的化,路径跳转时将会直接替换掉旧路径,旧路径不会进入历史列表,回退页面的话无法回退到旧页面。
active-class
:设置激活 a 元素后应用的 class 属性名称。默认是router-link-active
。exact-active-class
:链接精准激活时,应用于 a 元素的 class 属性名称。默认是router-link-exact-active
。
<router-view>
组件:
<router-view>
:占位组件。路径匹配时对应组件的渲染容器。
嵌套路由:
通过 children 配置嵌套路由。
// src/router/index.js
import {createRouter, createWebHashHistory} from 'vue-router'import User from '../components/User.vue'
import UserProfile from '../components/UserProfile.vue'
import UserPosts from '@/components/UserPosts.vue'const router = createRouter({routes: [{path: '/user/:id', // 以 / 开头的嵌套路径将被视为根路径component: User,// 1. 通过 children 配置嵌套路由children: [{// 当路径匹配到 /user/:id/profile,就会渲染 UserProfile 组件到 User 组件的 <router-view> 内部path: 'profile', // // 在生成路由时,主路由上的 path 会被自动添加到子路由之前,所以子路由上的 path 不用重新声明主路由上的 path 了component: UserProfile,},{// 当路径匹配到 /user/:id/posts,就会渲染 UserPosts 组件到 User 组件的 <router-view> 内部path: 'posts',component: UserPosts,},]},],history: createWebHashHistory(),
})export default router
// src/App.vue
<template><!-- 2. 顶层的 <router-view> 渲染顶层路由匹配的组件。User 组件将会被渲染到这个位置 --><router-view></router-view>
</template><script setup>
</script><style scoped>
</style>
// src/components/User.vue
<template><div>User:{{ $route.params.id }}</div><!-- 3. 一个被渲染的组件也可以包含自己嵌套的 <router-view>。UserProfile 和 UserPosts 组件将会被渲染到这个位置 --><router-view></router-view>
</template><script setup>
</script><style scoped>
</style>
动态路由:
路径是动态的,路径参数的部分在进行路由匹配时可以变化,使用冒号标记;当匹配到一个路由时,参数值会被设置到 $route.params
中。
// src/router/index.js
import {createRouter, createWebHashHistory} from 'vue-router'import User from '@/components/User.vue'const router = createRouter({routes: [// 1. 通过 :名称 配置动态路由。路径是动态的,路径参数的部分在进行路由匹配时是可以变化的{path: '/user/:id', component: User}],history: createWebHashHistory(),
})export default router
// src/App.vue
<template><!-- 2. 无论是 user/123 还是 user/456,都可以匹配得上 --><router-link to="/user/123">用户123</router-link><router-link to="/user/456">用户456</router-link><router-view></router-view>
</template><script setup>
</script><style scoped>
</style>
//src/components/User.vue
<template><!-- 3. 在 template 模板中获取动态路由的值 --><div>User:{{ $route.params.id }}</div>
</template><script setup>
// 3. 在 Options API 中获取动态路由的值
// this.$route.params.id// 3. 在 Composition API 中获取动态路由的值。通过 useRoute() Hook 函数获取
import { useRoute } from 'vue-router'
const route = useRoute()
console.log(route.params.id)
</script><style scoped>
</style>
可以在一个路由中设置多段路径参数,对应的值都会设置到 $route.params
中。
/user/:username/post/:post_id
通过动态路由实现 NotFound:
对于没有匹配到的路由,通常会匹配到某个固定的页面,例如 NotFound 页面。可以编写一个动态路由用于匹配所有的页面。
// // src/router/index.js
import {createRouter, createWebHashHistory} from 'vue-router'import NotFound from '@/components/NotFound .vue'const router = createRouter({routes: [// 1. 如果匹配到任何一个不存在的路径,那么就匹配 NotFound 组件。{path:'/:pathMatch(.*)', component: NotFound }],history: createWebHashHistory(),
})export default router
//src/components/NotFound .vue
<template><!-- 2. 获取当前的路径参数 --><div>NotFound:{{ $route.params.pathMatch }}</div>
</template><script setup>
</script><style scoped>
</style>
如果配置路由时,在 /:pathMatch(.*)
后面再加一个 *
,变成 {path:'/:pathMatch(.*)*', component: NotFound }
,那么在获取路径参数时,会以 /
为分隔符将路径参数解析为数组。
路由传参:
- 可以通过动态路由的方式传递简单参数;在组件中通过
$route.params
的方法获取。 - 也可以在通过代码逻辑实现路径跳转时,通过 query 传递参数;在组件中通过
$route.query
获取。const handleAboutNav = () => {router.push({path: '/about',// 1. 传递参数query: {name: 'Lee',age: 18,}}) }// 获取参数 import { useRoute } from 'vue-router' const route = useRoute() console.log(route.query.name)
路由懒加载:
// src/router/index.js
import {createRouter, createWebHashHistory} from 'vue-router'// 通过使用 import() 函数进行路由懒加载。打包时会进行分包处理,就可以在需要的时候再根据路径下载对应的组件代码
const Home = () => import('../components/Home.vue')
const About = () => import('../components/About.vue')const router = createRouter({routes: [{path: '/', redirect: '/home'}, {path: '/home', component: Home},{path: '/about', component: About}],history: createWebHashHistory(),
})export default router
路由导航守卫:
Vue Router 提供的路由导航守卫主要是通过跳转或者取消的方式守卫路由导航。
从一个路由跳转到另一个路由,就叫路由导航。对中间跳转的过程进行拦截处理,就叫导航守卫。
全局的导航守卫:
beforeEach()
:全局前置守卫。跳转到每个路由之前,beforeEach()
都会被触发。beforeEach()
接收一个回调函数作为参数。
回调函数接收两个参数,to 表示即将进入的路由对象,from 表示即将离开的路由对象。
回调函数的返回值如果是 false,取消当前导航;如果不返回或者返回 undefined,进行默认导航;如果返回一个字符串类型或者对象类型的路径地址,将会跳转到这个指定的路径。回调函数可选的第三个参数是 next。在 Vue2 中通过
next()
来决定如何进行跳转;但在 Vue3 中是通过返回值来控制的。因此不再推荐使用next()
函数。router.beforeEach((to, from) => {if(to.path !== '/login') {return '/login'} })
afterEach()
:全局后置守卫。不会改变导航本身。
组件内的导航守卫:
在 Composition API 中,可以通过 onBeforeRouteUpdate 和 onBeforeRouteLeave 分别添加 update 和 leave 守卫。
beforeRouteEnter()
:在进入组件对应的路由时触发。此时组件实例还没被创建,因为不能获取 this,可以通过回调函数的第三个参数 next 来访问组件实例。beforeRouteEnter (to, from, next) {next(instance=> {// 通过 instance 访问组件实例}) }
beforeRouteUpdate()
:在路由改变,但是组件被复用时调用。此时组件已经挂载好,导航守卫可以获取 this。例如对于一个带有动态参数的路径/users/:id
,在/users/1
和/users/2
之间跳转的时候,由于会渲染同样的UserDetails
组件,因此组件实例会被复用,这个钩子就会在这个情况下被调用。beforeRouteLeave()
:在离开组件对应的路由时触发。此时导航守卫可以获取 this。
导航守卫的解析流程:
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。