词法作用域与闭包实现微型前端路由匹配器,通过IIFE封装私有路由表、正则编译、参数提取及URL监听逻辑,支持路径注册、自动dispatch、嵌套路由,避免全局污染。

用词法作用域和闭包实现微型前端路由匹配器,核心是把路由规则和匹配逻辑封装在函数内部,让匹配器能记住注册的路径与对应处理器,同时避免全局污染。
用闭包保存路由表
路由匹配器需要维护一个路径-处理器映射表,但不应暴露给外部直接修改。通过立即执行函数(IIFE)创建私有作用域,把路由表 routes 声明在内部,仅暴露 on 和 dispatch 方法:
const createRouter = () => {
const routes = new Map(); // 私有路由表,外部无法访问
return {
on(path, handler) {
routes.set(path, handler);
},
dispatch(url) {
const pathname = new URL(url, location.origin).pathname;
const handler = routes.get(pathname);
if (handler) handler();
}
};
};
const router = createRouter();
router.on('/home', () => console.log('进入首页'));
router.on('/about', () => console.log('进入关于页'));
router.dispatch('/home'); // 输出:进入首页
支持简单参数提取(基于词法作用域捕获)
要解析如 /user/123 中的 123,需在注册时预编译正则、并在闭包中记住参数名。利用闭包让每个 on 调用都拥有自己的一份正则和 keys 数组:
const createRouter = () => {
const routes = [];
return {
on(pattern, handler) {
// 将 /user/:id → /^\/user\/([^/]+)$/, ['id']
const keys = [];
const regex = new RegExp(
'^' + pattern.replace(/:(\w+)/g, (_, key) => {
keys.push(key);
return '([^/]+)';
}) + '$'
);
routes.push({ regex, keys, handler });
},
dispatch(url) {
const pathname = new URL(url, location.origin).pathname;
for (const { regex, keys, handler } of routes) {
const match = pathname.match(regex);
if (match) {
const params = Object.fromEntries(
keys.map((key, i) => [key, match[i + 1]])
);
handler(params);
return;
}
}
}
};
};
监听 URL 变化并自动 dispatch
结合 popstate 和 hashchange 事件,让路由响应浏览器导航。这些事件监听器在闭包内定义,自然捕获当前 dispatch 函数,无需绑定或传参:
立即学习“Java免费学习笔记(深入)”;
const createRouter = () => {
const routes = [];
let currentPath = location.pathname;
const dispatch = (url) => {
// ……同上 dispatch 实现
};
const update = () => {
const url = location.href;
dispatch(url);
currentPath = new URL(url, location.origin).pathname;
};
window.addEventListener('popstate', update);
window.addEventListener('hashchange', update);
return {
on(pattern, handler) {
// ……注册逻辑
},
dispatch,
start() {
update(); // 初始化匹配一次
}
};
};
可选:支持嵌套路由前缀(复用闭包链)
若想支持子路由器(如 adminRouter 只处理 /admin/* 下的路径),可用高阶函数返回新路由器,并在闭包中捕获父级前缀:
const createSubRouter = (prefix) => {
const routes = [];
return {
on(path, handler) {
routes.push({ path: prefix + path, handler });
},
dispatch(fullUrl) {
const url = new URL(fullUrl, location.origin);
const pathname = url.pathname;
for (const { path, handler } of routes) {
if (pathname.startsWith(path)) {
handler({ pathname: pathname.slice(path.length) || '/' });
break;
}
}
}
};
};
const adminRouter = createSubRouter('/admin');
adminRouter.on('/users', () => console.log('管理用户'));



















