React-router和Vue-router底层实现原理
React-router和Vue-router中有两种路由管理的方式,一种通过 HTML5 History API 实现,另外一种通过Hash实现。
一、通过 HTML5 History API 实现
通过 HTML5 History API 实现前端路由管理,是一种无需页面刷新即可切换视图(即单页应用,SPA)的常用方式。它主要利用了 history.pushState()、history.replaceState()和 popstate事件来实现无刷新 URL 变更和路由控制。
下面将详细介绍如何使用 HTML5 History API 实现一个简单的路由管理系统。
1、HTML5 History API 简介
HTML5 History API 提供了以下三个核心方法/事件:
history.pushState(state, title[, url])
- 向浏览器历史记录栈中添加一条新记录。
- 不会触发页面刷新或跳转。
- url是可选的,可以是相对路径或绝对路径(但必须同源)。
history.replaceState(state, title[, url])
- 替换当前历史记录,而不是新增。
- 用途:比如更新当前页面的状态但不新增历史记录。
window.onpopstate事件
- 当用户点击浏览器的前进/后退按钮时触发。
- 可以通过 event.state获取之前通过 pushState/replaceState 设置的状态。
⚠️ 注意:这些 API 只修改 URL 和历史记录,不会自动加载页面内容或处理路由逻辑,需要开发者手动监听并执行相应的视图渲染逻辑。
2、基本思路
要实现一个基于 History API 的路由系统,通常需要以下几个部分:
- 监听 popstate 事件:处理浏览器前进/后退。
- 定义路由表:存储路径与对应处理函数的映射。
- 实现路由跳转函数(如 navigate):使用 pushState改变 URL 并渲染对应视图。
- 拦截 <a>标签等链接点击事件:阻止默认跳转行为,使用自定义路由逻辑。
- 初始化:根据当前 URL 渲染对应视图
3、一个简单的实现示例
下面是一个基于原生 JavaScript 的简单路由管理器实现:
3.1. HTML 示例
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><title>HTML5 History API 路由示例</title>
</head>
<body><nav><a href="/home">首页</a><a href="/about">关于</a><a href="/contact">联系</a></nav><div id="app">正在加载...</div><script src="router.js"></script>
</body>
</html>
3.2. JavaScript 路由管理器(router.js)
// router.js// 简单的路由表:路径 => 处理函数
const routes = {'/home': () => renderView('首页内容'),'/about': () => renderView('关于我们'),'/contact': () => renderView('联系方式'),
};// 默认路由(404)
const notFound = () => renderView('404 - 页面未找到');// 渲染视图的函数
function renderView(content) {const app = document.getElementById('app');app.innerHTML = `<h1>${content}</h1>`;
}// 路由匹配与执行
function handleRoute() {const path = window.location.pathname; // 如 /home, /aboutconst routeHandler = routes[path] || notFound;routeHandler();
}// 自定义导航函数:改变URL并渲染页面
function navigate(path) {history.pushState({}, '', path); // 不修改标题,只改URLhandleRoute(); // 执行对应的视图渲染
}// 监听浏览器前进/后退
window.addEventListener('popstate', handleRoute);// 拦截页面中的 <a> 标签点击,使用路由跳转而不是整页刷新
document.addEventListener('click', function (e) {if (e.target.tagName === 'A' && e.target.getAttribute('href').startsWith('/')) {e.preventDefault(); // 阻止默认跳转const path = e.target.getAttribute('href');navigate(path);}
});// 页面初次加载时,根据当前路径渲染
document.addEventListener('DOMContentLoaded', handleRoute);
4、关键点解析
4.1、navigate(path)函数
使用 history.pushState()修改 URL,但不会刷新页面。
然后调用 handleRoute()来根据新的 URL 渲染对应内容。
4.2、popstate事件
当用户点击浏览器的前进/后退按钮时,会触发此事件,此时需要重新根据 window.location.pathname渲染页面。
4.3、拦截 <a>标签
阻止默认的页面跳转行为,使用自定义的 navigate()方法进行路由切换,保持单页体验。
4.4、初始化渲染
在页面加载(DOMContentLoaded)时,根据当前 URL 执行一次路由匹配和视图渲染。
5、进阶优化方向(可选)
上述示例是一个非常基础的路由实现,实际项目中你可能还需要考虑以下功能:
功能 | 说明 |
---|---|
动态路由(如 /user/:id) | 支持参数化路径,通过正则或路径分割提取参数 |
路由守卫 | 比如在进入某个路由前检查登录状态 |
嵌套路由 | 支持多级路由和组件嵌套渲染 |
懒加载视图/组件 | 按需加载路由对应的模块,提高性能 |
Hash 模式降级支持 | 如果不支持 History API,可使用 Hash (#) 模式作为备选 |
6、总结
✅ HTML5 History API 路由管理的核心是:
- 使用 pushState()/ replaceState()改变 URL,不刷新页面;
- 监听 popstate事件处理浏览器的前进/后退;
- 手动根据 URL 匹配路由并渲染视图;
- 拦截链接点击,避免整页刷新,保持 SPA 体验。
二、通过Hash实现
1、什么是 Hash 路由?
在 URL 中,#后面的部分称为 hash(锚点),例如:
https://example.com/#/home
https://example.com/是页面路径;
#/home是 hash 部分,不会触发页面刷新;
浏览器监听 hashchange事件,当 hash 改变时,可以执行相应的 JavaScript 逻辑来切换视图内容。
2、Hash 路由的原理
2.1 URL 中的 Hash 不会触发页面刷新
改变 hash(如通过 location.hash = '#/about')不会导致页面重新加载。
2.2 监听 hash 变化
使用 window.addEventListener('hashchange', callback)监听 hash 的变化,根据不同的 hash 值渲染不同的内容。
2.3 映射 Hash 到对应的组件或页面逻辑
维护一个路由表(对象或数组),将不同的 hash 路径映射到对应的处理函数或组件。
3、如何用原生 JavaScript 实现一个简单的 Hash 路由?
下面是一个最基础的 Hash 路由实现示例:
1. HTML
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" /><title>Hash Router Demo</title>
</head>
<body><nav><a href="#/">首页</a><a href="#/about">关于</a><a href="#/contact">联系</a></nav><div id="app"><!-- 路由内容将在这里显示 --></div><script src="router.js"></script>
</body>
</html>
2. JavaScript(router.js)
// 简单的 Hash 路由实现// 路由配置:path => 对应要展示的内容
const routes = {'/': '<h1>首页</h1><p>欢迎来到首页!</p>','/about': '<h1>关于我们</h1><p>这是关于页面。</p>','/contact': '<h1>联系我们</h1><p>这是联系页面。</p>'
};// 获取显示内容的容器
const app = document.getElementById('app');// 渲染函数:根据当前 hash 显示对应内容
function render() {const hash = window.location.hash.slice(1) || '/'; // 去掉 #,默认为 '/'const content = routes[hash] || '<h1>404 页面未找到</h1>';app.innerHTML = content;
}// 监听 hash 变化
window.addEventListener('hashchange', render);// 页面初次加载时也执行一次
window.addEventListener('DOMContentLoaded', render);