你点的“刷新”是假刷新?前端路由的瞒天过海术
为什么单页应用切换页面时浏览器没有真正刷新地址栏变了页面却没白一下今天我们来拆穿前端路由的“魔术”——它根本没去服务器要新页面而是自己偷偷换了内容。看完这篇你也能实现一个自己的前端路由。前言你有没有注意过现在很多网站比如知乎、B站、Github点开一个新页面地址栏变了但页面没有那种“白屏-加载-闪现”的过程而是瞬间切换内容。这就像你走进一家餐厅菜单上写着“换桌”你以为换了个房间结果服务员只是把你桌上的桌布换了。这就是前端路由干的“好事”。它让页面看起来跳转了实际上只是JS在背后偷偷换了DOM地址栏的变化也是骗你的。今天我们就来揭开这个魔术的奥秘顺便自己写一个简单的路由。一、什么是前端路由传统网站点击链接会向服务器请求一个新HTML浏览器刷新整个页面。这叫后端路由。单页应用SPA里所有页面逻辑都在一个HTML里。切换“页面”时不会请求新HTML而是JS擦掉旧内容画上新内容。同时通过某种手段改变浏览器的地址栏URL让用户感觉像换了个页面。这就是前端路由。前端路由的实现依赖两个“戏法”改变URL但不刷新页面监听URL变化并渲染对应组件二、Hash模式带#号的“假跳转”早期前端路由用的是hash也就是URL里#后面的部分。改变#后的值不会触发页面刷新也不会向服务器发请求。浏览器自己会记录历史前进后退可用。// 改变hashwindow.location.hashhome;// 监听hash变化window.addEventListener(hashchange,(){consthashwindow.location.hash.slice(1);// 去掉#renderPage(hash);});比如https://example.com/#/home你改成#/about页面不会刷新但hashchange事件会触发你可以在回调里根据hash渲染不同内容。优点兼容性好IE也能用。缺点URL有个丑陋的#服务端无法捕获#后面的内容因为#之后的部分不会发到服务器。三、History模式看起来像真的HTML5新增了pushState和replaceState可以改变URL路径同样不刷新页面。加上popstate事件监听就能实现干净的路由没有#。// 改变URL添加一条历史记录history.pushState({page:home},Home,/home);// 替换当前历史记录不新增history.replaceState({page:about},About,/about);// 监听前进后退window.addEventListener(popstate,(event){conststateevent.state;// pushState时传的数据renderPage(location.pathname);});优点URL干净像真实多页面。缺点需要服务端配合——因为刷新页面时浏览器会按真实路径请求服务器如果服务器没配置会404。解决方案所有路由都返回同一个HTML即index.html。四、手写一个迷你前端路由我们来实现一个最简单的Hash路由包含三个“页面”首页、关于、404。navahref#/home首页/aahref#/about关于/aahref#/nothing不存在/a/navdividapp内容会变/divfunctionrenderPage(path){constappdocument.getElementById(app);if(path/home){app.innerHTMLh2 首页/h2p欢迎来到我的网站/p;}elseif(path/about){app.innerHTMLh2 关于/h2p这是一个前端路由演示/p;}else{app.innerHTMLh2❌ 404/h2p页面不存在/p;}}// 监听hash变化window.addEventListener(hashchange,(){consthashwindow.location.hash.slice(1);// 去掉#renderPage(hash||/home);});// 页面加载时执行一次window.addEventListener(load,(){consthashwindow.location.hash.slice(1);renderPage(hash||/home);});就这么几行你已经实现了一个前端路由。当然实际框架里的路由更复杂嵌套路由、动态参数、路由守卫等但核心原理就是监听URL变化 渲染对应组件。五、前端路由与后端路由的区别特性后端路由前端路由请求方式每次跳转都请求服务器不请求服务器JS切换内容刷新页面会重新下载HTML会刷新但需要服务端配合history模式首屏加载只加载当前页面通常要加载所有JS可代码分割用户体验有白屏、闪烁切换流畅SEO友好较差需SSR或预渲染六、常见坑点与解决方案1. History模式刷新404配置Nginx将所有路由指向index.htmllocation / { try_files $uri $uri/ /index.html; }2. 路由跳转但页面不滚动单页切换时滚动条位置可能保留在上一个页面的位置。需要在路由变化后手动window.scrollTo(0, 0)。3. 动态路由参数比如/user/:id你需要从路径中提取id。可以用正则或简单分割functionmatchRoute(path,routePath){constpathPartspath.split(/);constroutePartsroutePath.split(/);if(pathParts.length!routeParts.length)returnnull;constparams{};for(leti0;ipathParts.length;i){if(routeParts[i].startsWith(:)){params[routeParts[i].slice(1)]pathParts[i];}elseif(routeParts[i]!pathParts[i]){returnnull;}}returnparams;}七、总结前端路由让单页应用切换页面时不刷新体验流畅。Hash模式靠#hashchange兼容性好但URL丑。History模式靠pushStatepopstateURL干净需服务端配合。原理很简单监听URL变化 → 根据路径渲染不同内容。现代框架React Router、Vue Router都是在此基础上增强。下次再看到地址栏变了但页面没白你就可以自信地说“哼不过是在演我。”封面图生成建议主视觉一个魔术师站在舞台中央一只手拿着一张写着“/home”的卡片另一只手从帽子里变出一张“/about”卡片帽子旁边有浏览器地址栏的图标带#号或/path。背景是浏览器窗口轮廓。配色深蓝色背景代表技术感亮黄色或红色作为点缀魔术道具。文字大标题“前端路由你看到的刷新是假的”副标题“单页应用的瞒天过海术”。风格扁平插画 轻微的3D阴影带点幽默感。你可以用Midjourney或DALL·E提示词“A magician on stage with a browser address bar behind him, holding a card that says ‘/home’, pulling another card ‘/about’ from a hat, dark blue background, flat illustration style, humorous, tech concept.” 尺寸16:9。如果你喜欢今天的“魔术揭秘”点个赞让更多人看到。明天我们将聊聊Webpack的Loader和Plugin原理从零理解构建工具的核心。我们明天见