开宗明义 前端路由的本质:对 url 进行改变和监听,使 UI 与 url 保持同步。
当我们在浏览器地址栏输入 https://github.com/hk4114/How2Route
这个路径的时候,浏览器发起请求,服务器通过 url 从映射表中执行相应的函数,并将函数的执行结果返回给浏览器进行渲染。
这是传统 web 应用的交互方式。路由其实更像是一种请求。不同页面使用不同 url 从服务器加载对应资源渲染页面,如果跳转到新页面就会重新请求资源进行渲染。用户通过路由 url 和服务器 server 直接进行交互。
而在现在盛行的 SPA 单页应用来说,用户输入地址 url,向服务器发起请求,获取到的是整个应用的资源文件,一个 index.html
和打包过的 css/js。由前端 client 去判断该展示什么样的视图。
所以我理解:
如果将 web 应用比作 object。路由是一种状态 key。通过路由 key 展示不同视图/页面 value。
传统应用和单页应用的区别在于 通过路由返回相应视图/页面的逻辑是在哪里处理的。在前端 client 就是前端路由,在后端 server 就是后端路由。
为什么将路由放在前端?
维护过传统 web 应用,感觉有维护体验不佳:通过 controller 路由找到对应页面,最后发现有问题的模块 template 在其他地方,然后再按图索骥找对应的 jsp 文件再查看出问题的模型。改一个字段可能需要把整个项目都看一遍。
相对来说,后端 server 只提供接口,将路由交互,交给前端 client 处理,分工更明确。逻辑上也将数据和视图拆分出来:后端 server 和 前端 client 通过 ajax/fetch 进行交互。用户只是和前端 client 进行交互。
如何实现前端路由?
两步实现前端路由 /doge
- 监听 url 的改变,改变 UI 视图
- 改变 url
具体实现上有两种方式
- hash
baidu.com/#foo
- history
baidu.com/foo
hash 通过 window.addEventListener('hashchange')
这个事件,就可以监听到 hash 值的变化,驱动界面变化。
history 通过 history.pushState
让 url 改变,但是不重新加载页面,记录浏览器的历史,驱动界面发送变化。
原生
hash
监听 hashchange
可以通过操作 dom 的方式来处理
function onHashChange() { if (location.hash === '#/home') { vanillaHashRouterView.innerHTML = '<h2>Hash Router(Vanilla) Home</h2>' } else if (location.hash === '#/about') { vanillaHashRouterView.innerHTML = '<h2>Hash Router(Vanilla) About</h2>' } }
history
history.pushState
的方式需要拦截点击事件默认行为,点击时使用 pushState
修改 URL 并更新手动 UI,从而实现点击链接更新 URL 和 UI 的效果。
const linkList = document.querySelectorAll('.vanilla.history a[href]') linkList.forEach(el => el.addEventListener('click', function (e) { e.preventDefault() history.pushState(null, '', el.getAttribute('href')) onPopState() })) // 路由变化时,根据路由渲染对应 UI function onPopState () { switch (location.pathname) { case '/home': vanillaHisoryRouterView.innerHTML = '<h2>History Router(Vanilla) Home</h2>' return case '/about': vanillaHisoryRouterView.innerHTML = '<h2>History Router(Vanilla) About</h2>' return default: return } }
Router 在不同框架里结合各自框架的特性,具体到细节上有不同的实现方式。模仿实现一下效果。
参考资料:
- ssh_晨曦时梦见兮 深入探索前端路由,手写 react-mini-router from 掘金
- 动感超人 面试官:说说你对SPA(单页应用)的理解? from CSDN
- whinc Web 前端路由原理解析和实现 from github