前端抽象化,打破框架枷锁:统一路由的设计

只要你是在写前端页面,那么好的路由导航无非就两种

  • 编程式导航
  • 约定式导航

一种是你来控制导航方向和所有页面的导航路径,另一种是根据目录结构来自动生成导航.
而编程式导航,也许我们也应该抽象出来,统一下,让我们切换vue、react亦或者其他东西都不必再需要多余成本,让路由中需要加入一些业务逻辑变得方便,让路由的权限控制变得简单,让路由的历史记录管理变得可控。
所以,便有了这个设计
我在设计这个架构时,最核心的理念是抽象解耦。我希望路由导航的逻辑能够独立于具体的框架存在,这样一套代码就能适配各种环境,既减少重复开发,又方便未来的扩展。同时使用中间件,让路由导航的逻辑更加模块化,可插拔,可控。

这里面都有什么?

核心模块

  1. 导航器(INavigator)
    导航器是这个设计的导航口,通过这个去调用跳转,这并没有破坏我们使用vue-router或者react-router的习惯

  2. 路由解析器(IRouteResolver)
    路由解析器从路由信息中解析出来我原始的路由配置对象。对于页面的导航配置相关细则,就在这里面了。

  3. 中间件与导航管道(NavigationPipeline)

提示

这个部分是唯一特殊一点的部分,管道只是为了让中间件更好的用起来,所以这里我完善了一部分实现,你可以有你自己的设计,只要让中间件的思想和逻辑存在且可用

我借鉴了管道模式,且是阻塞式的管道模式。在导航前,用中间件去处理每个路由需要处理的事情,每个中间件只负责一件事,比如路由守卫检查权限、历史管理记录路径等。这样的模块化设计可以随时插拔不同逻辑处理,想加个新需求?插个中间件就搞定,维护起来也顺手。

  1. 历史管理器(IHistoryManager)
    主动对历史记录进行管理和追踪,可控的方式去进行历史管理。这里推荐用 History API去实现,达到和浏览器同步历史记录栈。
  2. 适配器(AbstractRouterAdapter)
    适配器,就是用不同框架来对接的接口。我定义了一个抽象类 AbstractRouterAdapter,里面封装了通用的接口和方法。只要为特定框架实现一个适配器,我的整个路由系统就能无缝接入,无论是 React、Vue 还是 Angular,你可以不必再重写路由的各种逻辑,直接复用

宏观的整体依赖关系

这里我严格遵循了低层策略依赖高层策略,一定要单向,这样才能保证系统的稳定性和可维护性。

uml diagram

涉及的设计模式

  • 适配器模式和工厂模式让我实现了跨框架复用和实例创建的灵活性。
  • 管道模式和观察者模式增强了导航过程的模块化和可控性。
  • 策略模式则让导航行为更加动态和多样化。

对于设计模式,善用可以增加可读性和可扩展性,否则就是破坏代码简单性。

设计的TS抽象源码

提示

这里是一个设计的抽象模型,你可以根据这个模型去实现你的路由导航系统。无视框架,甚至无视语言,只要你能实现这个模型,你就可以在任何地方使用这个路由系统。
其他平台不是ts语言怎么办?AI会出手,助你转译

如果你不想先看这些源码,而是想看看图像👇

/**
 * @description: 路由携带数据
 * @return {*}
 */
export interface NavigationOptions {
  params?: Record<string, string>;
  query?: Record<string, string>;
  hash?: string;
}
/**
 * @description: 跳转类型
 * @return {*}
 */
export enum NavigationType {
    push = 'push',
    replace = 'replace',
    back = 'back',
    forward = 'forward',
}

/**
 * @description: 路由守卫,返回一个真正要跳转的路由
 * @return {*} 返回一个新的跳转路径,或者什么也不返回,按原计划跳转
 */
export interface RouteGuard {
    (context: NavigationContext): NavigationInstruction|void | Promise<NavigationInstruction|void>;
}

/**
 * @description: 路由进入前的钩子
 * @return {*}
 */
export interface RouterBefore {
    (context: NavigationContext): void | Promise<void>;
}

/**
 * @description: 路由离开前的钩子
 * @return {*}
 */
export interface RouterLeave {
    (context: NavigationContext): void | Promise<void>;
}


/**
 * @description: 路由上下文
 * @return {*}
 */
export class NavigationContext {
  constructor(
    public instruction: NavigationInstruction,
    public prevInstruction: NavigationInstruction|null,
    public nextInstruction: NavigationInstruction|null,
    public currentRoute: ResolvedRoute,
  ) {}
}

/**
 * @description: 路由指令
 * @return {*}
 */
export class NavigationInstruction {
  constructor(
    public type: NavigationType,
    public target: string,
    public options: NavigationOptions,
    public timestamp: number
  ) {}
}

/**
 * @description: 路由解析
 * @return {*}
 */
export class ResolvedRoute {
  constructor(
    public path: string,
    public component: any,
    public guards: RouteGuard[] = [],
    public enterHook: RouterBefore[] = [],
    public leaveHook: RouterLeave[] = [],
    public children: ResolvedRoute[] = [],
    public metadata: Map<string, any> = new Map()
  ) {}
}

import {
  NavigationContext,
  NavigationInstruction,
  NavigationOptions,
  NavigationType,
  ResolvedRoute,
} from "./router_domain";

/**
 * @description:导航器,使用有两种方式进行编程式路由导航
 * @return {*}
 */
export interface INavigator {
  push(target: string, options: NavigationOptions): Promise<void>;
  replace(target: string, options: NavigationOptions): Promise<void>;
  back(): Promise<void>;
  forward(): Promise<void>;
}

/**
 * @description: 路由解析器,用于解析路由实际对象
 * @return {*}
 */
export interface IRouteResolver {
  resolve(path: string): Promise<ResolvedRoute>;
}

/**
 * @description: 中间件逻辑,用于处理路由跳转前的随时可插拔处理
 * @return {*}
 */
export interface IMiddleware {
  process(context: NavigationContext, next: () => Promise<void>): Promise<void>;
}

/**
 * @design: 建议实现为单例模式
 * @description: 历史管理器,用于管理路由历史记录,用于可监控式路由路径
 * @return {*}
 */
export interface IHistoryManager {
  push(entry: NavigationInstruction): void;
  replace(entry: NavigationInstruction): void;
  back(): void;
  forward(): void;
  getAllHistory(): NavigationInstruction[];
  clear(): void;
}

/**
 * @description: 路由数据,用于存储全部路由信息
 * @return {*}
 */
export interface IRoute {
  routes: ResolvedRoute[];
  routeMap: Map<string, ResolvedRoute>;
  createRouteMap(): void;
  addRoute(route: ResolvedRoute): void;
}

import { IHistoryManager, IMiddleware } from "./router_core";
import { NavigationContext } from "./router_domain";

/**
 * @description: 管道设计,当前这个是阻塞式的管道设计,依次处理中间件
 * @return {*}
 */
export class NavigationPipeline {
  middlewares: IMiddleware[] = [];
  errorHandler?: (error: Error, context: NavigationContext) => void;

  async pipe(context: NavigationContext): Promise<void> {
    let index = 0;
    const next = async () => {
      if (index < this.middlewares.length) {
        const middleware = this.middlewares[index++];
        await middleware.process(context, next);
      }
    };
    await next();
  }
  addMiddleware(middleware: IMiddleware): void {
    this.middlewares.push(middleware);
  }
  onError(handler: (error: Error, context: NavigationContext) => void): void {
    this.errorHandler = handler;
  }
}

/**
 * @description: 路由守卫中间件
 * @return {*}
 */
export class GuardMiddleware implements IMiddleware {
  async process(
    context: NavigationContext,
    next: () => Promise<void>
  ): Promise<void> {
    const route = context.currentRoute;
    for (const guard of route.guards) {
      const result = await guard(context);
      //   若得到了新的跳转路径,则跳转到新的路径
      if (result) {
        context.instruction = result;
        break;
      }
    }
    await next();
  }
}

export class HistoryMiddleware implements IMiddleware {
  histroyManager: IHistoryManager;
  constructor(histroyManager: IHistoryManager) {
    this.histroyManager = histroyManager;
  }
  async process(
    context: NavigationContext,
    next: () => Promise<void>
  ): Promise<void> {
    const instruction = context.instruction;
    const type = instruction.type;
    this.histroyManager[type](instruction);
    next();
  }
}


import { INavigator, IRouteResolver, IHistoryManager, IRoute } from "./router_core"
import { NavigationPipeline } from "./router_pipeline"

export abstract class AbstractRouterAdapter {
    navigator: INavigator
    resolver: IRouteResolver
    history: IHistoryManager
    route:IRoute
    pipeline: NavigationPipeline
    initialize(route:IRoute,pipeline:NavigationPipeline): void{
        this.route = route
        this.pipeline = pipeline
        this.navigator = this.createNavigator()
        this.resolver = this.createResolver()
        this.history = this.createHistory()
    }
    abstract createNavigator(): INavigator
    abstract createResolver(): IRouteResolver
    abstract createHistory(): IHistoryManager
    abstract errorHandler():void
}

图像也许会帮你更好的理解

流程图

uml diagram

细节的类图

以下uml图,可以帮你快速的理解我这里的依赖关系,他是单向的,高层策略和低层策略是很明显的。

你可以右键下面这个图,在新的标签页中打开,这样可以放大和拖动的查看

高级路由抽象设计

上面uml类图的源码,还是有一些价值的

@startuml 高级路由抽象设计
left to right direction

' 领域模型
package "Domain Models" {

    interface "NavigationOptions" {
        params?: Map<string, string> :路由参数,比如 /user/:id
        query?: Map<string, string>  :查询参数,比如 ?id=1
        hash?: string :比如锚点
    }
    enum "NavigationType" {
        push
        replace
        back
        forward
    }

    interface RouteGuard {
        (context: NavigationContext): NavigationInstruction| void | Promise<NavigationInstruction|void>;
    }

    interface RouterBefore {
        (context: NavigationContext): void | Promise<void>;
    }
    interface RouterLeave {
        (context: NavigationContext): void | Promise<void>;
    }


    class "NavigationContext" {
        +instruction: NavigationInstruction :导航指令
        +prevInstruction: NavigationInstruction
        +nextInstruction: NavigationInstruction
        +currentRoute: ResolvedRoute
    }

    class "NavigationInstruction" {
        +type: NavigationType
        +target: string
        +options: NavigationOptions
        +timestamp: number
    }

    class "ResolvedRoute" {
        +path: string
        +name?: string
        +component: any
        +guards?: RouteGuard[] :路由守卫,用于拦截导航
        +enterHook?: RouterBefore[] :完成一些预处理任务
        +leaveHook?: RouterLeave[] :完成一些后处理任务
        +children?: ResolvedRoute[] :子路由
        +metadata: Map<string, any>
    }
}

' 核心逻辑抽象
package "Core Abstractions" {

    ' 导航器,使用有两种方式进行编程式路由导航
    interface "INavigator" {
        +push(target: string, options: NavigationOptions): Promise<void>
        +replace(target: string, options: NavigationOptions): Promise<void>
        +back(): Promise<void>
        +forward(): Promise<void>
    }
    ' 路由解析器,用于解析路由路径
    interface "IRouteResolver" {
        +resolve(path: string): Promise<ResolvedRoute>
    }

    interface "IMiddleware" {
        ' 执行此中间件逻辑
        +process(context: NavigationContext, next: () => Promise<void>): Promise<void>
    }

    interface "IHistoryManager" {
        +push(entry: NavigationInstruction): void
        +replace(entry: NavigationInstruction): void
        +back(): void
        +forward(): void
        +getAllHistory(): NavigationInstruction[]
        +clear(): void
    }

    interface "IRoute" {
        routes: ResolvedRoute[]
        routeMap: Map<string, ResolvedRoute>
        +createRouteMap(): void: 根据routes创建路由映射表
        +addRoute(route: ResolvedRoute): void
    }
}



package "Pipeline" {
    class "NavigationPipeline" {
        -middlewares: IMiddleware[]
        -errorHandler?: (error: Error, context: NavigationContext) => void
        +pipe(context: NavigationContext): Promise<void>
        +addMiddleware(middleware: IMiddleware): void
        +onError(handler: (error: Error, context: NavigationContext) => void): void
    }
    ' 路由守卫中间件
    class "GuardMiddleware" {
        +process(context: NavigationContext, next: () => Promise<void>): Promise<void>
    }
    '
    class "HistoryMiddleware" {
        +process(context: NavigationContext, next: () => Promise<void>): Promise<void>
    }
}


package "Framework Adapters" {
    abstract class "AbstractRouterAdapter" {
        +navigator: INavigator
        #resolver: IRouteResolver
        +history: IHistoryManager
        +route:IRoute
        #pipeline: NavigationPipeline
        +initialize(route:IRoute,pipeline:NavigationPipeline): void
        +{abstract} createNavigator(): INavigator
        +{abstract} createResolver(): IRouteResolver
        +{abstract} createHistory(): IHistoryManager
        +{abstract} errorHandler():void
    }
}

' 关系定义


NavigationPipeline o-- IMiddleware
AbstractRouterAdapter o-- INavigator
AbstractRouterAdapter o-- IRouteResolver
AbstractRouterAdapter o-- NavigationPipeline
AbstractRouterAdapter o-- IHistoryManager

GuardMiddleware ..|> IMiddleware
HistoryMiddleware ..|> IMiddleware

NavigationContext --* NavigationInstruction
NavigationContext --* ResolvedRoute
ResolvedRoute o-- RouteGuard
ResolvedRoute o-- RouterBefore
ResolvedRoute o-- RouterLeave
ResolvedRoute o-- ResolvedRoute
NavigationInstruction --> NavigationType
NavigationPipeline --> NavigationContext

IRouteResolver --> ResolvedRoute
INavigator --> NavigationType
INavigator --> NavigationOptions
IHistoryManager o-- NavigationInstruction
IRoute o-- ResolvedRoute

note right of NavigationPipeline
    导航管道
    处理所有导航相关的中间件
end note

note right of IMiddleware
    中间件接口
    定义了导航过程中的处理单元
end note

note right of AbstractRouterAdapter
    路由适配器抽象基类
    可以采用适配器模式来实现不同框架的路由适配
    也可以采用工厂模式来创建不同框架的路由适配器
end note

@enduml


最后更新日期 6/24/2025, 10:29:18 AM