手撕Vue-Router-知识储备

前言
本文是手写Vue-Router的第一篇,主要是对Vue-Router的知识储备,为后面的手写做准备。
那么 VueRouter 怎么实现呢?要想实现 VueRouter,首先要知道 VueRouter 它的本质是什么。
VueRouter 的本质
VueRoute 的本质是什么?VueRouter 的本质就是根据 "不同的 hash 值" 或者 "不同的路径地址", 将不同的内容渲染到 router-view 中。
再过去,我学习 VueRouter 的时候,知道 VueRouter 有两种模式,一种是 hash 模式,一种是 history 模式。那么这两种模式有什么区别呢?
hash 模式和 history 模式的区别
如果是 history 模式,那么我们的路径就是这样的:http://localhost:8080/home,如果是 hash 模式,那么我们的路径就是这样的:http://localhost:8080/#/home。
了解了这些知识之后,所以实现 VueRouter 的核心关键点就在于如何监听 'hash' 或 '路径' 的变化, 再将不同的内容写到 router-view 中。
那么在实现 VueRouter 之前呢,我在给大家补充一下,如何监听 'hash' 或 '路径' 的变化。
如何监听 hash 或 路径 的变化
hash
首先我新建了一个 test.html 文件,然后在里面写了一个 div,然后给这个 div 设置了一个 id,id 的值为 html。
并且在页面当中添加了两个 a 标签,两个 a 标签的 href 分别跳转地址为,一个是 #/home,一个是 #/about。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="#/home">首页</a>
<a href="#/about">关于</a>
<div id="html"></div>
</body>
</html>
基本的结构我们搭建完毕,好了接下来我们怎么监听 hash 的变化呢?也非常的简单,其实在我们原生的 JS 当中,有一个 hashchange 事件,这个事件就是用来监听 hash 变化的(专门用于监听 hash 变化的)。
那么知道了监听 hash 变化的事件之后,我们怎么使用呢?我们可以给 window 绑定一个 hashchange 事件,然后在这个事件当中,有一个回调函数,主要 hash 变化之后,我们就可以在这个回调函数当中,获取到当前的 hash 值。
那么怎么验证它会执行这个回调函数呢,我们可以在这个回调函数当中,打印一下当前的 hash 值。
<script>
window.addEventListener('hashchange', () => {
console.log('当前的hash值发生了变化');
});
</script>
好了,我们打开浏览器,然后点击首页,我们可以看到控制台打印了一句话,说明我们的 hash 值发生了变化,看到这一点就可以验证我的一个说法。
接下来我们要做的就是将内容渲染到 div 中,我们先简单的来将 hash 值写入到 div 中。
window.addEventListener('hashchange', () => {
const currentHash = location.hash.slice(1);
document.querySelector('#html').innerHTML = currentHash;
});
我们打开浏览器,点击首页,我们可以看到 div 中的内容变成了 home,点击关于,我们可以看到 div 中的内容变成了 about。
将来我们是不是根据这个获取到对应的组件,然后将组件渲染到 div(某一个容器当中)中就可以了。
好了到这里我们的监听 hash 就可以,可以了之后还没完,可以了之后有没有这么一种情况,就是我们第一次打开页面的时候我们地址上面是没有 hash 值的,还有可能就是我们地址栏是有 hash 值的这种情况,是不是有可能,对吧,我们先来看看我们第一次打开页面的时候,有 hash 值我们的容器显示的是什么。

我们可以看到我们的容器显示的是空的,那么我们怎么解决这个问题呢?我们可以在页面加载的时候,手动的触发一次 hashchange 事件,这样我们就可以在页面加载的时候,将内容渲染到 div 中。
首先我们在 window 上面绑定一个 load 事件,然后在这个事件当中,我们手动的触发一次 hashchange 事件。
window.addEventListener('load', () => {
const currentHash = location.hash.slice(1);
document.querySelector('#html').innerHTML = currentHash;
});
我们打开浏览器,我们可以看到我们的容器当中显示的是 home,这样我们就解决了第一次打开页面的时候,我们的容器显示的是空的这个问题。
路径
到此为止,我们就可以监听 hash 的变化了,那么我们怎么监听路径的变化呢?我们可以使用 history 的 pushState 方法,这个方法可以改变路径,然后我们就可以监听路径的变化了。
在看路径地址之前,我们先将基本的代码页面结构搭建一下,路径与之前的 hash 是不一样的,所以我们这里的 a 标签就不能使用 href 属性了,路径我们可以给 a 标签绑定一个事件,绑定一个方法然后在这个方法当中来改变路径。
页面样式的基本结构代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a onclick="go('/home')">首页</a>
<a onclick="go('/about')">关于</a>
<div id="html"></div>
<script>
function go(path) {
}
</script>
</body>
</html>
定义了一个 go 方法,接收一个参数 path,接下来要做的事情就是根据这个 path 来改变路径,这个我们要怎么实现呢?这里我们可以借助一个 history 对象,在 history 对象当中有一个 pushState 方法,这个方法接收三个参数,第一个参数是 state,第二个参数是 title,第三个参数是 url。
pushState 方法参数:
- state:一个与指定网址相关的状态对象,popstate 事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填 null。
- title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填 null。
- url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
那么我们怎么使用呢?我们可以在 go 方法当中,调用 pushState 方法,然后将 path 传入到 pushState 方法当中,这样我们就可以改变路径了。
history.pushState(null, null, path);
好了,我们打开浏览器,点击首页,我们可以看到我们的路径变成了 http://localhost:8080/home,点击关于,我们可以看到我们的路径变成了 http://localhost:8080/about。
没问题之后,我们再将内容渲染到 div 中,我们可以在 go 方法当中,获取到当前的路径,然后将路径写入到 div 中。
document.querySelector('#html').innerHTML = path;
我们打开浏览器,点击首页,我们可以看到我们的容器当中显示的是 home,点击关于,我们可以看到我们的容器当中显示的是 about。
到此为止,我们就可以监听路径的变化了,好了知道这些内容之后,还有一个注意点需要给大家说一下:
注意点
我们先基于 IDEA 运行我们的项目,然后,点击一下首页这个时候我们的路径与容器内容都是 /home, 好,我们这个时候将地址复制一下,例如现在路径已经变为了 http://localhost:63342/home ,我们在点击一下关于,我们可以看到我们的路径变为了 http://localhost:63342/about, 好,这个时候我们的关键点就要来了:

正如上图所示,我们的路径变为了 http://localhost:63342/home, 但是容器的内容还是 about,这是为什么呢?所以说这个东西我们也需要进行同步一下,那么我们手动添加了路径那么它怎么知道我们有没有前进与后退呢?非常简单,其实在我们的原生 JS 当中,又有一个事件,这个事件就是 popstate 事件,通过这个事件,我们就可以监听到前进与后退的点击,通过这个事件监听了前进与后退的点击之后,它会执行一个回调函数,我们在这个回调函数当中,就可以处理之前的问题了。
更改我们的代码,我们可以在 window 上面绑定一个 popstate 事件,然后在这个事件当中,我们可以获取到当前的路径,然后将路径写入到 div 中。
window.addEventListener('popstate', () => {
document.querySelector('#html').innerHTML = location.pathname;
});
测试注意点
我们打开浏览器,点击首页,我们可以看到我们的容器当中显示的是 home,点击关于,我们可以看到我们的容器当中显示的是 about,好,这个时候我们的关键点就要来了,我们点击一下浏览器的前进与后退,我们可以看到我们的容器当中显示的是 home 与 about,这样我们就解决了这个问题。
总结
到此为止,我们了解了如何监听 hash 与路径的变化,并且了解到了如何监听前进与后退的点击,hash 与路径的变化。
本篇文章就到这里,感谢大家的阅读,如果有什么不足的地方,欢迎大家指出,我会及时的进行修改。
手撕Vue-Router-知识储备的更多相关文章
- 【Vuejs】350- 学习 Vue 源码的必要知识储备
前言 我最近在写 Vue 进阶的内容.在这个过程中,有些人问我看 Vue 源码需要有哪些准备吗?所以也就有了这篇计划之外的文章. 当你想学习 Vue 源码的时候,需要有扎实的 JavaScript 基 ...
- 「进阶篇」Vue Router 核心原理解析
前言 此篇为进阶篇,希望读者有 Vue.js,Vue Router 的使用经验,并对 Vue.js 核心原理有简单了解: 不会大篇幅手撕源码,会贴最核心的源码,对应的官方仓库源码地址会放到超上,可以配 ...
- 前端MVC Vue2学习总结(八)——Vue Router路由、Vuex状态管理、Element-UI
一.Vue Router路由 二.Vuex状态管理 三.Element-UI Element-UI是饿了么前端团队推出的一款基于Vue.js 2.0 的桌面端UI框架,手机端有对应框架是 Mint U ...
- python 全栈开发,Day91(Vue实例的生命周期,组件间通信之中央事件总线bus,Vue Router,vue-cli 工具)
昨日内容回顾 0. 组件注意事项!!! data属性必须是一个函数! 1. 注册全局组件 Vue.component('组件名',{ template: `` }) var app = new Vue ...
- vue路由知识整理
vue路由知识整理 对于单页应用,官方提供了vue-router进行路由跳转的处理.我们已经可以通过组合组件来组成应用程序,当你要把 vue-router 添加进来,我们需要做的是,将组件(compo ...
- [Vue 牛刀小试]:第十四章 - 编程式导航与实现组件与 Vue Router 之间的解耦
一.前言 在上一章的学习中,通过举例说明,我们了解了 Vue Router 中命名路由.命名视图的使用方法,以及如何通过 query 查询参数传参,或者是采用 param 传参的方式实现路由间的参数传 ...
- NN入门,手把手教你用Numpy手撕NN(一)
前言 这是一篇包含极少数学推导的NN入门文章 大概从今年4月份起就想着学一学NN,但是无奈平时时间不多,而且空闲时间都拿去做比赛或是看动漫去了,所以一拖再拖,直到这8月份才正式开始NN的学习. 这篇文 ...
- 8. Vue - Router
一.Vue Router 的使用 JavaScript: 1.创建组件:创建单页面应用需要渲染的组件 2.创建路由:创建VueRouter实例 3.映射路由:调用VueRouter实例的map方法 4 ...
- 「vue基础」一篇浅显易懂的 Vue 路由使用指南( Vue Router 上)
大家好,今天的内容,我将和大家一起聊聊 Vue 路由相关的知识,如果你以前做过服务端相关的开发,那你一定会对程序的URL结构有所了解,我没记错的话也是路由映射的概念,需要进行配置. 其实前端这些框架的 ...
- Vue大概知识体系和学习参考
Vue大概知识体系和学习参考文档 官方文档学习,参考,借鉴地址:https://cn.vuejs.org/v2/guide/installation.html 菜鸟教程:https://www.run ...
随机推荐
- 【算法】单调栈 & 单调队列学习笔记
1. 单调栈简介 1.1 前言 今天是 2023/1/15,一中寒假集训阶段性的结束了.集训的学习笔记可以在本人 blogs 的[算法]标签栏中找. 马上就要过年了,提前祝大家新年快乐! 1.2 什么 ...
- vue: 在页面中单独引入elment-ui
引入资源 首先引入Vue,之后引入element-ui. 引入组件 返回数据 全部代码 <!DOCTYPE html> <html lang="en"> & ...
- Linux 标准目录结构 FHS ——原文链接https://www.cnblogs.com/woider/p/6618295.html
因为利用 Linux 来开发产品或 distribution 的团队实在太多了,如果每个人都用自己的想法来配置文件放置的目录,那么将可能造成很多管理上的困扰.所以,后来就有了 Filesystem H ...
- 牛客小白月赛64 C题 题解
题目链接 题意描述 这一题的意思其实就是,让你构造一个\(n * k\)的矩阵,使得第 i 列的总和为 i ,同时使得:每一列的任意两个数之间的差不大于1,且任意两行之间的总和差不大于1. \(1 \ ...
- [postgresql]逻辑备份与还原
前言 Postgres提供pg_dump和pg_dumpall用于数据库逻辑备份. pg_dumpall:将一个PostgreSQL数据库集群全部转储到一个脚本文件中 pg_dump:可以选择一个数据 ...
- 让 GPT-4 给开源项目 GoPool Review 社区贡献者的 PR - 每天5分钟玩转 GPT 编程系列(5)
目录 1. 嘚瑟一下 2. 言归正传 2.1 GoPool 的第一个 PR 2.2 祭出 GPT-4 2.3 问问 GPT-4 怎么看这个 PR 2.4 让 GPT-4 重构代码 3. 打完收工 1. ...
- P1551 亲戚 && #569. 【例4-7】亲戚(并查集)
P1551 亲戚 题目链接:落谷 题目链接:TFLS OJ 落谷题解(具体分析见慎入潜出P239) #include<bits/stdc++.h> using namespace std; ...
- 《Linux基础》02. 目录结构 · vi、vim · 关机 · 重启
@ 目录 1:目录结构 2:vi.vim快速入门 2.1:vi 和 vim 的三种模式 2.1.1:一般模式 2.1.2:编辑模式 2.1.3:命令模式 2.2:常用快捷键 2.2.1:一般模式 2. ...
- Springboot多种字段copy工具比较
结论:推荐使用spring自带的copy工具,不能copy的手动set 1.springboot自带的BeanUtils.copyProperties package com.admin; impor ...
- vue3探索——pinia高阶使用
以下是一些 Pinia 的其他高阶功能: storeToRefs():响应式解构仓库,保证解构出来的数据是响应式的数据. 状态持久化:Pinia 并没有内置的状态持久化功能,但你可以使用第三方库或自定 ...