Vue 技术探索:深入理解 vue-router

396 阅读6分钟

文章主题: 全面掌握 Vue-router 的使用和原理

发展历史

在 B/S(浏览器/服务器)架构开发中,传统的路由机制通常依赖后端路由,每次用户访问不同页面(如 index.html、detail.html、editor.html)时,浏览器都会向服务器发送请求,服务器返回对应的 HTML 文件。这种方式虽然简单,但随着应用规模的扩大,存在明显的问题,如页面刷新导致用户体验不佳、服务器压力增加等。

为了解决这些问题,前端导航(单页面应用,SPA)逐渐发展起来。在 SPA 中,整个应用加载在一个 HTML 页面内,路由由前端 JavaScript 框架(如 Vue.js、React、Angular)管理。用户在不同视图间切换时,不需要重新加载页面,而是通过动态更新页面内容,实现更流畅的用户体验。然而,SPA 也带来了一些挑战,如初次加载时间较长、SEO 优化困难等。为此,现代前端框架通常结合服务端渲染(SSR)和客户端渲染(CSR)技术<同构>, 兼顾性能和用户体验。

前端路由是单页应用(SPA)下的一种弥补传统文件即路径模式的方式。在传统的 B/S 架构中,每个页面都对应一个 HTML 文件,并通过浏览器的导航栏或超链接进行访问。但在单页应用中,整个应用加载在一个 HTML 页面内,所有页面的内容动态渲染,不再依赖于服务器返回的不同 HTML 文件。

前端路由通过在浏览器端管理 URL 和视图之间的映射关系,使得用户可以在不同的视图间进行切换,而不需要重新加载整个页面。这种方式实现了更流畅的用户体验,同时也降低了服务器的压力,但它需要前端框架提供的路由管理功能来实现。通过在 URL 中使用哈希路由或 HTML5 History API,前端路由能够捕获浏览器的导航事件,并根据路由规则动态加载对应的视图内容,从而实现页面间的无缝切换

vue-router

框架介绍

Vue Router 是 Vue.js 官方的路由管理器,用于构建单页面应用(SPA)。它允许开发者通过定义路由来将不同的 URL 地址映射到不同的 Vue 组件,实现了页面间的切换和导航。Vue Router 提供了丰富的功能,包括嵌套路由、路由参数、路由导航守卫、动态路由匹配、命名路由等,同时与 Vue.js 生态系统无缝集成,为构建复杂的前端应用提供了强大的支持。

官网网站

vue-router 3.x

官方网站:

v3.router.vuejs.org/zh/installa…

源码地址:

github.com/vuejs/vue-r…

vue-router 4.x

官方网站:

router.vuejs.org/zh/installa…

源码地址:

github.com/vuejs/route…

使用流程

安装引入实例化配置相关属性配置全局钩子在 vue 实例中注册

安装

pnpm install vue-router

使用

main.js

import Vue from 'vue'
import router from './router/router.js'
import App from "./app.vue";

new Vue({
    router, // 将 Vue Router 实例传递给 Vue 根实例
    render: h => h(App)
}).$mount("#app");

router.js

import Router from "vue-router"
import Vue from "vue"
Vue.use(Router) // 在 Vue 实例中注册 Vue Router 插件

import HomeView from "@/views/HomeView.vue"
const router = new Router({
    mode: 'history', // 路由模式 history | hash
    routes:[
        {
            path:"/",
            name:"home",
            component: HomeView
        },
        {
            path:"/home",
            name:"home",
            component: () => import("@/views/HomeView.vue","home") // 异步加载 & chunk
        },
    ]
})
// 路由守卫 | 路由独享的守卫 | 组件内的守卫
// 全局前置守卫: 在导航被确认之前调用
router.beforeEach((to,from,next)=>{})
// 解析守卫:在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
router.beforeResolve((to,from,next)=>{})
// 全局后置钩子
router.afterEach((to,from,next)=>{})


export default router

扩展知识

说说如何给同一个网站配置不同的域名?

正向代理: 正向代理像是一个用户找一个中介去帮忙获取信息,它代表用户向服务器请求资源。

反向代理: 反向代理则像是一个服务员,它代表服务器向用户提供服务,隐藏了真实的服务器细节。

能力

路由模式 | 路由元信息 | 路由守卫 | 动态路由 | 组件传参 | 重定向 & 别名 & 404

过渡动效 | 滚动行为 | 路由懒加载的三种方式 |

函数式导航

使用 router 的实例方法,通过编写代码来实现导航。

router.push(location, onComplete?, onAbort?)
router.replace(location, onComplete?, onAbort?)
router.go(n)
router.push({path: "/"})
router.push({name: 'home', params:{}, query:{}})

扩展

Browser History APIs: developer.mozilla.org/en-US/docs/…

params 不能使用的场景

router.push() 方法中,使用 {path: "/"} 的方式不能用 params。

这是因为在使用 {path: "/"} 时,路由跳转是通过路径来匹配路由,而不是通过路由名称(name)。而 params 是在使用路由名称(name)进行路由跳转时传递参数的一种方式。因此,当使用 {path: "/"} 时,无法传递 params 参数。

正确的做法是使用 {name: 'home', params:{}, query:{}},这样可以通过路由名称来跳转,并且可以传递 params 参数。

路由模式

在 Vue Router 中,hash 模式和 history 模式是两种不同的路由模式,它们主要区别在于 URL 的格式和对浏览器的兼容性。

hash 模式

在 hash 模式下,URL 中的路径会被表示为 #/ 后面的部分,例如:http://example.com/#/home

这种模式在旧版浏览器中具有良好的兼容性,因为在 URL 中的 hash 值的改变不会导致页面的完全刷新,只会触发 hashchange 事件,从而实现单页面应用的路由切换。

但 hash 模式的 URL 可读性较差,且在 SEO 方面可能不够友好。

history 模式

在 history 模式下,URL 中的路径会像正常的 URL 一样,不带有 # 符号,例如:example.com/home。

这种模式通过 HTML5 History API 来管理路由,使得 URL 更加清晰易读,同时也更符合传统网站的 URL 结构。

但 history 模式需要服务器端的支持,因为在 history 模式下,服务器端需要配置当访问的 URL 不是一个文件时,返回同一个 HTML 页面,以便 Vue Router 可以处理路由。

实现这两种模式用到两个重要的API: window.onhashchange()window.onpopstate() 。

思考问题: 什么情况下 URL 改变页面会刷新? 1. window.location 2. a 标签强制地址跳转(非hash改变)

即 hash 模式下, 通过改变URL中的 hash 值, 触发 onhashchange 监听 然后切换对应模块

而 history 模式, 通过 pushState() | replaceState() 改变当前路由状态, 触发 window.onpopstate() 从而实现模块切换

当浏览器处于 hash 模式下,URL 中的 hash 改变时,通过监听 ~~~~onhashchange~~~~ 事件,可以实现页面模块的切换,而不会导致页面的完全刷新。

在 history 模式下,通过使用 ~~~~pushState()~~~~ 或 ~~~~replaceState()~~~~ 方法改变当前的路由状态时,页面也不会完全刷新,只会更新对应的视图模块。

路由守卫

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

手写 Router

数据流图 | 数据结构 | 相关 API

4.x 升级能力 🚀🚀

TODO

面试题