聊聊React Router中的History

在学习React Router时,看到有关History有以下描述:html

React Router 是创建在 history 之上的。 简而言之,一个 history 知道如何去监听浏览器地址栏的变化, 并解析这个 URL 转化为 location 对象, 而后 router 使用它匹配到路由,最后正确地渲染对应的组件。

这段描述看的晕晕的,history究竟是个什么东西呢?这得从history API的出身提及。前端

history对象的诞生

咱们都知道一个URL表明了网络上惟一的一个资源。这个资源能够是一个页面,一张图片等等。在地址栏里输入一个url地址,浏览器就会将对应的资源展现出来。当在不一样的地址之间跳转时,咱们很天然地想要回退或者前进一个地址。为了实现这个功能,浏览器厂商定义了history对象。这时的history对象大体有go()forward()back()等方法,用于实现历史记录的访问、跳转。html5

使用 history API与浏览器历史记录进行交互

ajax的挑战

history 与 URL 就这样和谐的相处了一段时间,直到ajax技术的兴起。git

浏览器有一个限制:若是你改变了URL,甚至是经过脚本,它就得向服务器发送请求,刷新整个页面。这很耗时,也耗资源,由于有时候2个页面长得差很少,仅仅有一小块地方不一样。为了解决这个问题,ajax技术兴起,利用ajax,能够不改变URL,只向服务器请求须要变更的数据,而后在前端经过DOM操做实现页面的局部更新。github

ajax极大提升了页面加载速度与用户体验。不过它同时带来了一个问题:同一个URL地址,可能会展现不少不一样的信息,那么它做为惟一资源标识符的这个定义,在这里被破坏了。ajax

有没有一个一箭双鵰的办法呢?既能够实现页面的局部更新,又可以改变地址栏里的URL?浏览器

解决办法

井号方案

第一个跳出来解决问题的是URL中的##表明网页中的一个位置。它右边的字符就是页面中的位置标识符。例以下面的URL:服务器

http://www.example.com/index.html#print

就表明网页index.html的print位置。浏览器读取这个URL后,会自动将print位置滚动至对应的区域。为网页位置指定标识符,有两个方法。一是使用锚点,好比,二是使用id属性,好比<div id="print" >网络

#的威力在于它是用来指导浏览器动做的,对服务器端彻底无用。因此,HTTP请求中不包括#,改变#不触发网页重载。所以,若是咱们在使用ajax局部刷新页面时,同时改变URL#后面的标识符,就实现了局部刷新+改变URL了。学习

事实上,早前的twitter、google就是这么实现的。google甚至为此专门定义了一个搜索引擎优化的标识符#!。能够参考这篇文章:URL的井号

history方案

URL毕竟是用于资源分享的,带个#总感受有点别扭。若是不用#,有没有办法可以修改URL,同时又不向服务器发送请求呢?这就轮到history对象再次登场了。在HTML5规范中,W3C对history对象进行了一波升级:

HTML5 history API包括2个方法:history.pushState()和history.replaceState(),以及1个事件:window.onpopstate。

利用这2个方法,能够实现经过脚本修改浏览器中的URL地址,而不触发页面重载。采用这个方案,一个ajax请求过程是这样的:

  1. 经过脚本发送ajax请求到服务器,获取服务器响应数据
  2. 根据响应的数据操做DOM,更新页面内容
  3. 利用history.pushState()或replaceState()方法,修改地址栏中的URL

问题获得解决。

React Router的整合

在React Router中,将上述2个方案进行了整合,统一到了一个history库中。也就是咱们看到的HashHistoryBrowserHistoryHashHistory是上面井号方案的封装,BrowserHistory则是对History方案的封装。特别须要注意到是,BrowserHistory须要对服务器端改造。

为何?

考虑如下场景,咱们用React Router的BrowserHistory开发了一个单页面应用,主页地址以下:

http://www.mysite.com/index

在index中能够点击连接进入欢迎页,详细介绍页等,地址为:

http://www.mysite.com/index/welcome
http://www.mysite.com/index/detail

若是咱们在浏览器里直接输入主页面地址,而后经过页面里连接跳转到二级页面,通常是没有问题的。可是,若是在浏览器里直接输入http://www.mysite.com/index/welcome会发生什么呢?

浏览器会认为这是一个URL请求,向服务器端请求这个URL表明的资源。但咱们这是一个单页面应用,服务端只有一个index.html页面,没有跟这个地址匹配的资源,页面就会报错了。

所以,咱们须要稍微改造一下服务端,将全部这样的请求都指向index.html页面,由前台处理路由信息。

总结

#有话说:

为嘛,长得丑就要被淘汰吗!

参考资料

相关文章
相关标签/搜索