JavaScript中用class封装前端路由控制器,通过监听URL变化、正则匹配路径、调用处理器实现轻量路由系统,支持参数解析、嵌套路由与生命周期钩子,适用于静态站或学习原理。

JavaScript 中用 class 语法管理前端路由控制器,核心是把路由匹配、页面渲染、状态维护封装成可复用、可继承的类。它不是直接替代 React Router 或 Vue Router 这类框架,而是帮你从零搭建轻量、可控的路由系统,尤其适合静态站点、工具页或学习原理。
定义一个基础路由控制器类
这个类负责监听 URL 变化、匹配路径、调用对应处理器,并支持简单参数解析:
- 使用
history.pushState和popstate实现无刷新跳转 - 用正则或字符串前缀匹配路径(如
/user/:id) - 每个路由绑定一个
handler方法,由子类实现具体逻辑
class Router {
constructor(routes = []) {
this.routes = routes;
this.init();
}
init() {
window.addEventListener('popstate', (e) => {
this.handleLocation(e.state?.path || location.pathname);
});
// 首次加载也触发一次
this.handleLocation(location.pathname);
}
add(path, handler) {
this.routes.push({ path, handler });
}
match(path) {
for (const route of this.routes) {
const regex = new RegExp('^' + route.path.replace(/:(\w+)/g, '([^/]+)') + '$');
const result = path.match(regex);
if (result) {
const params = {};
const keys = route.path.match(/:(\w+)/g)?.map(k => k.slice(1)) || [];
keys.forEach((key, i) => {
params[key] = result[i + 1];
});
return { handler: route.handler, params };
}
}
return null;
}
handleLocation(path) {
const matched = this.match(path);
if (matched) {
matched.handler.call(this, matched.params);
} else {
console.warn(`No route matched for ${path}`);
}
}
navigate(to) {
history.pushState({ path: to }, '', to);
this.handleLocation(to);
}
}
用子类封装页面逻辑和状态
继承 Router,把每个页面的初始化、数据获取、DOM 渲染、事件绑定都写在独立方法里,避免全局污染:
-
home()、user(id)等方法即路由处理器,自动接收解析出的参数 - 可在构造函数中预设公共状态(如用户信息、loading 标志)
- 配合
document.querySelector或模板字符串更新视图,不依赖框架
class AppRouter extends Router {
constructor() {
super();
this.userCache = new Map();
this.rootEl = document.getElementById('app');
this.add('/', () => this.home());
this.add('/user/:id', ({ id }) => this.user(id));
this.add('/about', () => this.about());
}
home() {
this.rootEl.innerHTML = `
<h1>首页</h1>
<nav>
<a href="https://www.php.cn/link/93ac0c50dd620dc7b88e5fe05c70e15b" onclick="router.navigate('/user/123')">用户 https://www.php.cn/link/93ac0c50dd620dc7b88e5fe05c70e15b123</a>
<a href="https://www.php.cn/link/93ac0c50dd620dc7b88e5fe05c70e15b" onclick="router.navigate('/about')">关于</a>
</nav>
`;
}
async user(id) {
const user = this.userCache.get(id) || await fetch(`/api/users/${id}`).then(r => r.json());
this.userCache.set(id, user);
this.rootEl.innerHTML = `
<h1>用户:${user.name}</h1>
<button onclick="router.navigate('/')">返回首页</button>
`;
}
about() {
this.rootEl.innerHTML = '<h1>关于本应用</h1><p>基于 class 的轻量路由</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill5092" title="PigX UI 前端开发"><img
src="https://img.php.cn/upload/skill/000/000/081/179033410052138.jpg" alt="PigX UI 前端开发" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill5092" title="PigX UI 前端开发">PigX UI 前端开发</a>
<p>PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。</p>
</div>
<a href="/xiazai/skill5092" title="PigX UI 前端开发" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/c1c2c2ed740f" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">Java免费学习笔记(深入)</a></a>”;</p>';
}
}
// 启动
const router = new AppRouter();
支持嵌套路由与生命周期钩子
在基础类上扩展 beforeEach、afterEach 和子路由挂载能力,让权限控制、加载动画、页面过渡更自然:
-
beforeEach可返回false中断导航,或返回 Promise 做异步校验(如登录态) - 子路由可通过
mount方法注入到指定容器,实现局部刷新 - 每个 handler 执行前后自动触发钩子,无需手动调用
// 在 Router 类中加入
this.beforeHooks = [];
this.afterHooks = [];
useBefore(fn) { this.beforeHooks.push(fn); }
useAfter(fn) { this.afterHooks.push(fn); }
async runBeforeHooks(toPath, fromPath) {
for (const hook of this.beforeHooks) {
const res = await hook(toPath, fromPath);
if (res === false) return false;
}
return true;
}
async handleLocation(path) {
const from = this.currentPath;
this.currentPath = path;
if (!(await this.runBeforeHooks(path, from))) return;
const matched = this.match(path);
if (matched) {
await matched.handler.call(this, matched.params);
}
this.runAfterHooks(path, from);
}
runAfterHooks(to, from) {
this.afterHooks.forEach(hook => hook(to, from));
}
注意事项与边界处理
这类手写路由适合中小项目或教学场景,但要注意几个实际问题:
- 浏览器前进/后退时,
popstate不会触发页面 reload,但需确保所有动态内容都通过handleLocation重建 - SEO 不友好——纯前端路由对爬虫不可见,服务端需配套支持(如 SSR 或静态生成)
- 路径参数只支持简单命名捕获,复杂规则(如可选段、通配符)需升级正则或引入 path-to-regexp 类库
- 避免在 handler 中直接操作未挂载的 DOM,建议统一用
this.rootEl容器 + 内部清空逻辑
不复杂但容易忽略。关键不在“怎么写 class”,而在于把路由当作状态机来设计:路径是输入,handler 是响应,钩子是副作用,整个流程可预测、可调试、可测试。


















