React Router提供的三种History, 之前认识得比较肤浅, 只是从字面上去比较, 经过实践后, 有了新的认识
browserHistory
browserHistory是文档重点推荐的: 它提供了更干净的url, 同时也是借助了History API来操作浏览器历史.
借助History API操作浏览器历史意味着什么呢? 这就意味着, 每次改变路由, 浏览器上的地址会改变, 也即window.location
会改变, 同时window.history
也会改变
此时的this.props.router.goBack()
相当于window.history.back()
, 而浏览器的后退按钮及移动端的硬件返回也是调用了window.history.back()
,
则在浏览器上, 可以前进/后退按钮来跳转至下一个/上一个路由, 这与用户平时访问网页的体验一致, 的确值得推荐
不过它有个缺点, 就是它开发出来的应用, 需要配置服务器
这就意味着, 使用browserHistory编译出来的静态文件, 必须通过http服务器才能访问, 本地直接通过file协议是无法正常访问的. 这是个注意点
这一点, 对于普通web应用来说, 无关紧要, 因为服务机上的静态资源一般都会有个服务器(如Ngnix)来提供的, 但对Hybrid应用就是不是了. 如果想把静态文件放到手机里, 直接在手机里打开, browserHistory编译的文件是会报错的.
因此, 如果需要编译出来的文件在本地打开, 则不能使用browserHistory
下面给出http服务器的生产示例配置, 以Nginx为例
1 | location / { |
它还有个缺点, IE8/IE9不支持局部刷新, 而下面两种则可以
createMemoryHistory
createMemoryHistory编译出来的静态文件, 可以本地打开, 不过它把路由历史放到内存里, 不会修改浏览器历史, 也不会改变浏览器地址
window.location
的pathname
始终不变则意味着, 假如首页点击后显示详情页, 无法把详情页的地址分享出去, 因为浏览器的地址仍然是首页的地址
同时, 页面上必须提供返回按钮, 因为浏览器的返回已不可靠. 前进后退必须通过react-router的api来实现
hashHistory
hashHistory具有以上两者的优点: 编译文件可以本地打开; 每一个路由都有单独的url, 可以使用浏览器的前进/后退按钮
它的缺点是, url”不干净”, 带着一串hash值. 如example.com/#/somepath?_k=kfeu3k
, #/somepath?_k=kfeu3k
就是window.location.hash
.
以上面的示例为说明, 它路由改变时, window.location.pathname
始终是不变的, 变的是hash值:
- 如果下一个路由与当前路由不同, 则
#
后面的的部分全部改变 - 如果下一个路由与当前路由相同, 则
=
后面的部分改变
也就是说, 除了前进/后退, 用户先一次点击同一个链接, 浏览器的url都是不同的
服务端渲染
文档上说, 服务端渲染就要使用browserHistory, hashHistory是做不到的. 可是, 我实验了一下, 并非如此啊, 服务端渲染根本与使用哪种历史没关系啊, 只是一定要有个nodejs服务器而已, 这里可能是个疑问点
实践
最后在实际项目中, 我使用的是createMemoryHistory.
场景是这样的: 对方用与我们完全不同的技术栈开发了一个app, 现在是要在其首页加一个icon, 点击后跳到我们的首页. 因为对方会把我们的首页资源放到手机里, 在本地打开, 并非通过http协议来访问, 所以不能用browserHistory.
那么, 为什么不使用hashHistory呢?
毕竟我们的页面是属于 “外来页面”, 对方要求在我们的页面里, 无论跳转了几个页面, 只要想返回, 都能返回到他们的首页.
考虑到我们的页面上已有返回按钮, 再添加一个按钮, 不好看, 而且还要考虑硬件返回的情况, 另外就是, 该app不会有分享链接的功能. 综上考虑, 我就选择了createMemoryHistory. 因为此时路由的改变记录是在内存的, 不会改变浏览器历史, 因此在我们的页面里, 无论嵌套有多深, history.back()
返回的都是对方的首页. 而手机的硬件返回调用的是history.back()
, 则对方直接操作手机就可以达到目的了, 我们也不需要在页面中多添加一个返回按钮