实现一个前端路由,www.129028.com金沙如何实现浏览器的前进与后退?

日期:2020-05-07编辑作者:Web前端

时间: 2019-09-09阅读: 194标签: 路由1. 需求

写在前面:通常 SPA 中前端路由有2种实现方式:

如果要你实现一个前端路由,应该如何实现浏览器的前进与后退 ?

window.history location.hash

  1. 问题

下面就来介绍下这两种方式具体怎么实现的

首先浏览器中主要有这几个限制,让前端不能随意的操作浏览器的浏览纪录:

一.history

没有提供监听前进后退的事件。不允许开发者读取浏览纪录,也就是 js 读取不了浏览纪录。用户可以手动输入地址,或使用浏览器提供的前进后退来改变 url。

1.history基本介绍

所以要实现一个自定义路由,解决方案是自己维护一份路由历史的记录,从而区分 前进、刷新、回退。下面介绍具体的方法。

window.history 对象包含浏览器的历史,window.history 对象在编写时可不使用 window 这个前缀。history是实现SPA前端路由是一种主流方法,它有几个原始方法:

  1. 方法

history.back() - 与在浏览器点击后退按钮相同 history.forward() - 与在浏览器中点击按钮向前相同 history.go - 接受一个整数作为参数,移动到该整数指定的页面,比如go相当于back相当于刷新当前页面 如果移动的位置超出了访问历史的边界,以上三个方法并不报错,而是静默失败

目前笔者知道的方法有两种,一种是在数组后面进行增加与删除,另外一种是利用栈的后进先出原理

在HTML5,history对象提出了 pushState 方法,这两个方法可以用来向历史栈中添加数据,就好像 url 变化了一样,这样就可以很好的模拟浏览历史和前进后退了,现在的前端路由也是基于这个原理实现的。

3.1 在数组最后进行 增加与删除

2.history.pushState

通过监听路由的变化事件 hashchange,与路由的第一次加载事件 load ,判断如下情况:

pushState 方法向历史栈中写入数据,其第一个参数是要写入的数据对象,第二个参数是页面的 title, 第三个参数是 url 。

url 存在于浏览记录中即为后退,后退时,把当前路由后面的浏览记录删除。url 不存在于浏览记录中即为前进,前进时,往数组里面 push 当前的路由。url 在浏览记录的末端即为刷新,刷新时,不对路由数组做任何操作。

stateObj :一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。 title:新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填null。 url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。

另外,应用的路由路径中可能允许相同的路由出现多次(例如 A - B - A),所以给每个路由添加一个 key 值来区分相同路由的不同实例。

关于pushState,有几个值得注意的地方:

注意:这个浏览记录需要存储在 sessionStorage 中,这样用户刷新后浏览记录也可以恢复。

pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应,只有当触发前进后退等事件时浏览器才会刷新

笔者之前实现的 用原生 js 实现的轻量级路由 ,就是用这种方法实现的,具体代码如下:

这里的 url 是受到同源策略限制的,防止恶意脚本模仿其他网站 url 用来欺骗用户,所以当违背同源策略时将会报错

// 路由构造函数function Router() { this.routes = {}; //保存注册的所有路由 this.routerViewId = "#routerView"; // 路由挂载点 this.stackPages = true; // 多级页面缓存 this.history = []; // 路由历史}Router.prototype = { init: function(config) { var self = this; //页面首次加载 匹配路由 window.addEventListener('load', function(event) { // console.log('load', event); self.historyChange(event) }, false) //路由切换 window.addEventListener('hashchange', function(event) { // console.log('hashchange', event); self.historyChange(event) }, false) }, // 路由历史纪录变化 historyChange: function(event) { var currentHash = util.getParamsUrl(); var nameStr = "router-history" this.history = window.sessionStorage[nameStr] ? JSON.parse(window.sessionStorage[nameStr]) : [] var back = false, // 后退 refresh = false, // 刷新 forward = false, // 前进 index = 0, len = this.history.length; // 比较当前路由的状态,得出是后退、前进、刷新的状态。 for (var i = 0; i  len; i++) { var h = this.history[i]; if (h.hash === currentHash.path  h.key === currentHash.query.key) { index = i if (i === len - 1) { refresh = true } else { back = true } break; } else { forward = true } } if (back) { // 后退,把历史纪录的最后一项删除 this.historyFlag = 'back' this.history.length = index + 1 } else if (refresh) { // 刷新,不做其他操作 this.historyFlag = 'refresh' } else { // 前进,添加一条历史纪录 this.historyFlag = 'forward' var item = { key: currentHash.query.key, hash: currentHash.path, query: currentHash.query } this.history.push(item) } // 如果不需要页面缓存功能,每次都是刷新操作 if (!this.stackPages) { this.historyFlag = 'forward' } window.sessionStorage[nameStr] = JSON.stringify(this.history) }, }

3.history.replaceState

以上代码只列出本次文章相关的内容,完整的内容请看原生 js 实现的轻量级路由,且页面跳转间有缓存功能。

replaceState 和pushState的区别就在于它不是写入而是替换修改浏览历史中当前纪录,其余和 pushState一模一样

3.2 利用栈的 后进者先出,先进者后出 原理

4.popstate事件

在说第二个方法之前,先来弄明白栈的定义与后进者先出,先进者后出原理。

定义:每当同一个文档的浏览历史出现变化时,就会触发popstate事件。

3.2.1 定义

注意:仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用JavaScript调用back、forward、go方法时才会触发。另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。

栈的特点:后进者先出,先进者后出

用法:使用的时候,可以为popstate事件指定回调函数。这个回调函数的参数是一个event事件对象,它的state属性指向pushState和replaceState方法为当前URL所提供的状态对象。

举一个生活中的例子说明:就是一摞叠在一起的盘子。我们平时放盘子的时候,都是从下往上一个一个放;取的时候,我们也是从上往下一个一个地依次取,不能从中间任意抽出。

5.history实现spa前端路由代码

因为栈的后进者先出,先进者后出的特点,所以只能栈一端进行插入和删除操作。这也和第一个方法的原理有异曲同工之妙。

a.htmlb.html // 注册路由 document.querySelectorAll.forEach(item => { item.addEventListener('click', e => { e.preventDefault(); let link = item.textContent; if (!!(window.history && history.pushState)) { // 支持History API window.history.pushState({name: 'api'}, link, link); } else { // 不支持,可使用一些Polyfill库来实现 } }, false) }); // 监听路由 window.addEventListener('popstate', e => { console.log({ location: location.href, state: e.state }) }, false)

本文由www.129028.com金沙发布于Web前端,转载请注明出处:实现一个前端路由,www.129028.com金沙如何实现浏览器的前进与后退?

关键词:

js查找和筛选的几种方式www.129028.com金沙

时间: 2019-09-06阅读: 108标签: 循环一、filter() 过滤数组 时间: 2019-11-29阅读: 153 find(); 创建一个新的数组,新的数组中的...

详细>>

www.129028.com金沙a标签调用js的几种方法

时间: 2019-09-10阅读: 249标签: 标签 a标签点击事件方法汇总 a 标签的 href 属性用于指定超链接目标的 URL,href属性的值可...

详细>>

www.129028.com金沙:Vue 开发必须知道的 36 个技巧

时间: 2019-09-08阅读: 153标签: 技巧前言 Vue 3.x 的Pre-Alpha 版本。后面应该还会有 Alpha、Beta等版本,预计至少要等到 2020 年...

详细>>

HTTPS为什么是安全的?【www.129028.com金沙】

时间: 2019-09-07阅读: 202标签: 安全 二、HTTP与HTTPS的区别 1、https协议需要到ca申请证书 2、http是吵文本传输协议,信息是...

详细>>