(解析)单页应用路由实现没那么难--Vue

news/2023/12/10 16:13:03

前言

单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序。简单来说就是用户只需要加载一次页面就可以不再请求,当点击其他子页面时只会有相应的URL改变而不会重新加载。
我们可以将实现路由的过程分为两部分:

  1. 更新URL页面不刷新
  2. 监听URL的变化,执行页面替换逻辑

现在主流有2种实现方案:

  1. history.pushState等触发popstate事件
  2. location.hash的变化触发hashchange事件

接下来我们一步一步看Vue-router如何实现的

Vue-router源码解剖

构造器

请各位同学翻到 src/index.js 第18行

export default class VueRouter {static install: () => void;static version: string;app: any;apps: Array<any>;ready: boolean;readyCbs: Array<Function>;options: RouterOptions;mode: string;history: HashHistory | HTML5History | AbstractHistory;matcher: Matcher;fallback: boolean;beforeHooks: Array<?NavigationGuard>;resolveHooks: Array<?NavigationGuard>;afterHooks: Array<?AfterNavigationHook>;constructor (options: RouterOptions = {}) { this.app = nullthis.apps = []this.options = optionsthis.beforeHooks = []this.resolveHooks = []this.afterHooks = []this.matcher = createMatcher(options.routes || [], this)let mode = options.mode || 'hash'this.fallback = mode === 'history' && !supportsPushState && options.fallback !== falseif (this.fallback) {mode = 'hash'}if (!inBrowser) {mode = 'abstract'}this.mode = modeswitch (mode) {case 'history':this.history = new HTML5History(this, options.base)breakcase 'hash':this.history = new HashHistory(this, options.base, this.fallback)breakcase 'abstract':this.history = new AbstractHistory(this, options.base)breakdefault:if (process.env.NODE_ENV !== 'production') {assert(false, `invalid mode: ${mode}`)}}}

构造器接收一个options参数

clipboard.png

默认mode为 "hash",如果显示传入参数mode为"history",则进行 是否支持的"history"的判断

    this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false

supportsPushState方法 里面 判断了 是否为浏览器环境且当前浏览器版本支持history

clipboard.png

options.fallback用来控制路由,在设置了mode为"history"但是当前浏览器环境不支持"history"的情况下是否应该回调判断,并重新设置mode为"hash"。
设置fallback为false本质上是为了让"router-link"在IE9上可以完整的页面刷新,如果是在hash模式下面不支持SSR,设置为false,会让那些在ie9服务端渲染的app更好用。(这段话没具体应用过)

clipboard.png

之后根据不同的mode,来执行不同的方案

clipboard.png

HTML5History

clipboard.png

构造器

构造器接收2个参数,router和base
router是在定义新路由的时候创建的对象
base如果没有传,则为undefined,
"?"这是通过一个名为flow的外部工具为javascript加上强类型检查的功能,不影响编译和运行。直接无视就好。
调动History的构造方法,History为 HTML5History,HashHistory,AbstractHistory的超类

clipboard.png

在History的构造方法中,

clipboard.png

如果base为undefined,查找是否有base的元素,有就赋值,没有就'/'
之后

const expectScroll = router.options.scrollBehaviorconst supportsScroll = supportsPushState && expectScrollif (supportsScroll) {setupScroll()}

判断路由参数,是否控制路由页面滚动条行为

clipboard.png

监听popstate事件,跳转

clipboard.png

clipboard.png
获取当前location的值之后,

clipboard.png
进行路由的更新,比如当前的History对应哪个路由

clipboard.png

clipboard.png
Html5History也添加了go,push,replace等方法用来路由跳转,

clipboard.png
先保存滚动条状态,之后可以使用history的自带方法进行地址的改变
clipboard.png

更多详情请见MDN

未完待续

HashHistory

构造器

clipboard.png
调动History的构造方法,History为 HTML5History,HashHistory,AbstractHistory的超类
判断当前hash地址

clipboard.png

如果开头不是/#,将当前location按照hash格式化

clipboard.png

根据href获取当前hash,如果没有匹配到'#'返回空字符串。
初始化地址栏hash后

clipboard.png

clipboard.png

监听popstate事件,替换路由,控制滚动条行为

导航守卫

clipboard.png

在registerHook将设置的守卫入栈
在每次跳转的时候,递归守卫集合,将触发的守卫进行解析和执行。

clipboard.png

clipboard.png

AbstractHistory

构造器

clipboard.png
相对于上两种方法,AbstractHistory看起来要简单很多,这种模式是用于 Node.js 环境的,一般场景也就是在做测试的时候。但是在实际项目中其实还可以使用的,利用这种特性还是可以很方便的做很多事情的。(我没有用过)
因为不涉及和浏览器地址相关记录关联在一起;整体流程依旧和 HashHistory 是一样的,只是这里通过数组来模拟浏览器历史记录堆栈信息。

clipboard.png

更新历史堆栈信息,更新当前所处位置
等等,除了不使用浏览器的history对象,其他的和html5history模式差不多。

小结

vue-router的源码剖析到这里就结束了,大概流程是这个样子,得益于开发人员代码的简洁性及可读性,我们阅读起来障碍还是没有那么多,难度也没有那么大,整体逻辑不复杂,但是想要把很多不复杂的细节,整合到一起,认真到细节,才是程序设计的美学。


https://dhexx.cn/news/show-363158.html

相关文章

adb怎么判断是否有root权限,并更改system/app内容

一、首先判断root权限&#xff1a; adb root 结果&#xff1a; C:\signapp>adb root restarting adbd as root # 说明有root权限 &#xff0c;若是adbd cannot run as root in production builds 则说明没有root权限 二、更改system/app内容 一般情况下system/app都会出现r…

HBase 是列式存储数据库吗

在介绍 HBase 是不是列式存储数据库之前&#xff0c;我们先来了解一下什么是行式数据库和列式数据库。 行式数据库和列式数据库 在维基百科里面&#xff0c;对行式数据库和列式数据库的定义为&#xff1a;列式数据库是以列相关存储架构进行数据存储的数据库&#xff0c;主要适合…

关于spring-cloud-config配置中心远程拉取有时失败问题记录

2019独角兽企业重金招聘Python工程师标准>>> 在使用spring-cloud-config时&#xff0c;如果我们在持续的开发过程中&#xff0c;对远程仓库内容发生改动时&#xff0c;经常会出现c.s.e.MultipleJGitEnvironmentRepository : Cannot pull from remote the working tr…

python 多进程

pythn 多进程 python中的多线程无法利用多核优势&#xff0c;若想要充分地使用多核CPU的资源&#xff08;os.cpu_count()查看&#xff09;&#xff0c;在python中大部分情况需要使用多进程。Python提供了multiprocessing模块用来开启子进程&#xff0c;并在子进程中执行我们定制…

在采集程序中增加定时发送邮件以及关机处理的实现

我们在采集特定数据的时候&#xff0c;往往需要耗费较长的时间&#xff0c;有时候因为一些事情&#xff0c;不可能长久的在电脑前等待结果&#xff0c;那么需要程序在一段时间后自动给我们发送邮件等通知&#xff0c;以及执行退出程序或者关机等处理善后工作&#xff0c;以节省…

细说OC中的load和initialize方法

OC中有两个特殊的类方法&#xff0c;分别是load和initialize。本文总结一下这两个方法的区别于联系、使用场景和注意事项。Demo可以在我的Github上找到——load和initialize&#xff0c;如果觉得有帮助还望点个star以示支持&#xff0c;总结在文章末尾。 load 顾名思义&#xf…

Git 提交的正确姿势

Git 提交的正确姿势&#xff1a;Commit message 编写指南 SCOP范围 middleware core config plugin test type范围 Git 每次提交代码&#xff0c;都要写 Commit message&#xff08;提交说明&#xff09;&#xff0c;否则就不允许提交。 $ git commit -m "hello world&quo…

监控各项服务

2019独角兽企业重金招聘Python工程师标准>>> 比如有三个服务, 为了减少故障时间,增加监控任务,使用linux的 crontab 实现. 步骤: 1,每个服务写一个ping接口 监控如下内容: 1,HouseServer 是否正常运行,所以需要增加一个ping的接口 ; http://house.yhskyc.com/ping 2…

Ubuntu18、Ubuntu20、Ubuntu22部署zabbix6.0

目录 一、简介 二、安装、部署过程 ​1、下载安装包&#xff0c;并安装插件 a、Ubuntu18 b、Ubuntu20 c、Ubuntu22 2、创建数据库 3、编辑MySQL的配置文件&#xff0c;并导入架构和数据 a、首先修改配置文件 b、导入架构和数据 4、修改zabbix的配置文件&#xff0c;并…

Android开发学习笔记:Intent的简介以及属性的详解

一.Intent的介绍 Intent的中文意思是“意图&#xff0c;意向”&#xff0c;在Android中提供了Intent机制来协助应用间的交互与通讯&#xff0c;Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述&#xff0c;Android则根据此Intent的描述&#xff0c;负责找到…