本专题为软件平台路由规则设计 (含移动端、Web、电脑、手表与手环等智能穿戴)。本文对应「组件化路由框架」阶段,介绍基于路由表与注解的组件化解耦方案,涵盖 iOS 、Android 、HarmonyOS 、Flutter 、MacOS 、WinOS 、WebApp 、ReactNative 、WatchOS 及穿戴设备的实现思路与代码示例,详见 08-软件体系与多平台路由对照。
一、概念与定位
1.1 什么是组件化路由框架
在多模块、多团队 的工程中,业务模块之间不宜直接依赖具体类(如 import DetailActivity),而应通过统一的路由层 :用 path/URL 表示目标,由路由框架在运行时(或编译期生成表)完成 path → 页面/服务 的映射与跳转。这类以「路由表 + 解耦跳转」为核心的方案,统称组件化路由框架。
典型能力包括:路由注册与查找 、参数传递 、拦截器链 、服务发现 (通过 path 获取接口实现)、按分组/模块加载(如按需加载某模块路由表)。
1.2 知识结构(思维导图)
二、整体架构与数据流(流程图)
三、路由表查找与拦截流程(泳道图)
四、路由表查找与拦截器链(伪代码)
4.0.1 路由表查找(支持静态 path 与简单动态段)
ini
函数 RouteTable.lookup(path, queryParams):
1. 先精确匹配: 若 table 中存在 key == path,返回 RouteMeta(path, target, type)
2. 否则遍历「模式」列表(如 /user/:id):
segments = path 按 '/' 分割
patternSegments = pattern 按 '/' 分割
若 length 不同且非通配符,continue
params = queryParams 的副本
for i in 0..length-1:
若 patternSegments[i] 以 ':' 开头:
params[patternSegments[i].slice(1)] = segments[i]
否则若 patternSegments[i] != segments[i]: break 内层
若全部匹配,返回 (RouteMeta, params)
3. 返回 null
4.0.2 拦截器链执行
sql
函数 InterceptorChain.process(context):
将拦截器按 order 升序排列
for each interceptor in sortedList:
ok = interceptor.process(context)
若 ok 为 false:
interceptor.onInterrupt(context)
返回 false
返回 true
五、iOS 实现详解
5.1 思路:运行时注册 URL → Handler
iOS 常见做法是维护一个 URL → Block 的 Map,在 App 启动或模块加载时注册;跳转时根据 URL 查表执行 Block,在 Block 内创建 ViewController 并 push。
5.2 简单路由管理器(Swift)
swift
// Router.swift
import UIKit
typealias RouteHandler = ([String: String]) -> UIViewController?
final class AppRouter {
static let shared = AppRouter()
private var routeTable: [String: RouteHandler] = [:]
func register(path: String, handler: @escaping RouteHandler) {
routeTable[path] = handler
}
func navigate(path: String, params: [String: String] = [:]) -> Bool {
guard let handler = routeTable[path] else { return false }
guard let vc = handler(params) else { return false }
topViewController()?.show(vc, sender: nil)
return true
}
func navigate(url: URL) -> Bool {
guard url.scheme == "myapp", url.host == "page" else { return false }
let path = "/" + url.pathComponents.dropFirst().joined(separator: "/")
let params = url.queryItems ?? [:]
return navigate(path: path, params: params)
}
private func topViewController() -> UIViewController? {
guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }),
let root = window.rootViewController else { return nil }
var top = root
while let presented = top.presentedViewController { top = presented }
while let nav = top as? UINavigationController, let visible = nav.visibleViewController { top = visible }
return top
}
}
5.3 业务侧注册与调用(Swift)
swift
// 在模块初始化或 AppDelegate 中注册
func setupRoutes() {
AppRouter.shared.register(path: "/page/detail") { params in
let id = params["id"] ?? ""
return DetailViewController(itemId: id)
}
AppRouter.shared.register(path: "/page/list") { _ in
return ListViewController()
}
}
// 业务代码跳转(无依赖 Detail 模块)
AppRouter.shared.navigate(path: "/page/detail", params: ["id": "123"])
5.4 支持 JLRoutes 风格的多段 path(Swift)
swift
// 支持 /user/:id/profile 形式
func register(pattern: String, handler: @escaping ([String: Any]) -> Bool) {
routePatterns.append((pattern: pattern, handler: handler))
}
func navigate(path: String, params: [String: String] = [:]) -> Bool {
let segments = path.split(separator: "/").map(String.init)
for (pattern, handler) in routePatterns {
let patternSegments = pattern.split(separator: "/").map(String.init)
guard patternSegments.count == segments.count else { continue }
var captured: [String: Any] = params as [String: Any]
var match = true
for (i, p) in patternSegments.enumerated() {
if p.hasPrefix(":"), !p.isEmpty {
captured[String(p.dropFirst())] = segments[i]
} else if p != segments[i] {
match = false
break
}
}
if match, handler(captured) { return true }
}
return false
}
六、Android 实现详解(ARouter 风格)
6.1 注解与路由表生成思路
通过 APT 在编译期扫描 @Route(path = "/page/detail"),为每个模块生成类似 ARouter$$Group$$main 的类,在初始化时或按分组懒加载时注入路由表。
6.2 路由注解与元数据(Kotlin)
kotlin
// 注解定义
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class Route(
val path: String,
val group: String = "main"
)
// 使用
@Route(path = "/page/detail", group = "page")
class DetailActivity : AppCompatActivity() { ... }
6.3 路由表与查找(Kotlin)
kotlin
// RouteMeta.kt
data class RouteMeta(
val path: String,
val clazz: Class<*>,
val type: RouteType = RouteType.ACTIVITY
)
enum class RouteType { ACTIVITY, FRAGMENT, SERVICE }
// Router.kt
object AppRouter {
private val routeTable = mutableMapOf<String, RouteMeta>()
private val interceptors = mutableListOf<RouteInterceptor>()
fun init(context: Context) {
// 实际 ARouter 会通过 SPI 加载各模块生成的注册类
// 这里简化:手动注册
register(RouteMeta("/page/detail", DetailActivity::class.java))
register(RouteMeta("/page/list", ListActivity::class.java))
}
fun register(meta: RouteMeta) {
routeTable[meta.path] = meta
}
fun addInterceptor(interceptor: RouteInterceptor) {
interceptors.add(interceptor)
}
fun build(path: String): Postcard = Postcard(path, routeTable[path])
fun navigate(context: Context, postcard: Postcard, requestCode: Int = -1) {
var current = postcard
for (interceptor in interceptors) {
if (!interceptor.process(current)) {
interceptor.onInterrupt(current)
return
}
}
val meta = current.routeMeta ?: return
val intent = Intent(context, meta.clazz).apply {
current.extras?.let { putExtras(it) }
}
if (requestCode >= 0) {
(context as? Activity)?.startActivityForResult(intent, requestCode)
} else {
context.startActivity(intent)
}
}
}
// Postcard:携带 path、params、extras
class Postcard(val path: String, val routeMeta: RouteMeta?) {
var extras: Bundle? = null
fun withString(key: String, value: String) = apply { ... }
}
6.4 业务侧使用(Kotlin)
kotlin
// 跳转
AppRouter.navigate(context, AppRouter.build("/page/detail").withString("id", "123"))
// 拦截器示例
class LoginInterceptor : RouteInterceptor {
override fun process(postcard: Postcard): Boolean {
return UserManager.isLoggedIn()
}
override fun onInterrupt(postcard: Postcard) {
context.startActivity(Intent(context, LoginActivity::class.java))
}
}
七、Flutter 实现详解
7.1 声明式路由表 + 统一入口
Flutter 侧可用 go_router 的 path 作为「路由表」,再封装一层 AppRouter,供各模块通过 path 跳转并传参;同时与 Deep Link 共用一套 path 规则。
7.2 路由表与 go_router 配置(Dart)
dart
// router_config.dart
import 'package:go_router/go_router.dart';
final goRouter = GoRouter(
initialLocation: '/',
routes: [
GoRoute(path: '/', builder: (_, __) => HomePage()),
GoRoute(
path: '/page/detail/:id',
builder: (_, state) => DetailPage(id: state.pathParameters['id']!),
),
GoRoute(path: '/page/list', builder: (_, __) => ListPage()),
],
);
7.3 统一路由门面(Dart)
dart
// app_router.dart
class GlobalRouter {
static void navigate(String path, {Map<String, String>? params}) {
final query = params?.entries.map((e) => '${e.key}=${Uri.encodeComponent(e.value)}').join('&');
final location = query != null && query.isNotEmpty ? '$path?$query' : path;
goRouter.go(location);
}
static void push(String path, {Map<String, String>? params}) {
final query = params?.entries.map((e) => '${e.key}=${Uri.encodeComponent(e.value)}').join('&');
final location = query != null && query.isNotEmpty ? '$path?$query' : path;
goRouter.push(location);
}
}
7.4 业务侧调用(Dart)
dart
// 任意模块内,不直接依赖 DetailPage
GlobalRouter.navigate('/page/detail/123');
GlobalRouter.navigate('/page/detail/123', params: {'from': 'home'});
八、拦截器链时序图
九、组件化路由管理工具类示例(Kotlin)
将「路由表 + 拦截器链 + 跳转」封装成单一门面,便于各模块通过 path 解耦跳转。
kotlin
// RouteManager.kt:门面,持有 RouteTable 与 InterceptorChain
class RouteManager(
private val table: RouteTable,
private val interceptors: List<RouteInterceptor>,
private val adapter: PlatformAdapter
) {
fun navigate(context: Context, path: String, params: Map<String, String> = emptyMap()) {
val result = table.lookup(path, params) ?: run {
adapter.openFallback(context)
return
}
val ctx = RouteContext(path, params, result, context)
for (i in interceptors.sortedBy { it.order }) {
if (!i.process(ctx)) {
i.onInterrupt(ctx)
return
}
}
when (result.type) {
RouteType.ACTIVITY -> adapter.openPage(context, result, params)
RouteType.SERVICE -> adapter.resolveService(result, params)
}
}
}
// 使用
val manager = RouteManager(table, listOf(LoginInterceptor(), LogInterceptor()), AndroidAdapter())
manager.navigate(context, "/page/detail", mapOf("id" to "123"))
十、九大平台组件化路由要点
| 平台 | 路由表形态 | 典型方案 / API |
|---|---|---|
| iOS | 运行时 Map、Block/VC 类型 | MGJRouter、JLRoutes、自建 Router |
| Android | 注解 APT 生成、运行时注册 | ARouter、TheRouter、DRouter |
| HarmonyOS | 路由表或注解、Want 参数 | router.pushUrl、页面栈 |
| Flutter | GoRoute 列表、自定义表 | go_router、GlobalRouter |
| MacOS | 同 iOS | 窗口/VC 栈、自建 Router |
| WinOS | 框架路由表 | Frame.Navigate、MVVM 路由 |
| WebApp | 路由配置表 | React Router、Vue Router |
| ReactNative | React Navigation 配置 | linking、navigation.navigate |
| WatchOS | 简化 Map | 少量界面名映射 |
十一、小结
- 组件化路由框架 用 path/URL 解耦模块间依赖,通过路由表 (运行时注册或编译期生成)完成查找,经拦截器链后再执行跳转或服务获取。
- iOS :可自建
Map<path, Handler>或使用 JLRoutes/MGJRouter 风格支持动态段;Android :ARouter/TheRouter 通过 APT 生成路由表并支持分组与拦截器;Flutter:go_router 声明 path,再封装 GlobalRouter 统一 navigate。 - 与 URL Scheme / Universal Links 统一:外链进 App 后解析出的 path + query 应走同一套路由表与拦截器,保证行为一致。
十二、参考文献
- 阿里 ARouter 原理与 APT.
- TheRouter 页面跳转能力.
- 《01-软件平台路由规则设计-总纲》§2.2、§三、§五.