Vue-Router详解:实现单页应用的正确姿势 – wiki基地

Vue-Router 详解:实现单页应用的正确姿势

在现代 Web 开发中,单页应用(Single Page Application,SPA)以其流畅的用户体验和高效的前端性能而备受青睐。Vue.js 作为一款流行的前端框架,提供了强大的 Vue-Router 插件,使得构建 SPA 变得轻而易举。本文将深入探讨 Vue-Router 的方方面面,从基本概念到高级用法,帮助您全面掌握使用 Vue-Router 构建单页应用的正确姿势。

一、单页应用与 Vue-Router 的必要性

1.1 什么是单页应用?

单页应用是指仅加载单个 HTML 页面,并通过 JavaScript 动态更新页面内容的 Web 应用程序。用户在与 SPA 交互时,无需重新加载整个页面,而是通过 AJAX 或 Fetch API 获取数据,并使用 JavaScript 动态更新 DOM,从而实现页面内容的无缝切换。

1.2 单页应用的优势

  • 更好的用户体验: SPA 避免了页面跳转时的白屏和闪烁,提供了更流畅、更快速的用户体验。
  • 减少服务器负载: SPA 仅需加载一次 HTML、CSS 和 JavaScript 资源,后续页面切换通过客户端路由完成,降低了服务器压力。
  • 前后端分离: SPA 通常采用前后端分离的架构,前端负责页面渲染和交互,后端提供 API 接口,提高了开发效率和可维护性。

1.3 为什么需要 Vue-Router?

在传统的 Web 开发中,页面之间的跳转由服务器端路由控制。而在 SPA 中,我们需要在客户端实现路由管理,即根据 URL 的变化来动态渲染不同的组件。Vue-Router 正是 Vue.js 官方提供的路由管理器,它提供了以下核心功能:

  • 声明式路由配置: 通过简洁的配置方式,将 URL 与组件进行映射。
  • 动态路由: 支持动态参数、嵌套路由等高级路由模式。
  • 导航守卫: 提供全局前置守卫、全局后置钩子、路由独享守卫和组件内守卫,用于控制路由的访问权限和执行特定操作。
  • 过渡效果: 支持在路由切换时添加过渡动画,提升用户体验。
  • 历史记录管理: 提供 hashhistory 两种模式,用于管理浏览器的历史记录。

二、Vue-Router 的基本用法

2.1 安装与引入

首先,通过 npm 或 yarn 安装 Vue-Router:

“`bash
npm install vue-router

yarn add vue-router
“`

然后,在你的 Vue 项目中引入 Vue-Router:

“`javascript
// main.js
import Vue from ‘vue’
import VueRouter from ‘vue-router’
import App from ‘./App.vue’
import Home from ‘./components/Home.vue’
import About from ‘./components/About.vue’

Vue.use(VueRouter)

const routes = [
{ path: ‘/’, component: Home },
{ path: ‘/about’, component: About }
]

const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})

new Vue({
router,
render: h => h(App)
}).$mount(‘#app’)
“`

2.2 核心概念

  • <router-link> 用于创建导航链接,类似于 HTML 的 <a> 标签,但它会在客户端进行路由跳转,而不是重新加载页面。

    vue
    <router-link to="/">Home</router-link>
    <router-link to="/about">About</router-link>

  • <router-view> 用于渲染当前路由匹配到的组件。

    vue
    <!-- App.vue -->
    <template>
    <div id="app">
    <router-link to="/">Home</router-link>
    <router-link to="/about">About</router-link>
    <router-view></router-view>
    </div>
    </template>

  • routes 配置: 一个数组,每个元素都是一个路由对象,定义了 URL 路径和组件的映射关系。

    javascript
    const routes = [
    { path: '/', component: Home },
    { path: '/about', component: About }
    ]

  • router 实例: VueRouter 的实例,包含了路由配置和导航方法。

2.3 动态路由

动态路由是指 URL 路径中包含参数,可以根据参数的不同渲染不同的内容。

javascript
const routes = [
{ path: '/user/:id', component: User }
]

在组件中,可以通过 $route.params 访问路由参数:

“`vue


“`

2.4 嵌套路由

嵌套路由是指在一个路由下嵌套多个子路由,用于构建更复杂的页面结构。

javascript
const routes = [
{
path: '/user/:id',
component: User,
children: [
{
path: 'profile',
component: UserProfile
},
{
path: 'posts',
component: UserPosts
}
]
}
]

在父组件中,需要使用 <router-view> 来渲染子路由的组件:

“`vue


“`

三、Vue-Router 的导航守卫

导航守卫是 Vue-Router 提供的一种强大的机制,用于控制路由的访问权限、执行特定操作或改变路由行为。

3.1 导航守卫的类型

  • 全局前置守卫: router.beforeEach
    在每次路由跳转前执行,可以用于进行全局的权限验证、登录状态检查等。

    “`javascript
    router.beforeEach((to, from, next) => {
    // to: 即将要进入的目标路由对象
    // from: 当前导航正要离开的路由
    // next: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

    if (to.meta.requiresAuth && !auth.isLoggedIn()) {
    next(‘/login’) // 如果需要登录且未登录,则跳转到登录页
    } else {
    next() // 否则,继续导航
    }
    })
    “`

  • 全局解析守卫: router.beforeResolve
    在所有组件内守卫和异步路由组件被解析之后调用。

  • 全局后置钩子: router.afterEach
    在每次路由跳转后执行,可以用于进行页面统计、埋点等操作。

    javascript
    router.afterEach((to, from) => {
    // to: 进入的目标路由对象
    // from: 离开的路由对象
    // 不会接受 next 函数也不会改变导航本身
    })

    * 路由独享守卫: beforeEnter
    在单个路由配置中定义,只在该路由被访问前执行。

    javascript
    const routes = [
    {
    path: '/admin',
    component: Admin,
    beforeEnter: (to, from, next) => {
    // ...
    }
    }
    ]

  • 组件内守卫:

    • beforeRouteEnter: 在渲染该组件的对应路由被 confirm 前调用。不能获取组件实例 this,因为当守卫执行前,组件实例还没被创建。
    • beforeRouteUpdate: 在当前路由改变,但是该组件被复用时调用。举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1/foo/2 之间跳转的时候,由于会渲染同样的 Foo 组件,因此组件实例会被复用。可以访问组件实例 this
    • beforeRouteLeave: 导航离开该组件的对应路由时调用。可以访问组件实例 this

    vue
    <script>
    export default {
    beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    next(vm => {
    // 可以通过 `vm` 访问组件实例, 这是该守卫独有的可以调用next时传入一个回调函数的方法
    })
    },
    beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。可以访问组件实例 `this`
    next()
    },
    beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
    if (answer) {
    next()
    } else {
    next(false)
    }
    }
    }
    </script>

3.2 next 函数

next 函数是导航守卫中最重要的一个参数,它用于控制路由的跳转行为:

  • next(): 继续导航。
  • next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
  • next('/')next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 router-linkto proprouter.push 中的选项。
  • next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

注意: 请确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。

四、Vue-Router 的高级用法

4.1 路由模式:hash vs history

Vue-Router 提供了两种路由模式:

  • hash 模式(默认): 使用 URL 的 hash(#)来模拟一个完整的 URL,当 hash 改变时,页面不会重新加载。

    javascript
    const router = new VueRouter({
    mode: 'hash', // 默认值,可以省略
    routes
    })

  • history 模式: 利用 HTML5 History API 的 pushStatereplaceState 方法来管理浏览器的历史记录,URL 看起来更像真实的 URL,没有 #

    javascript
    const router = new VueRouter({
    mode: 'history',
    routes
    })

    注意: history 模式需要服务器端的支持。如果服务器没有正确配置,当用户直接访问某个 URL 或刷新页面时,会出现 404 错误。

    服务器配置示例 (以 Nginx 为例):

    nginx
    location / {
    try_files $uri $uri/ /index.html;
    }

    该配置将所有未找到的请求都重定向到 index.html,由 Vue-Router 来处理路由。

4.2 路由元信息

可以通过 meta 字段在路由配置中添加自定义的元信息,然后在导航守卫中访问这些信息。

javascript
const routes = [
{
path: '/admin',
component: Admin,
meta: { requiresAuth: true }
}
]

javascript
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !auth.isLoggedIn()) {
next('/login')
} else {
next()
}
})

4.3 过渡效果

Vue-Router 可以与 Vue 的过渡系统结合使用,为路由切换添加动画效果。

“`vue

“`
更复杂的过渡效果可以设置不同的进入和离开动画, 动态过渡, 基于数据的过渡等等. 具体参考Vue的过渡系统文档: https://cn.vuejs.org/v2/guide/transitions.html

4.4 滚动行为

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

javascript
const router = new VueRouter({
routes,
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
})

scrollBehavior 方法接收 tofrom 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。

这个方法返回滚动位置的对象信息,长这样:

  • { x: number, y: number }
  • { selector: string, offset? : { x: number, y: number }} (offset 只在 2.6.0+ 支持)

如果返回一个 falsy 的值,或者是一个空对象,那么不会发生滚动。

更高级的用法可以根据不同的路由返回不同的滚动行为, 甚至异步滚动, 具体用法参考官方文档: https://router.vuejs.org/zh/guide/advanced/scroll-behavior.html

4.5 路由懒加载

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

“`javascript
const Foo = () => import(‘./Foo.vue’)

const routes = [
{ path: ‘/foo’, component: Foo }
]
“`

只需要将路由配置中的 component 属性改为一个返回 Promise 的函数,该 Promise 应该 resolve 组件本身。

五、总结

Vue-Router 是构建 Vue.js 单页应用的强大工具,它提供了丰富的功能和灵活的配置选项,可以满足各种复杂的路由需求。通过本文的详细介绍,相信您已经对 Vue-Router 有了更深入的了解。掌握 Vue-Router 的正确姿势,可以帮助您构建出用户体验更佳、性能更优的单页应用。

希望本文对您有所帮助!如果您有任何问题或建议,欢迎留言讨论。

发表评论

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

滚动至顶部