JavaScript系列(42)--路由系统实现详解
JavaScript路由系统实现详解 🛣️
今天,让我们深入探讨JavaScript的路由系统实现。路由系统是现代单页应用(SPA)中的核心组件,它负责管理视图的切换和状态的维护。
路由系统基础概念 🌟
💡 小知识:路由系统是单页应用中负责URL管理和视图切换的核心机制,它能够在不刷新页面的情况下更新视图内容。
基本实现 📊
// 1. 基础路由器
class SimpleRouter {
constructor() {
this.routes = new Map();
this.currentRoute = null;
// 监听浏览器历史记录变化
window.addEventListener('popstate', () => {
this.handleRoute(window.location.pathname);
});
}
addRoute(path, handler) {
this.routes.set(path, handler);
}
removeRoute(path) {
this.routes.delete(path);
}
navigate(path) {
window.history.pushState({}, '', path);
this.handleRoute(path);
}
handleRoute(path) {
const handler = this.routes.get(path);
if (handler) {
this.currentRoute = path;
handler();
} else {
console.warn(`No handler found for path: ${path}`);
}
}
}
// 2. 支持参数的路由器
class ParameterizedRouter {
constructor() {
this.routes = [];
this.currentRoute = null;
window.addEventListener('popstate', () => {
this.handleRoute(window.location.pathname);
});
}
addRoute(pattern, handler) {
const regex = this.patternToRegex(pattern);
this.routes.push({ pattern, regex, handler });
}
patternToRegex(pattern) {
return new RegExp(
'^' + pattern.replace(/:[^\s/]+/g, '([^/]+)') + '$'
);
}
navigate(path) {
window.history.pushState({}, '', path);
this.handleRoute(path);
}
handleRoute(path) {
for (const route of this.routes) {
const match = path.match(route.regex);
if (match) {
const params = this.extractParams(route.pattern, match);
this.currentRoute = path;
route.handler(params);
return;
}
}
console.warn(`No handler found for path: ${path}`);
}
extractParams(pattern, match) {
const params = {};
const paramNames = pattern.match(/:[^\s/]+/g) || [];
paramNames.forEach((param, index) => {
params[param.slice(1)] = match[index + 1];
});
return params;
}
}
// 3. 路由守卫
class GuardedRouter extends ParameterizedRouter {
constructor() {
super();
this.guards = [];
}
addGuard(guard) {
this.guards.push(guard);
}
async navigate(path) {
// 执行所有守卫
for (const guard of this.guards) {
const canProceed = await guard(path);
if (!canProceed) {
console.warn(`Navigation to ${path} was blocked by a guard`);
return;
}
}
super.navigate(path);
}
}
高级功能实现 🚀
// 1. 嵌套路由
class NestedRouter {
constructor() {
this.routes = new Map();
this.children = new Map();
this.parent = null;
}
addRoute(path, handler) {
this.routes.set(path, handler);
}
addChild(path, router) {
router.parent = this;
this.children.set(path, router);
}
async handleRoute(path) {
// 检查是否有直接匹配的路由
const handler = this.routes.get(path);
if (handler) {
await handler();
return true;
}
// 检查子路由
for (const [prefix, childRouter] of this.children) {
if (path.startsWith(prefix)) {
const remainingPath = path.slice(prefix.length);
const handled = await childRouter.handleRoute(remainingPath);
if (handled) return true;
}
}
return false;
}
}
// 2. 路由中间件
class MiddlewareRouter {
constructor() {
this.routes = new Map();
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
}
addRoute(path, handler) {
this.routes.set(path, handler);
}
async handleRoute(path) {
const handler = this.routes.get(path);
if (!handler) {
console.warn(`No handler found for path: ${path}`);
return;
}
const context = { path, handled: false };
// 执行中间件链
const chain = [...this.middlewares, async (ctx, next) => {
await handler();
ctx.handled = true;
await next();
}];
await this.executeMiddlewareChain(chain, context);
}
async executeMiddlewareChain(middlewares, context) {
let prevIndex = -1;
const runner = async (index) => {
if (index === prevIndex) {
throw new Error('next() called multiple times');
}
prevIndex = index;
const middleware = middlewares[index];
if (middleware) {
await middleware(context, () => runner(index + 1));
}
};
await runner(0);
}
}
// 3. 路由状态管理
class StatefulRouter {
constructor() {
this.routes = new Map();
this.state = new Map();
window.addEventListener('popstate', (event) => {
if (event.state) {
this.state = new Map(Object.entries(event.state));
}
this.handleRoute(window.location.pathname);
});
}
addRoute(path, handler) {
this.routes.set(path, handler);
}
setState(key, value) {
this.state.set(key, value);
}
getState(key) {
return this.state.get(key);
}
navigate(path, state = {}) {
this.state = new Map(Object.entries(state));
window.history.pushState(
Object.fromEntries(this.state),
'',
path
);
this.handleRoute(path);
}
handleRoute(path) {
const handler = this.routes.get(path);
if (handler) {
handler(Object.fromEntries(this.state));
} else {
console.warn(`No handler found for path: ${path}`);
}
}
}
性能优化技巧 ⚡
// 1. 路由缓存
class CachedRouter {
constructor() {
this.routes = new Map();
this.cache = new Map();
this.maxCacheSize = 10;
}
addRoute(path, handler) {
this.routes.set(path, handler);
}
async handleRoute(path) {
// 检查缓存
if (this.cache.has(path)) {
const cached = this.cache.get(path);
if (!this.isCacheExpired(cached)) {
return cached.content;
}
this.cache.delete(path);
}
const handler = this.routes.get(path);
if (handler) {
const content = await handler();
this.cacheRoute(path, content);
return content;
}
}
cacheRoute(path, content) {
// 如果缓存已满,删除最旧的条目
if (this.cache.size >= this.maxCacheSize) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
this.cache.set(path, {
content,
timestamp: Date.now()
});
}
isCacheExpired(cached) {
const maxAge = 5 * 60 * 1000; // 5分钟
return Date.now() - cached.timestamp > maxAge;
}
}
// 2. 路由预加载
class PreloadRouter {
constructor() {
this.routes = new Map();
this.preloadQueue = new Set();
}
addRoute(path, handler, preloadCondition) {
this.routes.set(path, {
handler,
preloadCondition
});
}
async checkPreloadConditions() {
for (const [path, route] of this.routes) {
if (route.preloadCondition &&
route.preloadCondition() &&
!this.preloadQueue.has(path)) {
this.preloadQueue.add(path);
this.preloadRoute(path);
}
}
}
async preloadRoute(path) {
const route = this.routes.get(path);
if (route) {
try {
await route.handler();
console.log(`Preloaded route: ${path}`);
} catch (error) {
console.error(`Failed to preload route: ${path}`, error);
}
}
}
}
// 3. 路由分片
class ChunkedRouter {
constructor() {
this.chunks = new Map();
this.loadedChunks = new Set();
}
defineChunk(name, routes) {
this.chunks.set(name, routes);
}
async loadChunk(name) {
if (this.loadedChunks.has(name)) {
return;
}
const chunk = this.chunks.get(name);
if (chunk) {
await chunk();
this.loadedChunks.add(name);
}
}
async handleRoute(path) {
// 确定路由所属的chunk
const chunkName = this.getChunkForPath(path);
if (chunkName) {
await this.loadChunk(chunkName);
}
}
getChunkForPath(path) {
// 实现路由到chunk的映射逻辑
// 这里可以使用正则表达式或其他匹配方式
}
}
最佳实践建议 💡
- 路由组织模式
// 1. 模块化路由
class ModularRouter {
constructor() {
this.modules = new Map();
}
registerModule(name, routes) {
this.modules.set(name, routes);
}
async loadModule(name) {
const module = this.modules.get(name);
if (module) {
await module.initialize();
}
}
}
// 2. 路由配置管理
class RouteConfig {
constructor() {
this.config = new Map();
}
addConfig(path, options) {
this.validateConfig(options);
this.config.set(path, options);
}
validateConfig(options) {
const requiredFields = ['component', 'auth'];
for (const field of requiredFields) {
if (!(field in options)) {
throw new Error(`Missing required field: ${field}`);
}
}
}
}
// 3. 错误处理
class ErrorBoundaryRouter {
constructor() {
this.routes = new Map();
this.errorHandlers = new Map();
}
addErrorHandler(errorType, handler) {
this.errorHandlers.set(errorType, handler);
}
async handleRoute(path) {
try {
const handler = this.routes.get(path);
if (handler) {
await handler();
}
} catch (error) {
await this.handleError(error);
}
}
async handleError(error) {
for (const [ErrorType, handler] of this.errorHandlers) {
if (error instanceof ErrorType) {
await handler(error);
return;
}
}
// 默认错误处理
console.error('Unhandled router error:', error);
}
}
结语 📝
路由系统是现代JavaScript单页应用的核心组件,通过本文,我们学习了:
- 路由系统的基本概念和实现
- 高级功能如嵌套路由和中间件
- 性能优化技巧
- 最佳实践和错误处理策略
- 路由配置和模块化管理
💡 学习建议:在实践中,要根据应用的规模和需求选择合适的路由实现方案。对于小型应用,简单的路由器就足够了;对于大型应用,则需要考虑使用更完整的路由解决方案,包括代码分割、路由守卫等特性。
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻