哈希路由是前端单页应用中常用的路由实现方案,核心是利用URL中#后面的哈希值变化不会触发页面整体刷新的特性,通过监听哈希变化来切换页面展示内容。下面我们就来一步步实现基础的哈希路由功能。

哈希路由的核心原理
URL中的哈希部分(即#后面的内容)变化不会向服务端发起请求,也不会导致页面重新加载,浏览器只会把新的哈希值记录到历史记录中。我们可以通过监听浏览器的hashchange事件,在哈希值变化时执行对应的逻辑,实现不同路径对应不同页面内容的展示。
核心依赖的API
- location.hash:获取或设置当前URL的哈希值,哈希值包含开头的#符号
- hashchange事件:当URL的哈希部分发生变化时触发,支持在window对象上监听
- history对象:可以通过pushState等方法操作历史记录,但哈希路由核心不需要依赖它也能实现基础功能
实现基础哈希路由的步骤
1. 定义路由规则
首先需要定义路由路径和对应处理函数的映射关系,每个路径对应一个渲染页面内容的函数。
// 定义路由规则,key是哈希路径(不包含#),value是对应的处理函数
const routes = {
'': () => {
return '<h3>首页</h3><p>这是首页内容</p>';
},
'about': () => {
return '<h3>关于我们</h3><p>这是关于页面内容</p>';
},
'contact': () => {
return '<h3>联系我们</h3><p>这是联系页面内容</p>';
}
};2. 实现路由匹配与视图更新
需要编写一个函数,用来获取当前哈希值,匹配对应的路由规则,然后将返回的页面内容渲染到指定的容器中。
// 获取渲染内容的容器
const appContainer = document.getElementById('app');
// 路由渲染函数
function renderRoute() {
// 获取当前哈希值,去掉开头的#,默认是空字符串(对应首页)
const currentHash = location.hash.slice(1) || '';
// 匹配路由规则,如果没有匹配到则返回404内容
const renderContent = routes[currentHash]
? routes[currentHash]()
: '<h3>404</h3><p>页面不存在</p>';
// 将内容渲染到容器中
appContainer.innerHTML = renderContent;
}3. 监听哈希变化事件
监听window的hashchange事件,当哈希值变化时执行路由渲染函数,同时页面初次加载时也需要执行一次渲染,保证初始路径对应的内容正确展示。
// 监听哈希变化事件
window.addEventListener('hashchange', () => {
renderRoute();
});
// 页面初次加载时触发一次渲染
window.addEventListener('load', () => {
renderRoute();
});4. 完整的HTML示例
下面是包含导航和路由容器的完整HTML代码,可以直接复制到本地运行测试。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>哈希路由示例</title>
</head>
<body>
<nav>
<a href="#/">首页</a>
<a href="#/about">关于我们</a>
<a href="#/contact">联系我们</a>
<a href="#/notfound">不存在的页面</a>
</nav>
<div id="app"></div>
<script>
// 定义路由规则
const routes = {
'': () => {
return '<h3>首页</h3><p>这是首页内容</p>';
},
'about': () => {
return '<h3>关于我们</h3><p>这是关于页面内容</p>';
},
'contact': () => {
return '<h3>联系我们</h3><p>这是联系页面内容</p>';
}
};
// 获取渲染容器
const appContainer = document.getElementById('app');
// 路由渲染函数
function renderRoute() {
const currentHash = location.hash.slice(1) || '';
const renderContent = routes[currentHash]
? routes[currentHash]()
: '<h3>404</h3><p>页面不存在</p>';
appContainer.innerHTML = renderContent;
}
// 监听哈希变化
window.addEventListener('hashchange', () => {
renderRoute();
});
// 初始加载渲染
window.addEventListener('load', () => {
renderRoute();
});
</script>
</body>
</html>哈希路由的优缺点
| 优点 | 缺点 |
|---|---|
| 兼容性极好,支持所有主流浏览器,包括低版本IE | URL中带有#符号,美观度不如history路由 |
| 不需要服务端额外配置,部署简单 | 哈希值不会被搜索引擎主动爬取,对SEO不够友好 |
| 实现逻辑简单,不需要依赖复杂API | 部分场景下和锚点功能可能产生冲突 |
扩展:支持带参数的哈希路由
如果需要支持类似#/user/123这样的带参数的路由,可以在路由匹配时做简单的正则处理,下面是扩展示例:
// 扩展后的路由规则,支持参数匹配
const routes = [
{ path: '', component: () => '<h3>首页</h3>' },
{ path: 'about', component: () => '<h3>关于我们</h3>' },
{ path: 'user/:id', component: (params) => `<h3>用户页面</h3><p>用户ID:${params.id}</p>` }
];
// 路由匹配函数,支持参数提取
function matchRoute(hash) {
const hashParts = hash.split('/').filter(Boolean);
for (const route of routes) {
const routeParts = route.path.split('/').filter(Boolean);
if (routeParts.length !== hashParts.length) continue;
let params = {};
let isMatch = true;
for (let i = 0; i < routeParts.length; i++) {
if (routeParts[i].startsWith(':')) {
const paramName = routeParts[i].slice(1);
params[paramName] = hashParts[i];
} else if (routeParts[i] !== hashParts[i]) {
isMatch = false;
break;
}
}
if (isMatch) return { component: route.component, params };
}
return null;
}
// 修改后的渲染函数
function renderRoute() {
const currentHash = location.hash.slice(1) || '';
const matchResult = matchRoute(currentHash);
if (matchResult) {
appContainer.innerHTML = matchResult.component(matchResult.params);
} else {
appContainer.innerHTML = '<h3>404</h3>';
}
}通过以上步骤,我们就可以在JavaScript中实现一个支持基础功能和简单参数的哈希路由了,开发者可以根据实际需求进一步扩展路由的拦截、懒加载等高级功能。
JavaScript哈希路由前端路由单页应用hashchange修改时间:2026-05-29 23:23:06