Skip to content

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 插件会做如下事情:

  1. 全局注册 RouterViewRouterLink 组件。
  2. 添加全局 $router$route 属性。
  3. 启用 useRouter()useRoute() 组合式函数。
  4. 触发路由器解析初始路由。

路由懒加载

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

  1. 所以,不能直接访问 this.$routerthis.$route
  2. 而作为替代,可以使用 useRouteruseRoute 函数:
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 中显式地返回 routerroute

不过,当前路由(route)仍有不同:

  • $routerrouter 并无差异。
  • $routeroute 有差异,$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>

嵌套路由

  1. 在路由配置文件中通过 children 来嵌套子路由。
  2. 在父组件中通过 <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 更新

  • 回调组件(激活)beforeRouteEnternext 函数(创建好的组件实例会作为回调函数的参数传入)

参阅 路由守卫

服务器配置示例

如果你部署到子目录,应该使用:

还需要调整下面的例子,以使用子目录而不是根目录(例如,将 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() // 获得所有路由记录的完整列表

基于 MIT 许可发布