Vue Router 3 完全指南:从入门到掌握 – wiki基地


Vue Router 3 完全指南:从入门到掌握

1. 引言:为什么需要路由?

在现代 Web 开发中,单页应用(Single Page Application, SPA)越来越流行。SPA 的核心特点是只有一个 HTML 页面,通过 JavaScript 动态地重写当前页面来实现不同视图之间的切换,而无需重新加载整个页面。这带来了更流畅的用户体验和更快的页面响应速度。

然而,SPA 也带来了一个问题:如何在同一个页面内管理不同“页面”或“视图”的状态和切换?用户如何通过 URL 来访问特定的视图,就像传统多页应用那样?如何实现前进、后退、收藏等浏览器原生功能?

这就是前端路由要解决的问题。前端路由允许我们在不刷新页面的情况下,根据 URL 的变化来渲染不同的组件(视图)。Vue.js 作为流行的前端框架,其官方路由管理器就是 Vue Router

本文将聚焦于 Vue Router 3,它是 Vue 2 生态系统的官方配套路由库。虽然 Vue 3 和 Vue Router 4 已经发布,但 Vue 2 仍然在大量现有项目中广泛使用,因此深入理解 Vue Router 3 仍然非常重要。我们将从最基础的概念讲起,逐步深入到高级用法,带你从入门到掌握 Vue Router 3。

2. Vue Router 3 入门:构建你的第一个路由应用

2.1 安装 Vue Router 3

首先,你需要在你的 Vue 2 项目中安装 Vue Router 3。使用 npm 或 yarn 命令:

“`bash
npm install vue-router@^3.0.0

或者

yarn add vue-router@^3.0.0
“`

这里指定 @^3.0.0 是为了确保安装的是 Vue Router 3 的最新版本,而不是 Vue Router 4。

2.2 创建 Router 实例

安装完成后,你需要在你的 Vue 项目中创建一个路由实例。通常,我们会在项目的 src 目录下创建一个 router 文件夹,并在其中创建 index.js 文件来存放路由配置。

“`javascript
// src/router/index.js
import Vue from ‘vue’
import VueRouter from ‘vue-router’

// 导入你的视图组件
import Home from ‘../views/Home.vue’ // 假设你有一个 Home.vue 组件
import About from ‘../views/About.vue’ // 假设你有一个 About.vue 组件

// 1. 安装 VueRouter 插件
Vue.use(VueRouter)

// 2. 定义路由规则
// 每个路由规则映射一个路径到一个组件
const routes = [
{
path: ‘/’, // 路径
name: ‘Home’, // 命名路由 (可选)
component: Home // 对应的组件
},
{
path: ‘/about’,
name: ‘About’,
component: About
// component: () => import(/ webpackChunkName: “about” / ‘../views/About.vue’) // 也可以使用懒加载
}
]

// 3. 创建 router 实例
const router = new VueRouter({
mode: ‘history’, // 路由模式,默认为 ‘hash’
base: process.env.BASE_URL, // 应用的根路径
routes // 路由规则数组
})

// 4. 导出 router 实例
export default router
“`

路由模式 (mode):

  • 'hash' (默认): 使用 URL hash (#) 来模拟完整的 URL 路径,例如 http://localhost:8080/#/about。这种模式不需要服务器端特殊配置,兼容性好。
  • 'history' 利用 HTML5 History API (pushState, replaceState) 来实现 URL 导航,使 URL 看起来更“正常”,例如 http://localhost:8080/about。使用这种模式需要服务器端配置,确保所有路径都回退到应用的根 HTML 文件,否则刷新时会出现 404 错误。

base: 应用的基路径。如果你的应用部署在子目录下(例如 example.com/my-app/),则需要将 base 设置为 /my-app/

2.3 将 Router 实例注入 Vue 应用

创建好路由实例后,需要将其注入到你的 Vue 根实例中,以便整个应用都能访问到路由功能。

“`javascript
// src/main.js
import Vue from ‘vue’
import App from ‘./App.vue’
import router from ‘./router’ // 导入之前创建的 router 实例
import store from ‘./store’ // 如果你使用 Vuex

Vue.config.productionTip = false

new Vue({
router, // 将 router 实例注入 Vue 根实例
store, // 如果你使用 Vuex
render: h => h(App)
}).$mount(‘#app’)
“`

2.4 在应用中使用路由组件

最后,在你的根组件 (App.vue) 或其他顶层组件中,你需要使用 Vue Router 提供的两个核心组件:<router-link><router-view>

“`vue

“`

  • <router-link>: 用于创建导航链接。它会被渲染成一个 <a> 标签(默认),其 href 属性会自动根据 to 属性的值生成。当用户点击时,它会阻止默认的浏览器导航行为,而是调用 Vue Router 的导航方法,从而实现客户端路由切换。
  • <router-view>: 这是一个函数式组件,它根据当前路由路径渲染对应的组件。不同的路由路径会渲染不同的组件到 <router-view> 的位置。

至此,你已经构建了一个最简单的 Vue Router 应用。运行你的项目 (npm run serveyarn serve),你应该能看到导航链接,点击链接可以在首页和关于页面之间切换,同时浏览器的 URL 也会相应变化。

3. 深入路由配置

简单的路径到组件映射只是开始,Vue Router 提供了丰富的路由配置选项来应对各种复杂的路由场景。

3.1 动态路由匹配

很多时候,我们需要将同一个组件映射到具有相似路径的不同 URL 上。例如,显示不同用户的个人资料页面:/user/john/user/jane 等。Vue Router 允许在路径中使用动态片段来实现这一点。

javascript
const routes = [
// ... 其他路由
{
path: '/user/:id', // :id 是一个动态片段
component: User // 假设你有一个 User.vue 组件
}
]

当一个路由被匹配时,动态片段的值会作为参数暴露在组件内部的 $route.params 对象中。

“`vue

“`

将路由参数作为组件 props 传递 (props: true)

将路由参数直接作为组件的 props 传递是一个很好的实践,这使得组件更容易复用和测试,因为它不依赖于 $route 对象。要启用此功能,只需在路由配置中设置 props: true

javascript
const routes = [
{
path: '/user/:id',
component: User,
props: true // 将路由参数作为 props 传递给 User 组件
// 如果动态片段是 :userId,那么 User 组件需要定义名为 userId 的 prop
}
// 或者使用函数形式,更灵活
// props: route => ({ userId: route.params.id })
]

如果使用函数形式 props: route => ({ userId: route.params.id }),你可以自定义 props 的名称和值,甚至可以同时传递静态值:props: route => ({ userId: route.params.id, staticValue: 'hello' })

3.2 嵌套路由

实际应用中,一个组件内部可能包含另一个 <router-view>,用于渲染子级路由。例如,用户页面可能包含一个用户资料子页面 /user/:id/profile 和一个用户帖子子页面 /user/:id/posts

嵌套路由通过在路由配置中使用 children 数组来定义:

javascript
const routes = [
{
path: '/user/:id',
component: User, // 父级组件 User.vue 内部需要有一个 <router-view>
children: [
// 当 /user/:id 匹配时,User 组件内的 <router-view> 会渲染以下任一子路由
{
// 匹配 /user/:id/profile
path: 'profile', // 注意:嵌套路径不以 / 开头,会自动拼接到父级路径后面
component: UserProfile // 假设 UserProfile.vue 是子组件
},
{
// 匹配 /user/:id/posts
path: 'posts',
component: UserPosts // 假设 UserPosts.vue 是子组件
}
// 默认子路由:当访问 /user/:id 时,如果 User 组件内的 <router-view> 没有匹配到特定子路径,
// 可以通过设置一个 path 为空字符串 '' 的子路由作为默认显示。
// {
// path: '', // 匹配父路径但不带子路径
// component: UserProfile // 例如默认显示用户资料
// }
]
}
]

在父级组件 User.vue 中,你需要添加另一个 <router-view> 来渲染子路由匹配到的组件:

“`vue

“`

3.3 命名路由

为路由定义一个 name 属性可以让你更方便地进行导航和管理,特别是在路径可能发生变化时。

javascript
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/user/:id',
name: 'UserProfile', // 命名路由
component: User,
props: true
}
]

使用命名路由进行导航:

“`vue


查看用户 {{ userId }}

查看用户 {{ userId }}
“`

javascript
// 编程式导航时使用命名路由
router.push({ name: 'UserProfile', params: { id: userId } });

使用命名路由的好处是,即使 /user/:id 的路径改成了 /profile/:id,你只需要修改路由配置中的 path 属性,使用命名路由的地方无需改动,提高了代码的可维护性。

3.4 命名视图

有时候,一个页面需要同时渲染多个组件,而不是只有一个主 <router-view>。例如,一个布局可能包含侧边栏、导航栏和主内容区域。Vue Router 允许你在 <router-view> 上使用 name 属性来定义命名视图。

“`vue


“`

在路由配置中,使用 components (注意是复数) 对象来指定哪些组件应该渲染到哪些命名视图:

javascript
const routes = [
{
path: '/',
// 匹配 '/' 路径时,渲染以下组件到对应的命名视图
components: {
default: Home, // 渲染到未命名的 <router-view>
header: HeaderComponent, // 渲染到 <router-view name="header">
sidebar: SidebarComponent, // 渲染到 <router-view name="sidebar">
footer: FooterComponent // 渲染到 <router-view name="footer">
}
},
// 对于命名视图,也可以使用动态匹配或嵌套路由
// {
// path: '/settings',
// components: {
// default: SettingsPage,
// sidebar: SettingsSidebar
// }
// }
]

这对于创建复杂的布局非常有用。

3.5 重定向和别名

  • 重定向 (redirect): 将一个路径重定向到另一个路径。当用户访问 /a 时,URL 会被替换成 /b,然后匹配 /b 对应的组件。

    javascript
    const routes = [
    { path: '/a', redirect: '/b' }, // 从 /a 重定向到 /b
    { path: '/b', component: ComponentB },
    // 也可以重定向到命名路由
    { path: '/c', redirect: { name: 'ComponentC' } },
    { path: '/d/:id', redirect: '/e/:id' }, // 重定向时保留参数
    {
    path: '/f', // 复杂重定向,使用函数
    redirect: to => {
    // to 是目标路由对象
    // 返回一个路径字符串或路由对象
    return { path: '/g', query: to.query };
    }
    }
    ]

  • 别名 (alias): 为一个路径设置别名。当用户访问别名路径时,URL 不会 改变,但会匹配到与原路径相同的组件。

    javascript
    const routes = [
    {
    path: '/user/:id',
    component: User,
    alias: '/profile/:id' // 现在 /profile/:id 也会匹配到 User 组件,但 URL 仍显示 /profile/:id
    // 也可以设置多个别名,alias: ['/profile/:id', '/u/:id']
    }
    ]

别名在你想让一个组件可以通过多个 URL 访问但不想让 URL 改变时很有用。

3.6 Catch-all 回退路由 (404 Not Found)

如果你想处理所有未匹配到的路由(例如显示一个 404 页面),可以定义一个路径为 * 的路由。这个路由应该放在路由配置数组的最后面,因为路由匹配是按顺序进行的。

javascript
const routes = [
// ... 其他路由
{
path: '*', // 匹配所有未匹配到的路径
component: NotFound // 假设有一个 NotFound.vue 组件
}
]

当用户访问任何没有在前面路由中定义的路径时,都会匹配到这个路由,并渲染 NotFound 组件。

4. 编程式导航

除了使用 <router-link> 进行声明式导航,我们还可以在 JavaScript 代码中进行导航,这称为编程式导航。Vue Router 提供了 router 实例上的一系列方法来实现这一点。

你可以在任何组件内部通过 this.$router 访问到路由实例。

4.1 router.push()

这个方法会在历史栈中添加一个新条目,当点击浏览器后退按钮时,会回到上一个页面。这类似于点击一个普通的 <router-link>

“`javascript
// 字符串路径
this.$router.push(‘/about’)

// 带有路径的对象
this.$router.push({ path: ‘/about’ })

// 带有命名路由和参数的对象
this.$router.push({ name: ‘UserProfile’, params: { id: ‘123’ } })

// 带有路径和查询参数 (query)
this.$router.push({ path: ‘/search’, query: { q: ‘vue’ } }) // 会生成 /search?q=vue

// 带有命名路由和查询参数 (query)
this.$router.push({ name: ‘SearchPage’, query: { q: ‘vue’ } }) // 如果 SearchPage 路由路径是 /search,也会生成 /search?q=vue

// 带有 hash
this.$router.push({ path: ‘/about’, hash: ‘#section-1’ }) // 会生成 /about#section-1

// params 和 path 不能同时使用
// 如果提供了 path,params 会被忽略
// 错误的例子: this.$router.push({ path: ‘/user’, params: { id: ‘123’ } }) // params 会被忽略,只跳转到 /user

// 正确的做法是使用命名路由结合 params
this.$router.push({ name: ‘UserProfile’, params: { id: ‘123’ } }) // 假设 UserProfile 路由路径是 /user/:id
“`

4.2 router.replace()

这个方法与 router.push() 类似,但它不会在历史栈中添加新条目,而是替换当前的历史记录。这类似于 <router-link>replace 属性。

“`javascript
// 字符串路径
this.$router.replace(‘/about’)

// 对象形式
this.$router.replace({ name: ‘UserProfile’, params: { id: ‘123’ } })
“`

4.3 router.go(), router.back(), router.forward()

这些方法允许你在历史记录中向前或向后导航,类似于浏览器的前进/后退按钮。

  • router.go(n): 在历史记录中向前或向后跳转 n 步。n 可以是正数 (前进) 或负数 (后退)。
  • router.back(): 等同于 router.go(-1),后退一步。
  • router.forward(): 等同于 router.go(1),前进一步。

“`javascript
// 后退一步
this.$router.back()

// 前进一步
this.$router.forward()

// 后退两步
this.$router.go(-2)
“`

4.4 获取当前路由信息 (this.$route)

在任何组件内部,你可以通过 this.$route 对象访问当前激活的路由信息。这个对象是只读的,包含当前路径、参数、查询参数、hash、匹配到的路由记录等信息。

“`javascript
// 当前路径,例如 ‘/user/123?q=abc#section’
this.$route.path

// 当前路由的名称 (如果定义了)
this.$route.name

// 路径参数,例如 { id: ‘123’ }
this.$route.params

// 查询参数,例如 { q: ‘abc’ }
this.$route.query

// URL 的 hash 部分 (包括 #)
this.$route.hash

// 匹配到的路由记录数组 (包含父级路由)
this.$route.matched

// 元数据 (如果路由配置中定义了 meta 字段)
this.$route.meta
“`

请注意,$route 对象是响应式的。如果你在组件中观察 $route 的变化(例如通过 watch 或 beforeRouteUpdate 导航守卫),可以在同一个组件实例中对路由参数的变化做出响应,而无需销毁和重新创建组件。

5. 导航守卫 (Navigation Guards)

导航守卫是 Vue Router 提供的一种机制,允许你在路由导航过程中执行逻辑,例如检查用户是否已登录、获取数据等。导航守卫分为全局、路由独享和组件内守卫。

导航的完整流程是:
1. 触发导航。
2. 调用全局前置守卫 beforeEach
3. 在当前失活的组件里调用 beforeRouteLeave
4. 调用路由独享的 beforeEnter
5. 解析异步路由组件。
6. 调用全局前置守卫 beforeResolve
7. 在将要激活的组件里调用 beforeRouteEnter
8. 调用全局后置守卫 afterEach

5.1 全局守卫

router.beforeEach(to, from, next)

在路由导航发生 之前 调用。这是最常用的守卫,可以用来进行全局的权限检查、登录跳转等。

  • to: 即将进入的目标路由对象。
  • from: 当前导航正要离开的路由对象。
  • next: 必须调用 的一个函数,用来决定导航的行为:
    • next(): 继续导航。
    • next(false): 中断当前导航。
    • next('/')next({ path: '/' }): 跳转到一个新的路由。
    • next(error): (2.4+) 导航被中断,并触发 router.onError 回调。

“`javascript
// src/router/index.js
router.beforeEach((to, from, next) => {
console.log(‘全局前置守卫:’, to.path, ‘<-‘, from.path);

// 示例:检查路由是否需要认证
if (to.matched.some(record => record.meta.requiresAuth)) {
// 这个路由需要认证
// 假设你有一个 auth 模块来检查登录状态
const isAuthenticated = checkAuthStatus(); // 你的检查登录状态函数

if (!isAuthenticated) {
  // 用户未登录,重定向到登录页
  console.log('未登录,重定向到登录页');
  next({
    path: '/login',
    query: { redirect: to.fullPath } // 将目标路径作为查询参数传递,登录后跳转回
  });
} else {
  // 用户已登录,继续导航
  console.log('已登录,继续导航');
  next();
}

} else {
// 不需要认证的页面,直接继续导航
console.log(‘不需要认证,继续导航’);
next();
}
});

function checkAuthStatus() {
// 模拟检查登录状态
return localStorage.getItem(‘token’) ? true : false;
}
“`

在需要认证的路由配置中添加 meta 字段:

javascript
const routes = [
// ...
{
path: '/dashboard',
name: 'Dashboard',
component: Dashboard, // 假设 Dashboard.vue 需要登录
meta: { requiresAuth: true } // 添加元数据
},
{
path: '/login',
name: 'Login',
component: Login
}
]

to.matched 是一个数组,包含当前路由记录以及所有父级路由记录。to.matched.some(record => record.meta.requiresAuth) 可以检查当前路由及其任何父级路由是否设置了 requiresAuth: true

router.beforeResolve(to, from, next)

在导航被确认之前、所有组件内守卫和异步路由解析完成之后调用。这个守卫可以用于在所有路由配置完成加载后,但在实际导航发生前做一些最后的检查或数据获取。它同样接受 (to, from, next) 参数。

router.afterEach(to, from)

在导航成功完成之后调用。这些守卫不会接收 next 函数,也不会改变导航本身。它们主要用于副作用,例如发送分析请求、修改页面标题等。

javascript
// src/router/index.js
router.afterEach((to, from) => {
console.log('全局后置守卫:', '导航完成', to.path, '来自', from.path);
// 例如,设置页面标题
if (to.meta && to.meta.title) {
document.title = to.meta.title + ' - My App';
} else {
document.title = 'My App';
}
});

在路由配置中添加页面标题元数据:

javascript
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: { title: '首页' }
},
{
path: '/about',
name: 'About',
component: About,
meta: { title: '关于我们' }
}
]

5.2 路由独享守卫 (beforeEnter)

在路由配置中直接定义的守卫,只应用于当前路由。

javascript
const routes = [
{
path: '/specific-route',
component: SpecificComponent,
beforeEnter: (to, from, next) => {
// 只有在进入 /specific-route 之前会调用
console.log('路由独享守卫:', to.path, '<-', from.path);
// 执行一些逻辑,例如检查特定权限
const hasSpecificPermission = checkSpecificPermission();
if (hasSpecificPermission) {
next(); // 有权限,继续导航
} else {
next(false); // 没有权限,中断导航
// next('/unauthorized'); // 或者重定向到无权限页面
}
}
}
]

这个守卫只在进入路由时触发,不会 在参数或查询参数改变时触发(例如从 /specific-route/1/specific-route/2)。

5.3 组件内守卫

直接在组件内部定义的守卫。

beforeRouteEnter(to, from, next)

在路由进入当前组件之前调用。这个守卫在组件实例被创建之前执行,所以你无法在这个守卫中使用 this。但你可以通过向 next 传递一个回调函数来访问组件实例。

“`vue


“`

这个守卫常用于在进入页面前异步获取数据。

beforeRouteUpdate(to, from, next)

在当前路由改变,但组件被复用时调用。例如,从 /user/1 导航到 /user/2 时,User 组件实例会被复用(而不是销毁重建),这时就会触发 beforeRouteUpdate。你可以在这个守卫中访问 this

“`vue


```

这个守卫非常适合处理动态路由参数变化时的数据更新。

beforeRouteLeave(to, from, next)

在离开当前路由之前调用。常用于在用户离开页面前进行确认提示(例如有未保存的表单)。

```vue

```

这个守卫可以有效地防止用户意外丢失未保存的数据。

6. 路由元信息 (Meta Fields)

路由配置中可以定义 meta 字段,用于存储与路由相关的任意信息。这些信息可以在导航守卫或组件中访问。

javascript
const routes = [
{
path: '/admin',
component: AdminPanel,
meta: {
requiresAuth: true, // 需要认证
roles: ['admin'], // 需要 admin 角色
layout: 'admin' // 使用 admin 布局
}
}
]

访问元信息:

```javascript
// 在全局守卫中
router.beforeEach((to, from, next) => {
// 检查 requiresAuth
if (to.matched.some(record => record.meta.requiresAuth)) {
// ... 认证逻辑
}
// 检查 roles
if (to.meta.roles && !userHasRequiredRole(to.meta.roles)) {
// ... 无权限逻辑
}
next();
})

// 在组件中
// 通过 this.$route.meta 访问当前路由的元信息
console.log(this.$route.meta.layout); // 'admin'
```

7. 滚动行为 (Scroll Behavior)

当路由导航时,页面的滚动位置默认会被重置到顶部。通过配置 scrollBehavior 函数,你可以自定义页面的滚动行为。这个函数在创建路由实例时配置。

```javascript
// src/router/index.js
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
scrollBehavior (to, from, savedPosition) {
// 返回 false 或一个 falsy 值,则不进行滚动
// return false

// 如果 savedPosition 存在,表示是浏览器前进/后退导航
if (savedPosition) {
  return savedPosition
} else {
  // 如果是新的导航,滚动到页面顶部
  return { x: 0, y: 0 }
}

// 处理锚点链接,滚动到对应的元素
if (to.hash) {
  return {
    selector: to.hash
    // 如果需要平滑滚动,可以添加 behavior: 'smooth' (需要浏览器支持)
    // behavior: 'smooth'
  }
}

// 异步滚动,可以配合数据获取等操作
// return new Promise((resolve, reject) => {
//   setTimeout(() => {
//     resolve({ x: 0, y: 0 })
//   }, 500)
// })

}
})
```

scrollBehavior 函数接收三个参数:
* to: 目标路由对象。
* from: 源路由对象。
* savedPosition: 如果是浏览器前进/后退导航,这个参数会是之前页面的滚动位置;否则为 undefined

这个函数应该返回一个滚动位置对象 ({ x: 0, y: 0 })、一个选择器字符串 ({ selector: '#anchor' }),或者一个 Promise,或者返回 false

8. 懒加载路由 (Code Splitting)

当你的应用变得庞大时,所有组件都被打包到一个文件中会导致初始加载时间过长。Vue Router 支持将路由对应的组件进行代码分割(Code Splitting),只在需要时才加载对应的组件代码,从而提高应用的加载性能。

这通过使用动态导入 (import()) 来实现:

javascript
// src/router/index.js
const routes = [
{
path: '/',
name: 'Home',
component: () => import(/* webpackChunkName: "home" */ '../views/Home.vue') // 使用动态导入
},
{
path: '/about',
name: 'About',
// 使用 webpackChunkName 注释可以指定 chunk 的名称,方便调试和缓存
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/user/:id',
name: 'UserProfile',
component: () => import(/* webpackChunkName: "user" */ '../views/User.vue'),
props: true
}
]

Webpack(或 Vite 等构建工具)会根据这些动态导入将组件打包成独立的 JavaScript 文件(chunk)。当用户访问对应的路由时,Vue Router 会异步加载并解析这些 chunk。

分组懒加载: 你还可以通过设置相同的 webpackChunkName 将相关的组件打包到同一个 chunk 中。

javascript
const routes = [
{
path: '/group-a',
component: () => import(/* webpackChunkName: "group-a" */ '../views/GroupA.vue')
},
{
path: '/group-a/child',
component: () => import(/* webpackChunkName: "group-a" */ '../views/GroupAChild.vue')
}
]

访问 /group-a/group-a/child 都会加载同一个 group-a chunk。

9. 路由过渡动效 (<transition>)

Vue 的 <transition> 组件可以与 <router-view> 结合使用,为路由切换添加过渡效果。

只需将 <router-view> 包裹在 <transition> 组件中:

```vue


```

然后定义对应的 CSS 过渡类:

css
/* styles.css 或 <style> 标签内 */
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}

你可以使用不同的过渡模式(mode="out-in"mode="in-out") 来控制新旧组件的进入/离开顺序,或使用动态过渡(根据路由决定不同的过渡效果)。

10. 数据获取策略

在 Vue Router 应用中,数据获取是一个常见的问题。通常有两种主要策略:

  1. 在导航进入前获取数据 (在导航守卫中):

    • 优点:确保数据获取完成后才渲染页面,避免页面闪烁或显示不完整数据。
    • 缺点:如果数据获取耗时较长,页面会停留在上一个状态或空白状态,用户感知较慢。
    • 实现:在 beforeRouteEnterbeforeEnter 守卫中进行异步数据请求,并在数据获取成功后调用 next()next(vm => { ... })
  2. 在导航进入后获取数据 (在组件的生命周期钩子中):

    • 优点:页面可以立即渲染,用户能更快看到页面结构(可以显示加载中状态),用户感知较快。
    • 缺点:需要处理数据加载中的状态、错误处理,页面可能在数据加载完成前显示空或旧数据。
    • 实现:在组件的 createdmounted 钩子中进行数据请求。当路由参数变化导致组件复用时,在 beforeRouteUpdate 钩子中重新获取数据。

选择哪种策略取决于具体的需求和用户体验目标。通常,对于需要依赖数据才能正确渲染的页面,使用守卫获取数据更安全;对于可以先渲染框架再填充数据的页面,在组件内部获取数据可以提供更好的用户感知性能。

11. Vue Router 3 的一些注意点和最佳实践

  • 参数变化与组件复用: 当从一个动态路由导航到另一个只改变参数的同一路由时(例如 /user/1/user/2),组件实例会被复用。此时不会触发组件的 createdmounted 钩子,但会触发 beforeRouteUpdate。务必在 beforeRouteUpdate 中处理参数变化带来的数据更新或其他副作用。
  • 导航守卫中的异步操作: 在导航守卫中执行异步操作(如数据请求)时,必须在异步操作完成后调用 next(),否则导航会一直处于等待状态。
  • 路由配置的组织: 对于大型应用,将所有路由配置放在一个文件中会变得难以管理。建议将路由配置按功能模块拆分成多个文件,然后在 router/index.js 中引入并合并。
  • 懒加载粒度: 不要将所有组件都懒加载。对于首屏必要的组件(如首页、布局组件),保持同步加载可能更好,以避免额外的网络请求延迟。可以根据应用的实际情况和性能分析来决定懒加载的粒度。
  • 路由参数的命名: 使用描述性的参数名称(例如 :userId 而不是 :id),提高代码可读性。
  • 使用命名路由: 优先使用命名路由进行导航,尤其是在路径可能发生变化的动态路由中。
  • 路由别名 vs 重定向: 理解它们的区别,选择适合场景的方式。别名保留原 URL,重定向改变 URL。

12. 快速了解 Vue Router 4 (For Vue 3)

虽然本文专注于 Vue Router 3,但了解 Vue Router 4 的主要变化对于未来迁移或使用 Vue 3 的项目也很重要:

  • Vue 3 支持: Vue Router 4 是 Vue 3 的官方路由库,不兼容 Vue 2。
  • 创建方式改变: 创建路由实例的方式改为工厂函数 createRouter (import { createRouter, createWebHistory } from 'vue-router'),并注入 Vue 应用的方式也不同。
  • History 模式默认: 推荐使用 createWebHistory 创建 history 模式的路由。
  • 导航守卫签名改变: next() 函数的用法有所调整,特别是 next() 传递参数的行为。
  • 获取路由和路由器: 在组件中不再通过 this.$routethis.$router 访问,而是通过 useRoute()useRouter() Hooks 函数(Composition API)。
  • 其他 API 调整: 部分 API 名称或用法有细微变化。

如果你正在或计划使用 Vue 3,请查阅 Vue Router 4 的官方文档。

13. 总结

Vue Router 3 是 Vue 2 应用中构建 SPA 不可或缺的利器。它提供了强大而灵活的功能,从基本的路由映射、动态路由、嵌套路由,到高级的导航守卫、元信息、滚动行为和懒加载。

通过本文的详细介绍,你应该已经掌握了 Vue Router 3 的核心概念和常用功能:

  • 学会了如何安装和初始化 Vue Router。
  • 理解了 <router-link><router-view> 的作用。
  • 掌握了如何定义各种类型的路由,包括动态匹配、嵌套、命名路由、命名视图、重定向和别名。
  • 学会了如何使用编程式导航进行页面跳转。
  • 深入理解了全局、路由独享和组件内导航守卫的用法,以及它们在处理认证、数据获取等场景中的应用。
  • 了解了如何利用路由元信息存储额外数据。
  • 配置了自定义的滚动行为。
  • 实现了路由级别的代码分割以优化性能。
  • 了解了如何结合 <transition> 实现页面切换动效。

掌握 Vue Router 3,你就能轻松构建复杂且用户体验良好的单页应用。实践是最好的学习方式,尝试在你的项目中应用这些知识,不断探索和解决遇到的问题。祝你在 Vue.js 开发的道路上越走越远!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部