SPA路由核心是拦截URL变化而不刷新页面,需同时处理a标签点击拦截与popstate事件监听,配合pushState/replaceState管理历史栈,并实现动态路径匹配与状态同步。

SPA 路由的核心是拦截 URL 变化而不刷新页面
关键不在“怎么写路由表”,而在于绕过浏览器默认的跳转行为,把 location.href 或 a 标签点击、前进/后退等操作,全部接管为 JS 控制的视图切换。原生方案依赖 history.pushState() 和 history.replaceState() 修改地址栏,再监听 popstate 事件响应浏览器导航键(前进/后退)。
必须手动处理 点击和 popstate 两个入口
只监听 popstate 不够——用户点链接仍会触发完整页面加载;只阻止 a 默认行为又会让前进/后退失效。二者必须同时处理:
- 给所有
绑定click事件,调用event.preventDefault(),再执行history.pushState()+ 手动渲染 - 监听
window.addEventListener('popstate', handler),在 handler 中根据event.state或location.pathname匹配路由并更新视图 - 注意:
pushState()第二个参数(title)在多数浏览器中被忽略,传空字符串即可:history.pushState({}, '', '/user')
history.pushState() 和 history.replaceState() 的区别直接影响用户体验
误用会导致后退卡死或历史栈爆炸:
- 用
pushState()添加新记录——适合用户主动跳转(如点击菜单) - 用
replaceState()替换当前记录——适合初始化渲染、404 重定向、或表单提交后避免重复提交 - 服务端未配置 fallback(如 Nginx 返回 404)时,直接访问
/user/123会失败;必须确保所有前端路由路径都返回同一份index.html
路由匹配逻辑不能只靠 ===,要支持动态参数和嵌套路由
硬编码 if (path === '/user') { ... } 无法扩展。真实场景需解析路径:
function matchRoute(path, pattern) {
const keys = [];
const regexp = new RegExp('^' + pattern.replace(/:(\w+)/g, (_, key) => {
keys.push(key);
return '([^/]+)';
}) + '/?$');
const result = path.match(regexp);
if (!result) return null;
const params = Object.fromEntries(keys.map((key, i) => [key, result[i + 1]]));
return { params };
}
// 使用示例
const match = matchRoute('/user/123', '/user/:id'); // { params: { id: '123' } }
更复杂场景(如通配符、可选参数)建议用现成解析器如 path-to-regexp,自己实现容易漏掉边界 case。










