在现代前端开发中,前端路由几乎是单页应用(SPA)不可或缺的一部分。Vue Router、React Router 等流行工具对路由进行了很好的实现,本文将通过一个简单的 Hash 路由实现一个简单的前端路由系统,帮助更好的理解路由实现逻辑。
前端路由
前端路由的核心目标就是:在不刷新页面的前提下,根据 URL 的变化加载不同的页面内容。
常见的路由模式有两种:
- Hash 模式:基于 URL 的
#
号之后的内容,例如 /#/about
;
- History 模式:使用 HTML5 提供的
history.pushState()
API,URL 更“干净”,例如 /about
。
本文我聚焦于实现成本最低的 Hash 模式
路由。
手写一个 Hash 路由
下面是完整的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>手写前端路由</title> </head> <body> <nav> <a href="#/">首页</a> <a href="#/about">关于</a> <a href="#/contact">联系</a> </nav>
<div id="app"></div>
<script> class Router { constructor(routes) { this.routes = routes this.app = document.getElementById("app") window.addEventListener("hashchange", this.routeChange.bind(this)) window.addEventListener("load", this.routeChange.bind(this)) }
routeChange() { const hash = location.hash.slice(1) || "/" const component = this.routes[hash] if (component) { this.app.innerHTML = component() } else { this.app.innerHTML = "<h2>404 Not Found</h2>" } } }
const Home = () => "<h2>欢迎来到首页</h2>" const About = () => "<h2>关于我们</h2>" const Contact = () => "<h2>联系我们</h2>"
const routes = { "/": Home, "/about": About, "/contact": Contact, }
const router = new Router(routes) </script> </body> </html>
|
路由表
通过一个对象来维护路由与组件之间的映射关系:
1 2 3 4 5
| const routes = { "/": Home, "/about": About, "/contact": Contact, }
|
每个路径对应一个函数(组件),返回一段 HTML 字符串。
监听 URL 变化
使用原生事件监听器:
1 2
| window.addEventListener("hashchange", this.routeChange.bind(this)) window.addEventListener("load", this.routeChange.bind(this))
|
hashchange
:当地址栏的 #
部分变化时触发;
load
:页面首次加载时触发一次。
解析并渲染页面内容
1 2 3
| const hash = location.hash.slice(1) || "/" const component = this.routes[hash] this.app.innerHTML = component ? component() : "<h2>404 Not Found</h2>"
|
通过 location.hash
获取当前的 hash 值,去除 #
后再匹配路由表。
最终效果
点击顶部导航栏的链接时,页面内容会即时更新,无需刷新页面:
#/
→ 首页
#/about
→ 关于页面
#/contact
→ 联系页面
小结
前端路由的核心其实非常纯粹:监听 URL 变化 → 匹配路由表 → 渲染对应内容。
当然,实际项目中的路由功能远不止于此,比如嵌套路由、动态参数、路由守卫等,但这个手写例子是一个很好的理解路由的切入点。