URL - hash 的委屈

URL 都出发去服务器了,hash 却被浏览器一把拽了下来:“你不能去!”凭什么?更扎心的是,等了半天,却只被安排去找一个元素……

分享
URL - hash 的委屈

诞生

午夜的钟声已经响了 11 次。随着用户在浏览器地址栏输入 https://breeze.vin/what-is-url/#苏醒,一个 URL 被创建了。

这个 URL 身上还挂着一串小尾巴:# 后面的那段字符串,学名 fragment,大家更习惯叫它 hash。没有固定格式,也不会被发送给服务器——这就是 hash 全部的家当。

拦截

突然间,浏览器收到了 Enter 指令,“嗡嗡嗡” 整个浏览器大厅发出阵阵警报。

HTTP 服务已准备就绪,随时可以发送请求。” HTTP 服务自信满满地说道。

URL,快带上你的包裹,去 HTTP 那儿吧。” 浏览器老大说道。

“协议没少,地址没少,OK。” URL 一边整理一边说,“万事俱备,出发!”

“等等!” 浏览器老大像是发现了什么,大喝一声,大家都怔住了。

“你这个 hash 啊,说了多少次了,赶紧从 URL 上下来。你属于浏览器,服务器不会收到你。”

偷偷想去服务器看一眼的 hash,只能乖乖地从 URL 的口袋里出来,走到浏览器身边坐下。

“各部门继续。” 大哥一声令下,各部门又开始了有条不紊的工作。

DNS 查询完毕,服务器地址 127.0.0.1。”

TCP 通道建立成功,可以准备发送。”

URL 已进入休眠,随时可以发送。”

...

各部门都开始忙碌起来,只有 hash 呆坐在一旁,无所事事。

委屈

大概过了 10 毫秒,HTTP 服务确认报文正确发出,报告道:“报文已正确发出,等待结果。”

“好的,大家都休息休息吧。” 浏览器老大一声令下,大家都松了口气。随后转头看向一旁的 hash:“想去服务器?”

“嗯,那里我还没去过呢!” hash 就像个好奇宝宝。

“那里不接待你,这是我和服务器他们的约定,你去了也没用。”

“好吧。” 感受到了来自服务器和浏览器的嫌弃,hash 顿时有点委屈。

“等下有你发挥的地方,别丧气!” 浏览器老大在一旁打气。

大概又过了 5 秒,HTTP 服务满心欢喜地跑到浏览器跟前:“URL 回来了!”

“好的,赶紧把它给唤醒,这次怎么过了这么久,服务器这小子不行啊,小主人都等不及了。” 浏览器老大见响应回得这么慢,咕哝了一句。

尴尬的用处

又是一盆冷水泼下,URL 慢慢地醒了过来,抱怨道:“又来!!”

浏览器老大却没有管它,一头扎进了它一旁的包裹中去。

过了大概 10 毫秒的样子,浏览器老大叫来了 GPU,把网页显示了出来。

一旁的 hash 有点纳闷:不是说好了有发挥的地方吗,怎么页面都显示了,自己还呆在这!心中的委屈更深了,活像一个没人要的孩子。

hash,你过来一下,你的内容是什么来着?”

苏醒!” 听到浏览器的召唤,hash 突然有点兴奋,脱口而出。

“让我找找哈,在这,找到你了!” 浏览器老大自言自语道。

“找到什么了?”

“就是这个元素,这个元素的 ID苏醒。我让 GPU 把页面滚动到这。” 浏览器一边说着,一边对 GPU 下了一连串的命令。

“什么!我的任务就是匹配一个元素?”

“是啊!”

“什么嘛!我等了这么久就是为了匹配一个元素?”

“是啊!”

顿时,心头的委屈再也控制不住,hash 的眼泪夺眶而出。

“别呀,在这里你的用处虽然不多,但在 JavaScript 那儿还有大作用。最近很火的单页应用就会用到你,你去问问他。” 浏览器老大赶忙安慰道。

遇伯乐

hash 没有动静,浏览器老大叹了口气,叫来了 JavaScript

“怎么啦?这么委屈!” 被浏览器老大叫来的 JavaScript 说道。

“他们都欺负我,服务器不让我去,而我的存在就是为了匹配一个元素。” 话刚说出口,hash 觉得自己更没用了。

“不哭,他们不重视你,我重视,现在好多我的小弟都用到了你哦,你可是很强大的!” JavaScript 说得 hash 一愣一愣的。

“真的?”

“那当然。听过 AngularVueReact 吗?它们的路由方案都支持使用你,你可以找它们聊聊。” JavaScript 信誓旦旦地说,并叫来了 Vue

“小兄弟,怎么哭了?” 走过来的 Vue 问道。

“我感觉我好没用啊!” 说着,hash 哭得更大声了。

“怎么会,你在我这就和 URL 的地位差不多。你想想你就是一个字符串,是不是也可以拥有类似 <path>?<query> 的格式?”

“是啊,那又怎么样,还不是匹配 ID 为这个字符串的元素。”

NoNoNo。在我这儿,你不再只是匹配一个元素。前端可以根据你的内容切换视图,再通过网络请求获取数据。你看,我这里有好多组件,你携带的路径可以告诉我该显示哪一个。” 说着,Vue 从口袋里哗啦啦地掏出十几个组件。

“真的?我真的能指明你要使用哪个页面!”

“是呀,你看我的配置,这个是你的路径,这个是我的模板,是不是一一对应呀?” Vue 掏出配置表给 hash 看了看。

“好像是的诶,我真的可以做到吗?” hash 不太自信地问道。

“当然可以,你还可以给我查询参数呢!就和 URL 那小子一样,只不过他为服务器服务,你为我服务罢了。” Vue 一脸得意地说道:“我就是你的服务器!”

听着 Vue 的解释,hash 陷入了沉思:URL 的路径会参与服务器路由,而自己也可以被前端路由解析。先匹配组件,再去服务器获取数据,最后由 JavaScript 更新页面并交给浏览器显示,完全行得通!

“是啊,原来可以这么搞!” 想通的 hash 兴奋地喊道。

“是吧,你的作用还是很大的,匹配元素只是其中一小部分。” Vue 在一旁说道,“那好,我走了,还有一个 hash 等着我生成页面呢,下次再聊。”

Vue 走了,留下一脸兴奋的 hash,期待着下次合作。

抢生意

时间慢慢流逝,与 AngularVueReact 的合作逐渐增加了 hash 的信心,原来世界可以如此美好。

直到有一天,hash 发现:虽然大厅里的 Vue 忙得不亦乐乎,却始终没有光顾过自己。

是啊,最近好像这 3 位找自己的次数变少了,什么情况?

不安的 hash 赶忙找来 Vue 问道:“最近怎么了,单页应用不流行了吗?”

“不啊,还是很流行的,只不过...” Vue 在一旁揶揄道。

“发生什么事了?”

“那个,要不你找 JavaScript 大哥聊聊?”

发现事情不对劲,hash 赶忙去 JavaScript 那儿问道:“Vue 让我来找你,最近这是怎么了?”

“这个啊,好吧,就直接告诉你。” JavaScript 眼见事情已经瞒不住,说道:“HTML 那小子更新啦,我这儿又多了两个小弟,就是那两位:history.pushState()history.replaceState(),看到了吗?”

放眼望去,确实多了些生面孔。那儿有两人,和 AngularVueReact 忙得不亦乐乎。

“他们是干嘛的?” hash 有些担心地问道。

“它们可以修改地址栏里的路径和查询参数,但调用时不会立刻让浏览器发起页面导航请求。”

“这和我有什么关系?”

“你之前不是和 Vue 他们学了一招吗?叫什么单页应用的。”

“对啊,难不成他们也能做这个单页应用?”

“是啊,你看看他们控制的内容,是不是和你作为单页应用的字符串一模一样?”

听到 JavaScript 的回答,hash 心中一惊,一股凉意升起。

“这么干的话,页面被小主人主动刷新了咋办?虽然他们不会让浏览器大哥进行 HTTP 请求,但是刷新却会啊!” hash 反抗道。

“你说得不错,这就需要服务器配合:当用户直接访问或刷新前端路由时,服务器要把这些路径回退到同一个入口页面,再由 Vue 读取当前 URL 并恢复对应的视图。”

“但是,但是,我已经这么好用了,还不占用 URL 大哥的位置,为什么要这两啊!” hash 越看那两位和 Vue 卿卿我我的,心中越是嫌弃。

“想知道原因?”

“嗯。” hash 感到了丝丝不对劲。

“不能生气!答应了我就告诉你!”

“好!我不生气!”

“其实吧,也没什么,就是小主人们觉得用你的话,太长了,所以就想了这个办法。但是!” JavaScript 停顿了一下,“你也有你的优点。”

  1. 不占用 URL 中的其他位置
  2. 不要求服务器为前端路由配置入口页面回退
  3. JavaScript 获取和监听你的变化很方便
  4. 前端路由与服务器路由边界清晰,部署简单

“哦~ 原来是这样!我和他们两是竞争对手啊,那我要加油了!”

“呼~” JavaScript 长舒一口气,心想:这小子终于长大了,不哭哭闹闹了。

来自屏幕外的抱怨

“这个 URL 也太长了吧,让我怎么给别人。”

“用 history 模式吧,那个能精简 URL。”

“好,我试试”

“刷新报错啊,兄弟。服务器还没配入口页面回退,hash 模式确实省事多了。”

“你配置下你的 nginx 啊!把所有这个目录的请求转到同一个 HTML。”

“... 我这个目录是公用的,不能把所有的请求都转啊!”

“那就写正则过滤,或者放在一个单独的目录下!”

“我试试,不行啊,我的静态资源请求不到,什么鬼!”

“被你过滤掉了呗,你看你的 css 是不是返回了个 HTML?”

“我靠,还真是!算了我放弃,也就多了 #/ 服务器还是保持简单点,用 hash 模式吧!”

“你开心就好。”