这是一篇基础文章,讲述一些浏览器里面历史记录栈管理的相关内容。写这个的起因,源于我最近想研究pushState,看看用它来实现SPA会遇到哪些问题,而pushState最终影响的就是浏览器历史记录栈里面的内容,所以就花了点时间琢磨了一下浏览器是如何管理历史记录栈的。因为在研究的过程中,发现了一些曾经不曾注意到一些要点,所以就记录下来了。
demo地址:http://liuyunzhuge.github.io/blog/history/demo1.html
这个demo用于进行本文后面内容涉及到的相关测试,假如你也感兴趣的话,建议每次要测试一个新的问题时,都在新选项卡里面打开这个demo,而不是从一个已经打开过网页的选项卡里面打开;因为已经打开过网页的选项卡,它的历史记录栈里面已经包含了之前访问的网页记录,所以会对你要测试的问题结果产生影响。
浏览器会对同一个窗口(选项卡)中访问的网页进行记录,不管我们是通过以下哪种方式改变网页,浏览器都会把改变后的网页记录下来,以便通过浏览器的前进和后退按钮,能够快速的切换到已经访问过的网页:
1)直接在地址栏输入网页地址;
2)通过网页内的超链接点击,跳转到其它网页;但是不能是在新窗口中打开的链接;
3)通过脚本改变location.href跳转到其它网页;
4)通过表单提交跳转到其它网页;但是不能是提交到新窗口的表单。
总之,只要是在同一个窗口内,网页发生了跳转,浏览器都会记录。不过刷新除外,history对象的length属性可以查看当前窗口存储的历史记录总数,在前面的demo页面中,我把这个属性打印在页面上,只有网页改变的时候,这个属性才会变化;而刷新网页不会改变这个属性。
浏览器有一个数据结构来存储网页的历史记录,我把它称之为历史记录栈,因为它的结构跟栈的使用方式有些相似。
操作测试一:假如你复制前面的demo地址,然后在chrome浏览器下按以下步骤进行操作:
打开新选项卡;输入demo1.html;点击demo2.html;点击demo3.html;点击demo4.html;点击demo3.html;点击demo2.html;点击demo1.html。
浏览器会以下图类似的方式来存储以上的访问记录:
由于现在的浏览器都是多选项卡的模式,当你打开一个选项卡的时候,即使没有访问具体网页,浏览器也为这个选项卡创建好了BOM对象,比如history对象,然后把新选项卡的空白页作为历史记录里面的第一条记录。所以在上图中的最后一列可以看到8条记录,跟demo页面里显示的数字一样:
跟历史记录栈一起的,浏览器还有一个访问指针来表示当前网页在历史记录栈中的位置。默认情况下,当我们通过前面列举的方式改变网页地址的时候,都会把新的页面压入到历史记录栈的顶部,同时把指针指向到这个最新的网页,就如上面的图中所示,每次改变了页面,当前页面的指针始终指向的是历史记录栈最顶部的那条记录;当我们通过浏览器的前进后退功能(包括按钮,快捷,右键菜单等方式)或者是history提供的go/back/forward方法,都不会改变历史记录栈的内容,只会移动一下这个指针:
1)前进功能/go(1)/forward,只是让这个指针上移1个位置;
2)后退功能/go(-1)/forward,只是让这个指针下移1个位置;
3)go(n)让指针上移n个位置;go(-n)让指针下移n个位置。
浏览器根据移动后的指针位置,找到历史记录栈中的网页进行显示。假如接着操作测试一的结果,继续做以下操作。
操作测试二:点击7次浏览器后退按钮。
浏览器此时历史记录栈的存储情况就变成下面这个状态了:
虽然history.go(n)和history.go(-n)可以将指针移动到任意位置,但是当要移动到的位置超出了历史记录栈的位置范围时,指针就不会移动。所以在操作测试二的结果中,调用history.go(-100)和history.go(100)都是不会起作用的。
还有两种情况会改变历史记录栈的内容。
操作测试三:假如我们接着操作测试二的结果,点击三次前进按钮,让浏览器的历史记录栈进入到下面这个状态:
此时由于操作测试二和操作测试三都没有改变历史记录栈的内容,所以正确的话,页面上的历史记录统计应该还是8:
操作测试四:接着,我们做以下两步操作:点击demo2.html,点击demo3.html。这个时候页面上的历史记录统计变成了:
history.length已经改变了,说明历史记录栈的内容也变化了。只不过为什么变成6,而不是10(8+2)呢?看看此时浏览器历史记录栈的状态:
浏览器在往历史记录栈里面压入新的记录时,是直接在当前指针后面压入的,如果当前指针的后面,还有其它的记录项,都会被丢弃掉。这样就好理解为啥操作测试四之后的历史记录总数只有6个了。
浏览器对历史记录的管理还有一个要点就是对历史记录栈的存储总数有限制,chrome和firefox都是50。当历史记录栈的存储的量超出这个限制后,历史记录的存储就会采取滚动的方式存储,也就是新的记录会压入到栈的顶部,最底部的记录会从栈的底部移除出去。通过在demo页面里,不断地切换点击demo1,demo2,demo3,demo4,当达到一定次数的时候,页面打印的统计信息不再变化,就表示达到历史记录达到限制了。不过IE11,我点到100多,发现它还在变化,说明IE的限制可能更高,也可能没有。。
本文记录了一些浏览器关于历史记录管理的内容,可能有分析不到位的地方,欢迎大家的批评与指正。以上内容希望对有需要的朋友加深对history以及pushState的理解有所帮助,谢谢阅读:)
补充于2016-10-12:
网页锚点的变化,也会导致历史记录栈的更新,特性与前文描述相同。