Hash 模式?history 模式?
router 作为单页应用中重要的组成,有两种模式供我们选择,那么这两种模式的区别如何,分别的优势又在哪里?
前言
这次的 why what or how 主题:单页应用的 router 模式,Hash、History是什么?
router 作为单页应用中重要的组成,有两种模式供我们选择,那么这两种模式的区别如何,分别的优势又在哪里?
想要深入对比两种模式的区别,我们首先要知道什么是路由?
什么是路由?
路由定义如下:路由为 URL 的路径,也就是 URL 中的 path 部分,一个路由对应一个具体的资源,可能是文件,可能是数据。
在单页应用出现之前,路由已在服务端被广泛使用,那么前端何时开始有了路由?
伴随着浏览器性能的提升、网速加快、Ajax 技术的成熟,部分前端程序员发现,使用 JavaScript 在前端维护一个模板,然后在用 Ajax 去服务端获取数据,就能够将模板和数据拼凑成一个动态的页面呈现给用户。随着这项技术的日渐成熟,一个问题渐渐的显示出来:如果 JavaScript 端维护了几个不同模板,用以在不同的状态下的呈现,该如何去维护这个状态?
路由!服务器就是用路由来区分不同资源,只需要把路由的概念引入前端开发中,用路由来标志这些状态!
那么如何引入?或者说如何实现?
模拟
路由的样子:一个形如 <path>?<query> 的字符串。
那么该如何存储这个路由呢?页面生成之初,我们所拥有的只有 URL。如果一开始就需要确定页面的状态,那就只能用这个 URL。
首先分析下 URL 的组成:
<protocol>://<token>@<host>:<port>/<path>?<query>#<hash>
除去服务器需要使用的,能使用的仅剩下了 hash。
虽然在浏览器中 hash 为一个锚点值,但却拥有以下特性:
hash值改变会记录历史记录。- 当 
hash值未匹配到元素时,页面并不会发生滚动。 hash值为一个任意的字符串。hash的改变并会不让浏览器刷新页面。
那么现在再来看一个前端路由所需要的特性
- 需要记录历史。
 - 一个类似 
/a/b/c的字符串。 - 路由改变不需要浏览器重新获取资源(也就是不刷新页面)。
 
这两者似乎是生来就该呆在一起,简直是完美契合。
有了思路,那我们使用 hash 来模拟一个简单的前端路由,如下:
http://test.com/app#/page/123
# 号前为需要请求服务器路由,# 后为前端路由。
看起来一切都挺完美?服务器浏览器各自处理各自的部分,JavaScript 中包含了所有的页面模板,以及路由映射表。但这就是最终的版本了吗?
是的!至少在 pushState、replaceState 出现之前是。那么这两个 Api 效果是如何?为何会有这两个 Api?
history Api
先来看看官方定义:
| API | 作用 | 
|---|---|
pushState | 
向浏览器的历史记录中添加一条历史,同时将地址栏改为相应的地址,但却并不会导致浏览器刷新。 | 
replaceState | 
直接替换地址栏的链接,不会产生新的历史,同样不会导致浏览器刷新。 | 
需要注意的是,这两 API 仅能改变 URL 的 <path>?<query> 部分,并不能改变其余部分。
看起来挺符合前端路由的概念的,甚至可以说为了配合前端路由诞生了这两个 API。但是问题真的解决了吗?
一切问题隐藏在用户主动刷新上。虽然说这两 API 屏蔽了浏览器的刷新,但用户主动触发的刷新却不能控制,思考以下场景:
- 用户打开 
http://test.com/app。 - 用户点击按钮导航到 
http://test.com/app/page/123由于是API控制,不会导致浏览器发出对应地址的请求。 - 用户主动刷新了页面。
 - 浏览器发出 
http://test.com/app/page/123的请求,但服务器并没有对应的资源,导致404。 
那么如何解决?由于问题发生在服务器,那只能在服务器上解决,nginx 重定向,或是让 http://test.com/app/page/123 返回和 http://test.com/app 一样的结果,那么其他页面怎么办,单页应用可不止有两个页面!写正则过滤请求!
优缺点
在说明优缺点时,把使用 hash 实现前端路由称为 hash 模式,把使用 history Api 称为 history 模式。
hash 模式
使用 hash 模拟路由,URL 格式如下
<protocol>://<token>@<host>:<port>/<path>?<query>#<path>?<query>
优点
- 职责明确,各部分处理各部分的事,互不干扰。
 - 服务器不需要支持实现
 JavaScript获取简单
缺点
- 带有 
#/不精简,hash部分的分享可能有问题。 
history 模式
直接使用 URL 的 <path>?<query>,格式与 URL 一致
<protocol>://<token>@<host>:<port>/<path>?<query>#<hash>
优点
URL简单,分享容易。hash可以匹配ID。
缺点
- 服务器需要支持。
 - 职责不明确,项目废弃后还需要去服务将相应的过滤去掉,会埋下一些隐藏 
bug。 - 如果有二级目录存在,请求过滤会比较复杂。
 
小结
个人喜欢使用 hash 模式,原因如下
- 开发简单。
 - 现在分享基本上使用二维码,不用直接分享链接。
 - 不会污染服务器。
 - 锚点?什么是锚点?我不认识
 
最后,惯例提几个问题
- 什么是路由?
 - 前端路由存在意义?
 hash模式与history模式的区别?
最后的最后
该系列所有问题由 minimo 提出,爱你哟~~~