主题
Vue Router
提示
当前版本:v4.x
介绍
Vue Router 是一个官方的 Vue.js 应用路由管理器。
安装
bash
npm i vue-router
项目搭建
bash
/src
/router
index.js
App.vue
main.js
js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home', // 命名路由,唯一
component: HomeView,
},
{
path: '/about',
name: 'about',
component: () => import('../views/AboutView.vue'),
},
],
})
export default router
js
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
vue
<template>
<RouterView />
</template>
当我们完成上述操作后,Vue Router 插件会做如下事情:
- 全局注册
RouterView
和RouterLink
组件。 - 添加全局
$router
和$route
属性。 - 启用
useRouter()
和useRoute()
组合式函数。 - 触发路由器解析初始路由。
路由懒加载
js
// 将
// import UserDetails from './views/UserDetails.vue'
// 替换成
const UserDetails = () => import('./views/UserDetails.vue')
const router = createRouter({
// ...
routes: [
{ path: '/users/:id', component: UserDetails }
// 或在路由定义里直接使用它
{ path: '/users/:id', component: () => import('./views/UserDetails.vue') },
],
})
按组分块
webpack
需要 Webpack > 2.4:
js
const UserDetails = () => import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
const UserDashboard = () => import(/* webpackChunkName: "group-user" */ './UserDashboard.vue')
const UserProfileEdit = () => import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue')
vite
在 vite 中,需要在 rollupOptions
下定义分块:
js
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
// https://rollupjs.org/guide/en/#outputmanualchunks
output: {
manualChunks: {
'group-user': ['./src/UserDetails', './src/UserDashboard', './src/UserProfileEdit'],
},
},
},
},
})
路由模式
Hash 模式
jsx
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [
//...
],
})
- url 表现:
url/#/login
#
这部分不会发送到服务器,所以不需要后端支持- SEO 不好
- 底层原理:hashChange 事件
HTML5 模式
jsx
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
- url 表现:
url/login
- 由于是 单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问
https://example.com/user/id
,就会得到一个 404 错误,所以需要 后端支持 - 底层原理:history 对象 + pushState 事件
Memory 模式
js
import { createRouter, createMemoryHistory } from 'vue-router'
const router = createRouter({
history: createMemoryHistory(),
routes: [
//...
],
})
- Memory 模式不会假定自己处于浏览器环境,因此不会与 URL 交互也 不会自动触发初始导航。所以它非常适合
Node
环境和SSR
。 - 底层原理:它是用
createMemoryHistory()
创建的,并且 需要你在调用app.use(router)
之后手动push
到初始导航。
虽然不推荐,你仍可以在浏览器应用程序中使用此模式,但请注意它 不会有历史记录,这意味着你无法后退或前进。
router
& route
在组件中访问
在组合式 API,在 setup
里面没有访问 this
:
- 所以,不能直接访问
this.$router
或this.$route
。 - 而作为替代,可以使用
useRouter
和useRoute
函数:
vue
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter() // 路由器实例
const route = useRoute() // 当前路由(reactive 包装)
function pushWithQuery(query) {
router.push({
name: 'search',
query: {
...route.query,
...query,
},
})
}
</script>
vue
<script setup>
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
</script>
<template>
<!-- 返回 true -->
{{ $router === router }}
<!-- 返回 false -->
{{ $route === route }}
</template>
提示
在组合式 API 中,<template>
模板中仍然可以访问 $router
和 $route
,而不需要在 setup
中显式地返回 router
和 route
。
不过,当前路由(route
)仍有不同:
$router
和router
并无差异。$route
和route
有差异,$route
会比route
多一个href
属性。
在组件外访问
js
import router from '@/router'
console.log(router) // 路由器实例
console.log(router.currentRoute) // 当前路由(ref 包装)
提示
关于 router
,可做参考:
vue
<script setup>
import router from '@/router'
import { useRouter } from 'vue-router'
const _router = useRouter()
console.log(router === _router) // true
</script>
<template>
{{ $router === _router }} <!-- true -->
{{ $router === router }} <!-- true -->
</template>
监听当前路由(route
)的变化
route
对象是一个响应式对象,所以它的任何属性都可以被监听,但你应该避免监听整个 route
对象。在大多数情况下,你应该直接监听你期望改变的参数。
vue
<script setup>
import { useRoute } from 'vue-router'
import { ref, watch } from 'vue'
const route = useRoute()
const userData = ref()
// 当参数更改时获取用户信息
watch(
() => route.params.id,
async newId => {
userData.value = await fetchUser(newId)
}
)
</script>
路由导航
声明式导航
vue
<template>
<RouterLink to="/about">About</RouterLink>
</template>
<router-link>
实际上就是通过创建 <a>
标签来定义导航链接的。
具体用法可参考 RouterLinkProps。
编程式导航
vue
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<template>
<button @click="() => router.push('/about')">关于页</button>
</template>
router.push()
和 router.replace()
会返回一个 Promise
对象,若希望在路由变更后在执行某些操作,需要配合 then()
或者 async / await
来进行使用。
具体用法可以参考 Router,其中与 路由导航 相关的方法有:
push()
replace()
go()
back()
forward()
路由传参 & 接参
queryParams
template
<RouterLink :to="{ path: '/user', query: { id: 12, name: 'vfanlee' } }">User</RouterLink>
vue
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
const { id, name } = route.query
</script>
pathParams
js
{
path: '/user/:id/:name',
component: User
}
vue
<script setup>
import { useRoute } from 'vue-router'
const route = useRoute()
const { id, name } = route.params
</script>
嵌套路由
- 在路由配置文件中通过
children
来嵌套子路由。 - 在父组件中通过
<RouterView>
组件渲染子路由。
js
{
path: '/',
component: Layout,
children: [
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
}
]
}
vue
<template>
<h1>layout</h1>
<RouterView />
</template>
默认路由
嵌套路由时,指定某个子路由为默认路由,可通过定义 path
为空字符串:
js
{
path: '/',
component: Layout,
children: [
{
path: '', // 默认路由
component: Home
},
{
path: '/about',
component: About
}
]
}
404 路由
js
{
path: '/:pathMatch(.*)*',
component: NotFound
}
完整的导航解析流程
导航触发
- 组件(失活)
beforeRouteLeave
- 全局
beforeEach
- 组件(重用)
beforeRouteUpdate
- 独享
beforeEnter
- 组件(失活)
解析异步路由
- 组件(激活)
beforeRouteEnter
- 全局
beforeResolve
- 组件(激活)
导航确认
- 全局
beforeEach
- 全局
DOM 更新
回调组件(激活)
beforeRouteEnter
的next
函数(创建好的组件实例会作为回调函数的参数传入)
参阅 路由守卫。
服务器配置示例
如果你部署到子目录,应该使用:
还需要调整下面的例子,以使用子目录而不是根目录(例如,将 RewriteBase/
替换为 RewriteBase/name-of-your-subfolder/
)。
附加说明
这有一个注意事项。你的服务器将不再报告 404 错误,因为现在所有未找到的路径都会显示你的 index.html
文件。为了解决这个问题,你应该在你的 Vue 应用程序中实现一个万能的路由来显示 404 页面。
js
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/:pathMatch(.*)', component: NotFoundComponent }],
})
另外,如果你使用的是 Node.js 服务器,你可以通过在服务器端使用路由器来匹配传入的 URL,如果没有匹配到路由,则用 404 来响应,从而实现回退。查看 Vue 服务器端渲染文档 了解更多信息。
获取路由表
js
// https://router.vuejs.org/zh/api/interfaces/RouterOptions.html#Properties-routes
router.options.routes // 路由器的初始路由列表
// https://router.vuejs.org/zh/api/interfaces/Router.html#Methods-getRoutes
router.getRoutes() // 获得所有路由记录的完整列表