前端路由是怎么来的

你有没有想过,为什么现在的网站切换页面这么丝滑? 点一下菜单,内容立刻变了,页面却没刷新。这个看起来理所当然的体验,其实是前端发展很多年之后才有的成果。而前端路由,就是这段进化史里最关键的一环。

一、石器时代:每一次换页面都是一次深呼吸

有一天你打开一个老网站,点了一下"关于我们"。

就一下。

然后屏幕白了半秒,风扇开始响,页面从头加载,刚刚看到一半的内容没了,滚动条也回到了最上面。你站在那儿,多少会有点迷惑:我只是想看看别的内容,又不是把整个网站炸了,至于这么大动静吗?

可在很早很早的时候,这就是网页的正常打开方式。

那会儿的网站,根本没有前端路由这种东西。你点"首页""关于我们""联系方式",浏览器就老老实实去服务器那里再要一份新的 HTML。服务器收到请求,现做现发;浏览器拿到新页面,把旧页面整个替换掉。说白了,就是整个世界重建一次。

所以你会看到一种特别朴素的操作链路:

点链接 → 发请求 → 拿新 HTML → 整页刷新。

简单,直接,甚至有点笨。但那时候大家没觉得这有什么问题。网页嘛,不就该这样吗?你要换内容,就把整张纸重新印一遍;你要看别的页,就把上一页翻过去再发一本新的。没人会想:"能不能只改中间那一块,别把整个屋子都掀了?"

可用户是很诚实的。你点一下菜单,他给你整个页面重新加载;你只是想换个栏目,他连滚动位置都给你清空了。刚刚看新闻看到一半,下一秒回到顶部。刚刚填了一点表单,页面一刷新,字没了。那种感觉,就像你在图书馆里翻书,结果每翻一页,管理员都把你手里的书收走,重新发一本新的一样......挺礼貌,但也挺烦。

这里有个坑。

问题不只是"慢"这么简单。整页刷新意味着浏览器要重新解析 HTML、重新加载资源、重新渲染页面。哪怕你只是切换了一个很小的内容区域,背后也可能带着一整套重复劳动。对用户来说,是眼睛一闪,对机器来说,是又跑了一遍流程。你说离谱吧,它还真是当年的标准答案。

但那时候没有别的路子。

前端还没长出"我只改局部,不动全身"的本事。网页更像一张一张独立的纸,而不是后来我们熟悉的那种"壳子不变,里面换内容"的应用。你可以把它想成住酒店。你本来只是想从卧室走到客厅,结果流程是:先退房,再重新办理入住,再领一套新的房卡,再被告知"您的房间已经换了"。听起来像整活,但当时的网页就是这么干的。

所以石器时代的前端路由,严格来说,甚至还谈不上"路由"这两个字。大家只是照着浏览器和服务器最原始的规则在跑。链接指向哪里,就去哪里;页面是什么,就整页返回什么。没什么花活,也没什么想象力。你可以说它粗暴,但它确实稳定,稳定到让人一度以为世界就该如此。

可问题也正是在这里慢慢冒出来的。

如果只是换个内容区域,真的有必要把整个页面都重新加载吗?

二、偷偷换房间的人:AJAX 开始搞事情

一开始大家还能忍整页刷新,但有些场景真的开始让人破防。

比如聊天室。你刚打完一句"在吗",页面一刷新,这句话没了,人也没了。再比如邮箱,你点一封邮件,结果整个页面重新加载,列表回到顶部,还要再找刚刚那封。地图更夸张,拖一下、点一下就刷新,体验像在用拨号上网....

开发者那时候的感受很直接:不行,这玩意不能再这么搞了。

问题本质其实很简单,我们只是想要一小块数据变化,比如一条消息、一段列表更新,但浏览器每次都默认"你要换整张页面",于是就全量刷新。效率低,体验也差。

然后 AJAX 出现了。

AJAX 说白了就是让浏览器多了一种能力:不用整页跳转,也可以向服务器要数据。它不是换页面,而是"悄悄发请求,把数据拿回来,然后自己改页面的一小部分"。这里的"悄悄",就是重点,它不会打断当前页面的状态。

比如你在聊天室里打字,AJAX 在后台偷偷请求新消息,回来之后只更新那一小块聊天记录,输入框里的内容完全不动。页面不刷新,但内容在变。对用户来说,就是"怎么突然就更新了,但我啥也没动"。

体验一下子就变了。页面开始有"连续性"了,不再是一张张断开的纸,而更像一个持续运行的东西。

但问题也跟着来了。

因为页面没刷新,浏览器根本不知道你"换了内容状态"。在它眼里,你一直还停留在同一个 URL 下的同一个页面。

结果就是几个很现实的坑:

后退按钮开始不可靠。你以为你退一步是回到上一个内容状态,但浏览器只是简单回到上一个完整页面记录,而 AJAX 更新的内容,它根本没记录。

收藏夹也变得很尴尬。你收藏当前页面,结果别人打开,看到的是默认初始状态,而不是你当时看到的那个具体内容。

分享链接更是直接失真。你发给别人一个 URL,别人打开看到的不是你刚刚那一屏,而是"首页默认样子"。

你会发现一个很奇怪的现象:页面确实越来越像应用了,但 URL 还停留在"网页时代"。

浏览器这时候的逻辑其实很死板:URL 没变,就说明你没去新地方。它不理解什么"局部更新",也不理解什么"状态变化",它只认地址栏。

于是用户开始迷路。明明点了很多东西,但每次回头都像回到原点。

说人话就是:页面在变,但地址不记事。

问题抛出来就很尖锐了:

如果页面内容已经变了,但 URL 没变,那用户到底算不算到了一个新页面?

三、井盖下面的秘密:Hash 路由横空出世

前面那个问题卡住了大家很久。页面不刷新,体验是好了,可 URL 一动不动,浏览器就像什么都没发生过。你想后退,它不一定听话;你想分享,它也未必能给你一个准确的位置。于是开发者开始想,能不能既不刷新,又让地址栏动起来。

这时候,Hash 路由就冒出来了。

它的思路其实挺巧的。URL 里不是有个 # 吗?以前大家更多把它当成页面内部定位,比如跳到某个标题。后来有人发现,# 后面的内容变化,浏览器通常不会把它当成一次真正的页面请求,也就是说,服务器根本不会重新返回 HTML,但地址栏看起来又确实变了。这样一来,前端就可以自己根据 #/home#/user#/about 这些内容,决定页面该显示什么。

说人话就是,浏览器还以为你只是换了个"书签位置",其实前端已经偷偷把内容换掉了。

这个方案一出来,很多人松了一口气。因为它几乎同时解决了三个事:页面不刷新,URL 能变化,浏览器后退也终于有了点用武之地。你点到 #/user,地址栏变了;你按后退,浏览器还能回到上一个 hash 状态。虽然不算特别优雅,但至少能跑,能用,能把前面那个"地址不记事"的问题先按住。

可你猜怎么着,能用不代表好看。

Hash 路由最大的毛病,就是它太像一个临时补丁了。URL 里带着 #,总给人一种"这里本来不该长这样,但先这样凑合一下"的感觉。比如 example.com/#/product/123,你一眼看过去就知道,这网址后面像裂开了一条缝。用户可能不太在意,开发者看着多少有点别扭。更麻烦的是,# 后面的内容服务器根本收不到。它只负责把 example.com/ 这个页面给你,至于后面那段"路由状态",服务器完全不参与。

这就带来一个新问题:SEO 开始不舒服了。

搜索引擎更喜欢清晰、稳定、能被理解的 URL。你现在把真正的页面路径藏在 # 后面,搜索引擎看到的就只是一个很模糊的地址。它知道你访问了这个站,但不太知道你到底在访问哪个具体页面。对用户来说可能还能接受,对做内容站、产品站的人来说,这就有点难受了。

所以 Hash 路由像什么呢?像你在门牌号后面贴了一张便利贴。302 室是 302 室,但你还额外写了个"游戏房"或者"书房"。住户看得懂,物业看得也烦。它解决了问题,但方式很粗糙,像是先把井盖掀开,发现下面有水,就先往里垫了块木板。

不过历史就是这样,很多方案一开始都不体面。Hash 路由至少证明了一件事:URL 不一定非得等于整页刷新,它也可以变成前端自己管理状态的一部分。这个想法很重要,真的很重要。因为它把大家的注意力从"浏览器只能这样"带到了"浏览器还能不能再往前走一步"。

问题也就在这里慢慢变清楚了。

如果我们已经能让 URL 变化、页面不刷新,那有没有办法让 URL 看起来像真的页面路径,同时又不刷新?

四、浏览器终于开窍:History API 的出现

Hash 路由用久了之后,有点像一直贴着透明胶带过日子。能用,但总觉得哪儿不对劲。开发者开始盯着地址栏发呆:为什么我不能直接用 /home/user 这种正常路径?非要带个 #,看起来像系统没修完。

问题其实一直卡在浏览器的能力边界上:它以前只认两种状态,一种是整页刷新,一种是 hash 变化。除此之外的"只改 URL 不刷新页面",它不太支持。

直到 HTML5 出来,History API 才把这事补上。

它的核心就两个方法:history.pushState()history.replaceState()

说白了,就是允许你在不重新请求页面的情况下,直接修改浏览器地址栏里的 URL,并且还能把这次变化记录进历史栈。这样一来,你点 /user,页面不刷新,但浏览器真的"记住"了你去过 /user。后退、前进也终于能按正常逻辑走。

前端路由这才算真正进入现代模式。

你可以开始写看起来非常正常的路径:/home /user /order

没有 #,没有补丁感,URL 终于像一个真正的网站结构了。

那一刻开发者是挺开心的,有点像突然把房间装修完了,把所有临时线路都藏进墙里,表面终于干净了。

但问题很快又冒出来,而且是个更底层的问题。

你改了 URL,但服务器不知道。

浏览器一旦刷新,它不会管你前端写了什么逻辑,它只会做一件事:去服务器请求 /user 这个路径的资源。结果服务器一看这个请求,一脸问号:我哪有这个文件?于是直接 404。

这就是 History API 的第一个现实问题:它只管浏览器这一侧的行为,但没有帮你解决"刷新之后怎么办"。

所以你会看到一个很典型的现象:前端路由跑得好好的,一刷新就崩。开发者这时候才意识到,事情没那么简单。你在前端把路径改得再漂亮,服务器如果不配合,它照样给你打回原形。

说人话就是:浏览器开始配合你演戏了,但快递员还按旧地址送货。

这个坑挺经典的。你终于把门牌号改成了正常的样子,看起来像一栋真正的楼。但问题是,快递系统还是老逻辑:它看到 /user 就真的去找一栋叫 user 的楼,找不到就直接丢件。

所以 History API 解决的是"怎么优雅地改 URL",但没解决"服务器怎么理解这些 URL"。

它把前端路由往前推了一大步,但也顺手把一个更现实的问题推到了台前:

如果 URL 看起来是真的,那服务器和前端,到底谁来负责解释它?

五、当框架开始替你决定路径

History API 解决了"URL 怎么变得像真的"这个问题之后,事情并没有结束,反而进入了更麻烦的一步。

上一个问题其实已经很关键了:如果 URL 看起来是真的,那服务器和前端,到底谁来负责解释它?

答案慢慢变清楚了:在单页应用(SPA)里,这个责任开始明显偏向前端。

服务器的角色被压缩到极致,它只负责一件事:不管你访问 /home/user 还是 /order,都返回同一个 HTML 壳子。真正的页面内容解释权,交给前端路由系统。

前端拿到这个壳子之后,再根据 URL 去决定显示什么组件。于是路由不再只是"从 A 页面跳到 B 页面",而是变成了整个应用运行逻辑的入口。

但需求很快就不满足于"切页面"这么简单了。

比如你开始加登录控制。某些页面必须登录才能进,于是路由里多了守卫逻辑:进入之前先检查 token,没有就跳转登录页。再比如权限控制,不同用户看到的菜单不一样,路由要动态生成。还有缓存策略,有些页面切走之后不能销毁,要保留状态。甚至还有面包屑、嵌套路由、页面切换动画,每一项都在往路由系统里塞逻辑。

你会发现一个变化:路由不再只是导航,它开始管"应用怎么运行"。

跳转这件事变得越来越像机场安检。你不是点一下就过去了,而是要先验身份、查权限、决定路径、判断缓存状态,最后才允许你进入目标页面。

于是 Vue Router、React Router 这些东西就不再只是"切页面工具",而是变成了应用级的交通指挥中心。它知道你在哪一层嵌套结构里,要去哪个子页面,是否需要拦截,是否需要重定向,甚至要不要提前加载数据。

能力越来越强,配置也越来越长。

一个简单路由表,可能已经不再是三行配置,而是带守卫、元信息、懒加载、动态参数的一整套体系。有时候你改一个路径,不是在改配置,是在整个系统里找一个牵一发动全身的节点,稍微不小心就会影响一堆跳转逻辑。

说人话就是,路由开始变复杂了,复杂到不像"路由",更像一个小型操作系统的入口层。

但故事还没结束。

当大家已经习惯"路由即系统"的时候,又有人开始觉得:写这些配置是不是有点太烦了?一个页面要写一遍 { path, component },写多了真的像重复劳动。

于是 Next.js、Nuxt 这类框架开始换思路:既然你们总是一个页面对应一个路径,那干脆别写路由配置了,直接用文件结构来生成路由。

你创建一个 pages/user.vue,系统自动帮你生成 /user 路由。你建一个文件夹,它就变成嵌套路由。你删文件,路由也自动消失。

路由不再是你手写的规则,而是文件系统的映射结果。

这一层变化看起来很爽,开发者确实轻松了,不用再维护一大坨路由配置。但问题也随之出现:你对路径的控制能力变弱了。

以前你是自己设计路由结构的人,现在你更像是在配合框架的规则写目录结构。路径怎么长、怎么嵌套、怎么组织,不再完全由你决定,而是被文件系统的约定约束住了。

自由度被换成了效率。

于是整个演化链条慢慢清晰了:最开始是浏览器决定必须整页刷新,后来前端接管了页面切换,再后来路由接管了应用逻辑,现在连路由本身都开始被框架自动生成。

你会有一种微妙的感觉:我们一直在"优化路由",但优化到最后,路由这件事反而越来越隐形。

从最早的手动跳转,到 AJAX 偷偷更新,再到 Hash 补状态,再到 History API 改地址,再到 SPA 路由接管逻辑,最后文件系统直接替你生成路径。整个过程像是从"我决定去哪",慢慢变成"系统帮我决定怎么走"。

人还在写代码,但路径这件事,越来越不像人手动控制的东西了。

最后剩下的现实其实挺简单:当一切都自动化之后,我们确实不用再关心路由怎么写了,但也很容易忘了它到底在替我们做什么。

6、总结

我们打开浏览器,输入一个 URL,以为自己是在访问一个页面。

但回头看路由的变化,会发现大家一直在做同一件事:想更自由地决定"下一步去哪"。

最开始是服务器说了算,你走哪条路,它给你哪一站。后来浏览器参与进来,让"路径变化"变得更轻一点,但代价是过程有点别扭。再后来前端接管了状态,让变化不再打断连续性。再往后,路由把跳转、权限、缓存这些事情都收进来。直到最后,连路径怎么写,都被框架帮你安排好了。

控制权一直在换人,但表面上,你只是点了一下链接。

路径与目标这件事,其实一直没变。变的是"谁在解释你现在在哪"。

放大一点看,这件事有点像我们面对世界的方式。

我们以为自己在做选择,在规划路线,在一步步走向某个目标。但很多时候,真正决定方向的,不是某一个瞬间的点击,而是背后那些规则、环境、结构,它们在默默定义"你能去哪"。

路由这个词听起来很技术,本质却很像生活本身:它不是在回答"去哪",而是在不断调整"怎么定义去"。

我们总以为自己在导航,其实更多时候是在被重新导航。到达还是那个到达,但过程早就被改写了。

相关推荐
铁皮饭盒6 分钟前
用 Bun.cron 定时 7 月 7 日,为啥? 看图1
javascript
Coder_Shenshen1 小时前
西门子S7CommPlus协议鉴权算法原理与流程详解
网络·后端·算法
大圣编程1 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang1 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆2 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜3 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
geovindu3 小时前
python: Functional Options Pattern
开发语言·后端·python·设计模式·惯用法模式·函数式选项模式
负责的蛋挞4 小时前
异步HttpModule的实现方式
java·服务器·前端
卷无止境5 小时前
C++ 存储类说明符(Storage Class Specifier)大横评
c++·后端
用户019027581615 小时前
量化数据的 batch 接口有多好用?从 1 只到 500 只,批量拉数据的正确姿势
后端