前言
这次的 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
提出,爱你哟~~~