前言

本文是手写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 设置了一个 idid 的值为 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 的变化了,那么我们怎么监听路径的变化呢?我们可以使用 historypushState 方法,这个方法可以改变路径,然后我们就可以监听路径的变化了。

在看路径地址之前,我们先将基本的代码页面结构搭建一下,路径与之前的 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,好,这个时候我们的关键点就要来了,我们点击一下浏览器的前进与后退,我们可以看到我们的容器当中显示的是 homeabout,这样我们就解决了这个问题。

总结

到此为止,我们了解了如何监听 hash 与路径的变化,并且了解到了如何监听前进与后退的点击,hash 与路径的变化。

本篇文章就到这里,感谢大家的阅读,如果有什么不足的地方,欢迎大家指出,我会及时的进行修改。

手撕Vue-Router-知识储备的更多相关文章

  1. 【Vuejs】350- 学习 Vue 源码的必要知识储备

    前言 我最近在写 Vue 进阶的内容.在这个过程中,有些人问我看 Vue 源码需要有哪些准备吗?所以也就有了这篇计划之外的文章. 当你想学习 Vue 源码的时候,需要有扎实的 JavaScript 基 ...

  2. 「进阶篇」Vue Router 核心原理解析

    前言 此篇为进阶篇,希望读者有 Vue.js,Vue Router 的使用经验,并对 Vue.js 核心原理有简单了解: 不会大篇幅手撕源码,会贴最核心的源码,对应的官方仓库源码地址会放到超上,可以配 ...

  3. 前端MVC Vue2学习总结(八)——Vue Router路由、Vuex状态管理、Element-UI

    一.Vue Router路由 二.Vuex状态管理 三.Element-UI Element-UI是饿了么前端团队推出的一款基于Vue.js 2.0 的桌面端UI框架,手机端有对应框架是 Mint U ...

  4. python 全栈开发,Day91(Vue实例的生命周期,组件间通信之中央事件总线bus,Vue Router,vue-cli 工具)

    昨日内容回顾 0. 组件注意事项!!! data属性必须是一个函数! 1. 注册全局组件 Vue.component('组件名',{ template: `` }) var app = new Vue ...

  5. vue路由知识整理

    vue路由知识整理 对于单页应用,官方提供了vue-router进行路由跳转的处理.我们已经可以通过组合组件来组成应用程序,当你要把 vue-router 添加进来,我们需要做的是,将组件(compo ...

  6. [Vue 牛刀小试]:第十四章 - 编程式导航与实现组件与 Vue Router 之间的解耦

    一.前言 在上一章的学习中,通过举例说明,我们了解了 Vue Router 中命名路由.命名视图的使用方法,以及如何通过 query 查询参数传参,或者是采用 param 传参的方式实现路由间的参数传 ...

  7. NN入门,手把手教你用Numpy手撕NN(一)

    前言 这是一篇包含极少数学推导的NN入门文章 大概从今年4月份起就想着学一学NN,但是无奈平时时间不多,而且空闲时间都拿去做比赛或是看动漫去了,所以一拖再拖,直到这8月份才正式开始NN的学习. 这篇文 ...

  8. 8. Vue - Router

    一.Vue Router 的使用 JavaScript: 1.创建组件:创建单页面应用需要渲染的组件 2.创建路由:创建VueRouter实例 3.映射路由:调用VueRouter实例的map方法 4 ...

  9. 「vue基础」一篇浅显易懂的 Vue 路由使用指南( Vue Router 上)

    大家好,今天的内容,我将和大家一起聊聊 Vue 路由相关的知识,如果你以前做过服务端相关的开发,那你一定会对程序的URL结构有所了解,我没记错的话也是路由映射的概念,需要进行配置. 其实前端这些框架的 ...

  10. Vue大概知识体系和学习参考

    Vue大概知识体系和学习参考文档 官方文档学习,参考,借鉴地址:https://cn.vuejs.org/v2/guide/installation.html 菜鸟教程:https://www.run ...

随机推荐

  1. opencv-python中 boundingRect(cnt)以及cv2.rectangle用法

    矩形边框(Bounding Rectangle)是说,用一个最小的矩形,把找到的形状包起来.还有一个带旋转的矩形,面积会更小,效果见下图 首先介绍下cv2.boundingRect(img)这个函数 ...

  2. .NET周刊【7月第5期 2023-07-30】

    国内文章 PaddleSharp:跨越一年的版本更新与亮点 https://www.cnblogs.com/sdflysha/p/20230724-paddlesharp-in-a-year.html ...

  3. 七 APPIUM Android 定位方式(转)

    1.定位元素应用元素 1.1通过id定位元素 Android里面定位的id一般为resrouce-id: 代码可以这样写: WebElement element = driver.findElemen ...

  4. vite — 超快且方便的编译工具

    我们编写的代码,比如 ES6. TypeScript.react 等是不能被浏览器直接识别的,需要通过 webpack .rollup 这样的构建工具来对代码进行转换.编译. 但随着项目越来越大,需要 ...

  5. [ABC128E] Roadwork

    2023-01-14 题目 题目传送门 翻译 翻译 难度&重要性(1~10):4 题目来源 AtCoder 题目算法 区间覆盖,线段树,双堆 解题思路 可以将问题转化为区间覆盖问题和单点查询问 ...

  6. Git-更换服务器问题

    一.Permission denied (publickey) git指令出现Permission denied (publickey),是ssh key过期的问题,需要对ssh key进行更新,所有 ...

  7. 重复的dna序列

    DNA序列 由一系列核苷酸组成,缩写为 'A', 'C', 'G' 和 'T'.. 例如,"ACGAATTCCG" 是一个 DNA序列 . 在研究 DNA 时,识别 DNA 中的重 ...

  8. CodeForces 1388D Captain Flint and Treasure

    题意 给长度为\(n\)的序列\(a[n]\)和\(b[n]\),初始时\(ans=0\),有以下操作: \(ans=ans+a[i]\) 如果\(b[i]\neq-1\),则\(a[b[i]]=a[ ...

  9. Java语言与其环境:常见问题解答

    Java语言与其环境:常见问题解答 在本博客文章中,将深入探讨Java编程语言的特点和环境,解释一些常见的关于Java的疑问. Java语言的特点是什么? Java是一种高级编程语言,它具有以下几个主 ...

  10. KRPANO 最新官方文档中文版(持续更新)

    KRPano最新官方文档中文版分享,后续持续更新: http://docs.krpano.tech/ 本博文发表于:http://www.krpano.tech/archives/849 发布者:屠龙 ...