1. 需求

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

博客首更地址 :github

2. 问题

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

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

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

下面介绍具体的方法。

3. 方法

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

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

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

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

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

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

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

// 路由构造函数 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) {                  // 刷新,不做其他操作