Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浅谈 JS 中的 history 对象以及路由插件原理 #6

Open
KarthusLorin opened this issue Jun 26, 2020 · 0 comments
Open

浅谈 JS 中的 history 对象以及路由插件原理 #6

KarthusLorin opened this issue Jun 26, 2020 · 0 comments

Comments

@KarthusLorin
Copy link
Owner

简介

History对象最初设计用来表示窗口的浏览历史,但是,出于隐私方面的原因,History对象不再允许脚本访问已经访问过的实际URL。虽然,我们不清楚历史URL,但是,我们可以通过History对象的内置属性方法进行跳转。

对象属性

length

该属性代表着浏览器历史列表中的URL数量。初始值为1,如果当前窗口先后访问了两个网址,那该属性的值变为2。

history.length			// 1
// 访问了一个新的URL
history.length 			// 2

state

HTML5新增属性,返回一个表示历史堆栈顶部的状态的值,这是一种可以不必等待popstate 事件而查看状态的方式。

scrollRestoration

允许Web应用程序在历史导航上显式地设置默认滚动恢复行为。此属性可以是自动的(auto)或者手动的(manual)。

对象方法

go方法

go方法是History对象三个方法中的核心方法,通过go方法可以完美替代其他的两个方法。该方法接收一个可选参数,这个参数可以是number也可以是URL

ps:经过多次试验,传入URL参数貌似没有作用,有待后续研究

当使用number参数时,页面会跳转到History的URL列表的相对位置。比如当传入参数为-1,则相当于点击浏览器后退按钮的效果;当传入参数为1时,相当于点击浏览器前进按钮;当不传参数或者传入参数为0时,页面会刷新。

window.history.go(-1)			// 后退
window.history.go(1)			// 前进
window.history.go(0)			// 刷新
window.history.go()			// 刷新

tips:当传入的number对应的位置没有URL,则该条语句会静默失败

forward方法

forward方法可以加载历史列表中的下一个URL,类似于go(1),实现了点击浏览器前进按钮的效果。

window.history.forward()		// 前进
window.history.go(1)			// 前进

back方法

back方法可以加载历史列表中的上一个URL,类似于go(-1),实现了点击浏览器后退按钮的效果。

window.history.back()			// 后退
window.history.go(-1)			// 后退

tips:当URL队列中没有上一个URL时,back方法会失效;同理,当URL队列中没有下一个URL时,forward方法会失效。

HTML5新增方法

HTML5为History对象添加了两个新方法,pushStatereplaceState方法,用来在浏览历史中添加和修改记录。

pushState方法

pushState方法接收三个参数:

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

比如当前的网址是localhost:63342/1.html,使用pushState方法在浏览记录中可以添加一条新的记录:

window.history.pushState({params: 'aaa'}, null, '2.html')

输入这行语句后,浏览器地址栏中的URL变为了localhost:63342/2.html,但是无论这是不是一个真实网址,它都不会跳转,这只是一条历史记录。此时,可以通过state取到状态。

此时,如果你前往下个地址后点击后退按钮,页面将返回到localhost:63342/2.html,此时,也可以通过state属性取到状态。

window.history.state			// {params: "aaa"}

tips:如果pushState的第三个参数是一个跨域网址,控制台会报错,这主要是因为安全问题,防止不法分子伪装URL

window.history.pushState(null, null, 'www.baidu.com')		// 报错

replaceState方法

该方法基本和pushState一致,但是不同的是,该方法会直接替换当前的历史记录。

路由插件的原理

传统的History用法是操纵浏览器进行前进或后退的跳转,用处不是很大。但是,HTML5新增的方法为其带来了脱胎换骨的变化。

众所周知,vue-router等一众路由插件实现的功能是更新页面的视图,但是却不重新请求页面,也就是说,其实,他们并没有实际进行了跳转,而是修改了页面的DOM并通过修改页面的URL来模拟跳转。

在HTML5之前,页面路由只有hash模式,而HTML5中History对象的新增方法,带来了另一种模式:history模式。

hash模式

在HTML5之前,vue-router是通过修改URL的hash值(URL中#开始的字符串,不了解的同学可以看我的上一篇文章)来达到修改页面URL并生成历史记录,但却不会重新请求页面。所以,不使用vue-router中history模式的情况下,你会发现你的路径前总会有一个#

比如,你在vue-router中设置的路径是/b,在你的想象中,路径应该是http://localhost:8080/b,但是,现实很骨感,实际路径是:http://localhost:8080/#/b,原因就是因为没开启history模式的情况下,vue-router是通过hashchange事件来监听URL中hash的改变并通过修改hash来模拟路径的变化。

由于通过window.location.hash修改hash是会有历史记录产生的,所以,在SPA中,依然可以通过后退、前进按钮来控制路由的跳转。

hash模式最大的优点是兼容性强,可以兼容一众老式浏览器。而它最大的缺点是,页面URL中一直挂着一个难看的#,这一点连vue-router的官网也对其进行了吐槽。

有需求就有功能,所以,当HTML5发布后,又有了history模式。

history模式

看到这里,如果认真看了pushState方法的同学应该已经差不多明白了,vue-router的history模式就是通过HTML5中History对象的pushState方法进行模拟的。

当vue-router每次需要跳转页面时,页面DOM的修改方式和时机并没有改变,和hash模式一样。但是,修改URL的方式改变了。此时,有了pushState方法,可以不用修改丑陋的hash模拟而是直接在历史记录中添加一条新的URL。

那么,没有了hash,如何监听URL的改变呢?HTML5还提供了一个popstate事件,当用户点击前进、后退按钮,或者调用backforwardgo方法时触发,可以监听URL的改变。

在这里提一句,使用history模式,就连路由传值都有更好的方式——使用pushState的第一个参数进行传值,使用History的state属性进行取值。

当使用了history模式时,使用vue-router跳入/b时,此时的页面URL不是丑陋的http://localhost:8080/#/b,而是预料之中的http://localhost:8080/b

// hash模式
window.location.href			// http://localhost:8080/#/b

// history
window.location.href			// http://localhost:8080/b

总结

虽然,history模式提供了完美的URL显示,但是,正所谓鱼和熊掌不可兼得,兼容性和美观也不可兼得。只有兼容了HTML5的浏览器(IE10+)才能使用history模式,不然,就老实的继续使用hash模式吧。

所以,使用何种模式,还是取决于软件的兼容性,如果不需要兼容低级浏览器,那就放心大胆的使用history模式吧!

@KarthusLorin KarthusLorin changed the title 浅谈 history 对象以及路由插件原理 浅谈 js 中的 history 对象以及路由插件原理 Jun 26, 2020
@KarthusLorin KarthusLorin changed the title 浅谈 js 中的 history 对象以及路由插件原理 浅谈 JS 中的 history 对象以及路由插件原理 Jun 26, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant