浏览器原生 history.pushState 和 hashchange 可实现轻量前端路由:hash 模式用 hashchange(兼容 IE8+,URL 带 #),history 模式用 popstate(需服务端 fallback,URL 干净);首次加载需手动匹配路由;pushState 的 state 传状态对象、title 传空字符串、url 传同源相对路径;路由匹配建议用 path-to-regexp 等库处理动态路径;所有跳转(含手动输入 URL)均需服务端将前端路由 fallback 至 index.html。

浏览器原生支持的 history.pushState 和 hashchange 事件已足够实现轻量级前端路由,无需框架也能跑通核心逻辑。
监听 URL 变化用 popstate 还是 hashchange?
取决于你选的是 history 模式还是 hash 模式:
-
hashchange仅监听 URL 中#后面的变化,兼容性极好(IE8+),但 URL 会带#/user这种不美观的前缀 -
popstate监听pushState/replaceState触发的前进后退,支持干净 URL(如/user),但需要服务端配合——否则直接刷新会 404 - 单页应用首次加载时,
popstate不会触发,必须手动调用一次路由匹配逻辑
pushState 的三个参数到底怎么填?
调用形式是 history.pushState(state, title, url),其中:
-
state:任意可序列化的 JS 值(如{page: 'user', id: 123}),会在popstate事件中作为event.state返回,用于恢复页面状态 -
title:目前所有主流浏览器都忽略该参数,传空字符串''即可 -
url:要推入历史栈的相对路径(如'/post/42'),浏览器地址栏会更新,但不会发起真实请求
注意:url 必须同源,且不能跨域或写成绝对 URL(如 https://example.com/a)——否则抛出安全错误。
立即学习“Java免费学习笔记(深入)”;
免费 盛世企业网站管理系统(SnSee)系统完全免费使用,无任何功能模块使用限制,在使用过程中如遇到相关问题可以去官方论坛参与讨论。开源 系统Web代码完全开源,在您使用过程中可以根据自已实际情况加以调整或修改,完全可以满足您的需求。强大且灵活 独创的多语言功能,可以直接在后台自由设定语言版本,其语言版本不限数量,可根据自已需要进行任意设置;系统各模块可在后台自由设置及开启;强大且适用的后台管理支
如何匹配路由并渲染对应组件?
最简做法是维护一个路由表,用正则或 startsWith 判断当前路径:
const routes = [
{ path: '/', component: HomePage },
{ path: '/user', component: UserList },
{ path: '/user/:id', component: UserDetail }
];
function matchRoute(pathname) {
for (const route of routes) {
// 简单前缀匹配(适合无嵌套路由场景)
if (pathname.startsWith(route.path.replace(/\/:([^/]+)/g, '/[^/]+'))) {
return route;
}
}
return null;
}
// 手动触发匹配(首次加载 + popstate 时调用)
function navigate() {
const current = location.pathname;
const matched = matchRoute(current);
if (matched) render(matched.component);
}
实际项目中建议用更健壮的路径解析库(如 path-to-regexp),否则带参数的动态路由(/user/:id)容易漏匹配或误匹配。
点击链接时不刷新页面的关键在哪?
- 给所有路由链接加
data-nav属性(避免劫持外部链接) - 用事件委托监听
click,拿到e.target.href后截取 pathname - 调用
history.pushState更新地址栏,再立即执行navigate() - 别忘了处理右键、Ctrl+点击等非主键点击,这些不应拦截
容易被忽略的是:SPA 中所有跳转都得走 JS 路由逻辑,包括用户手动输入 URL 后回车——这意味着服务端必须把所有前端路由路径都 fallback 到 index.html,否则直接访问 /admin 会 404。










