网站域名的管理密码如何索取,漫画网站建设教程视频,wordpress 主体安装,网站设计用的技术路由机制概述
为什么需要路由?
┌─────────────────────────────────────────────────────────────────┐
│ 传统页面跳转 │
├────────…路由机制概述为什么需要路由?┌─────────────────────────────────────────────────────────────────┐ │ 传统页面跳转 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ 直接依赖 ┌──────────────┐ │ │ │ 首页模块 │ ─────────────────────→ │ 商品模块 │ │ │ │ │ import ProductModule │ │ │ │ │ HomeVC │ ───────────────────────→│ ProductVC │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ │ │ │ 直接依赖 │ │ │ └─────────────────────┐ ┌─────────────┘ │ │ ▼ ▼ │ │ ┌──────────────┐ │ │ │ 订单模块 │ │ │ │ OrderVC │ │ │ └──────────────┘ │ │ │ │ 问题: │ │ ❌ 模块间强耦合,形成网状依赖 │ │ ❌ 无法独立编译和测试 │ │ ❌ 代码改动影响范围大 │ │ ❌ 不支持动态化和外部跳转 │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ 路由解耦后 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 首页模块 │ │ 商品模块 │ │ │ └──────┬───────┘ └───────▲──────┘ │ │ │ │ │ │ │ router.navigate("/product/123") │ │ │ │ │ │ │ ▼ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 路由中心 │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ 路由表: │ │ │ │ │ │ /home → HomeViewController │ │ │ │ │ │ /product/:id → ProductViewController │ │ │ │ │ │ /order/:id → OrderViewController │ │ │ │ │ │ /cart → CartViewController │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ 订单模块 │ │ │ └──────────────┘ │ │ │ │ 优势: │ │ ✅ 模块间完全解耦 │ │ ✅ 支持外部 DeepLink │ │ ✅ 动态化配置 │ │ ✅ 统一拦截和处理 │ └─────────────────────────────────────────────────────────────────┘主流路由方案对比┌──────────────────────────────────────────────────────────────────────────┐ │ iOS 主流路由方案对比 │ ├────────────────┬────────────────┬────────────────┬───────────────────────┤ │ 方案 │ 代表框架 │ 优点 │ 缺点 │ ├────────────────┼────────────────┼────────────────┼───────────────────────┤ │ URL Router │ JLRoutes │ • 解耦彻底 │ • 参数传递受限 │ │ │ MGJRouter │ • 支持DeepLink │ • 编译期无类型检查 │ │ │ HHRouter │ • 配置灵活 │ • 硬编码URL字符串 │ ├────────────────┼────────────────┼────────────────┼───────────────────────┤ │ Target-Action │ CTMediator │ • 无需注册 │ • 依赖runtime │ │ │ │ • 延迟加载 │ • 方法名硬编码 │ │ │ │ • 原生调用 │ • 缺少编译检查 │ ├────────────────┼────────────────┼────────────────┼───────────────────────┤ │ Protocol-Class │ BeeHive │ • 类型安全 │ • 需要注册协议 │ │ │ Swinject │ • 编译期检查 │ • 接口变更成本高 │ │ │ │ • IDE支持好 │ • 协议维护成本 │ ├────────────────┼────────────────┼────────────────┼───────────────────────┤ │ 混合方案 │ 自研Router │ • 各取所长 │ • 架构复杂度较高 │ │ │ │ • 适应不同场景 │ • 学习成本 │ └────────────────┴────────────────┴────────────────┴───────────────────────┘URL Scheme 机制URL Scheme 基础/* ═══════════════════════════════════════════════════════════════════ URL Scheme 结构 ═══════════════════════════════════════════════════════════════════ myapp://product/detail?id=123source=home ───── ───────────── ───────────────────── │ │ │ Scheme Path Query (协议) (路径) (查询参数) 完整URL结构: ┌────────────────────────────────────────────────────────────────┐ │ scheme://user:password@host:port/path?query=value#fragment │ └────────────────────────────────────────────────────────────────┘ 常见使用场景: ┌─────────────────────────────────────────────────────────────────┐ │ 场景 │ 示例URL │ ├─────────────────────────────────────────────────────────────────┤ │ App外部打开 │ myapp://home │ │ Push通知跳转 │ myapp://order/123 │ │ 第三方分享回调 │ myapp://share/callback?code=xxx │ │ 广告投放追踪 │ myapp://product/456?utm_source=ad │ │ App间跳转 │ weixin:// │ │ Universal Links │ https://app.example.com/product/1 │ └─────────────────────────────────────────────────────────────────┘ */Info.plist 配置!-- Info.plist URL Scheme 配置 --?xml version="1.0" encoding="UTF-8"?!DOCTYPEplistPUBLIC"-//Apple//DTD PLIST 1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd"plistversion="1.0"dict!-- URL Types 配置 --keyCFBundleURLTypes/keyarray!-- 主 Scheme --dictkeyCFBundleURLName/keystringcom.yourcompany.myapp/stringkeyCFBundleURLSchemes/keyarraystringmyapp/string/arraykeyCFBundleTypeRole/keystringEditor/string/dict!-- 微信回调 Scheme --dictkeyCFBundleURLName/keystringweixin/stringkeyCFBundleURLSchemes/keyarraystringwx1234567890abcdef/string/array/dict!-- 支付宝回调 Scheme --dictkeyCFBundleURLName/keystringalipay/stringkeyCFBundleURLSchemes/keyarraystringap2021123456789012/string/array/dict/array!-- 可以打开的外部 Scheme(iOS 9+需要白名单) --keyLSApplicationQueriesSchemes/keyarraystringweixin/stringstringwechat/stringstringalipay/stringstringmqq/stringstringweibo/string/array/dict/plistUniversal Links 配置/* ═══════════════════════════════════════════════════════════════════ Universal Links ═══════════════════════════════════════════════════════════════════ 优势对比: ┌────────────────────┬─────────────────┬─────────────────────────┐ │ 特性 │ URL Scheme │ Universal Links │ ├────────────────────┼─────────────────┼─────────────────────────┤ │ 唯一性 │ ❌ 可能冲突 │ ✅ 域名唯一 │ │ 回退网页 │ ❌ 不支持 │ ✅ 可打开网页 │ │ 微信等App内打开 │ ❌ 被屏蔽 │ ✅ 可直接打开 │ │ 安全性 │ ❌ 任何App可用 │ ✅ 域名验证 │ │ 配置复杂度 │ 简单 │ 需服务端配合 │ └────────────────────┴─────────────────┴─────────────────────────┘ */// MARK: - apple-app-site-association 文件 (放在服务器 .well-known 目录)/* { "applinks": { "apps": [], "details": [ { "appID": "TEAMID.com.yourcompany.myapp", "paths": [ "/product/*", "/order/*", "/user/*", "/share/*", "NOT /api/*", "NOT /static/*" ] } ] }, "webcredentials": { "apps": ["TEAMID.com.yourcompany.myapp"] } } */// MARK: - Entitlements 配置/* ?xml version="1.0" encoding="UTF-8"? !DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" plist version="1.0" dict keycom.apple.developer.associated-domains/key array stringapplinks:app.yourcompany.com/string stringapplinks:www.yourcompany.com/string /array /dict /plist */URL 处理入口// MARK: - SceneDelegate (iOS 13+)classSceneDelegate:UIResponder,UIWindowSceneDelegate{varwindow:UIWindow?// 冷启动时通过 URL 打开funcscene(_scene:UIScene,willConnectTo session:UISceneSession,options connectionOptions:UIScene.ConnectionOptions){// 处理 URL Schemeifleturl=connectionOptions.urlContexts.first?.url{handleIncomingURL(url,isLaunch:true)}// 处理 Universal LinksifletuserActivity=connectionOptions.userActivities.first,userActivity.activityType==NSUserActivityTypeBrowsingWeb,leturl=userActivity.webpageURL{handleUniversalLink(url,isLaunch:true)}}// App 在后台时通过 URL 唤起funcscene(_scene:UIScene,openURLContextsURLContexts:SetUIOpenURLContext){guardleturl=URLContexts.first?.urlelse{return}handleIncomingURL(url,isLaunch:false)}// Universal Linksfuncscene(_scene:UIScene,continueuserActivity:NSUserActivity){guarduserActivity.activityType==NSUserActivityTypeBrowsingWeb,leturl=userActivity.webpageURLelse{return}handleUniversalLink(url,isLaunch:false)}// MARK: - URL HandlingprivatefunchandleIncomingURL(_url:URL,isLaunch:Bool){print("📥 [URL] Incoming:\(url.absoluteString), isLaunch:\(isLaunch)")// 检查是否是第三方回调ifhandleThirdPartyCallback(url){return}// 使用路由系统处理DispatchQueue.main.asyncAfter(deadline:.now()+(isLaunch?0.5:0)){URLRouter.shared.open(url:url,from:self.topViewController())}}privatefunchandleUniversalLink(_url:URL,isLaunch:Bool){print("🔗 [UniversalLink] Incoming:\(url.absoluteString)")// 转换为内部 URL 格式letinternalURL=convertToInternalURL(url)handleIncomingURL(internalURL,isLaunch:isLaunch)}privatefunchandleThirdPartyCallback(_url:URL)-Bool{letscheme=url.scheme??""// 微信回调ifscheme.hasPrefix("wx"){returnWXApi.handleOpen(url,delegate:WXApiManager.shared)}// 支付宝回调ifscheme.hasPrefix("ap"){AlipaySDK.defaultService().processOrder(withPaymentResult:url){resultin// 处理支付结果}returntrue}returnfalse}privatefuncconvertToInternalURL(_url:URL)-URL{// https://app.yourcompany.com/product/123 - myapp://product/123varcomponents=URLComponents(url:url,resolvingAgainstBaseURL:false)components?.scheme="myapp"components?.host=nilreturncomponents?.url??url}privatefunctopViewController()-UIViewController?{guardletrootVC=window?.rootViewControllerelse{returnnil}returnfindTopViewController(from:rootVC)}privatefuncfindTopViewController(from viewController:UIViewController)-UIViewController{ifletnav=viewControlleras?UINavigationController,lettopVC=nav.topViewController{returnfindTopViewController(from:topVC)}iflettab=viewControlleras?UITabBarController,letselectedVC=tab.selectedViewController{returnfindTopViewController(from:selectedVC)}ifletpresentedVC=viewController.presentedViewController{returnfindTopViewController(from:presentedVC)}returnviewController}}// MARK: - AppDelegate (iOS 12 及以下兼容)@mainclassAppDelegate:UIResponder,UIApplicationDelegate{varwindow:UIWindow?// iOS 12 及以下的 URL 处理funcapplication(_app:UIApplication,open url:URL,options:[UIApplication.OpenURLOptionsKey:Any]=[:])-Bool{print("📥 [URL] Open:\(url.absoluteString)")// 获取来源App信息letsourceApp=options[.sourceApplication]as?Stringletannotation=options[.annotation]print(" Source App:\(sourceApp??"unknown")")// 处理URLhandleIncomingURL(url)returntrue}// Universal Links (iOS 12 及以下)funcapplication(_application:UIApplication,continueuserActivity:NSUserActivity,restorationHandler:@escaping([UIUserActivityRestoring]?)-Void)-Bool{guarduserActivity.activityType==NSUserActivityTypeBrowsingWeb,leturl=userActivity.webpageURLelse{returnfalse}handleUniversalLink(url)returntrue}privatefunchandleIncomingURL(_url:URL){URLRouter.shared.open(url:url,from:nil)}privatefunchandleUniversalLink(_url:URL){// 转换并处理handleIncomingURL(url)}}URLRouter 路由方案完整 URLRouter 实现// MARK: - URL路由核心实现importUIKitimportCombine// ═══════════════════════════════════════════════════════════════════// MARK: - 路由协议定义// ═══════════════════════════════════════════════════════════════════/// 路由结果publicenumRouteResult{caseviewController(UIViewController)casehandler(()-Void)caseredirect(URL)casefailed(RouteError)}/// 路由错误publicenumRouteError:Error,LocalizedError{casenotFound(path:String)caseinvalidParameter(String)caserequireLogincaseaccessDenied(reason:String)casecustom(Error)publicvarerrorDescription:String?{switchself{case.notFound(letpath):return"路由未找到:\(path)"case.invalidParameter(letmsg):return"参数无效:\(msg)"case.requireLogin:return"需要登录"case.accessDenied(letreason):return"访问被拒绝:\(reason)"case.custom(leterror):returnerror.localizedDescription}}}/// 导航类型publicenumNavigationType{casepush// Push导航casepresent// 模态弹出casepresentFullScreen// 全屏模态casereplace// 替换当前页caseroot// 设为根控制器casecustom((UIViewController,UIViewController)-Void)}/// 路由选项publicstructRouteOptions{publicvarnavigationType:NavigationTypepublicvaranimated:Boolpublicvarcompletion:(()-Void)?publicinit(navigationType:NavigationType=.push,animated:Bool=true,completion:(()-Void)?=nil){self.navigationType=navigationTypeself.animated=animatedself.completion=completion}publicstaticlet`default`=RouteOptions()publicstaticletpresent=RouteOptions(navigationType:.present)publicstaticletpresentFullScreen=RouteOptions(navigationType:.presentFullScreen)}/// 路由处理器协议publicprotocolRouteHandler{funchandle(url:URL,params:RouteParams)-RouteResult}/// 路由拦截器协议publicprotocolRouteInterceptor{/// 拦截器优先级(数字越大越先执行)varpriority:Int{get}/// 是否拦截该路由funcshouldIntercept(url:URL,params:RouteParams)-Bool/// 处理拦截(返回nil表示继续路由,返回URL表示重定向)funcintercept(url:URL,params:RouteParams,completion:@escaping(InterceptResult)-Void)}/// 拦截结果publicenumInterceptResult{case`continue`// 继续路由caseredirect(URL)// 重定向casereject(RouteError)// 拒绝路由casehandled// 已处理,不需要继续}extensionRouteInterceptor{publicvarpriority:Int{return0}}// ═══════════════════════════════════════════════════════════════════// MARK: - 路由参数// ═══════════════════════════════════════════════════════════════════/// 路由参数容器publicstructRouteParams{/// URL中的路径参数 (如 /product/:id 中的 id)publicvarpathParams:[String:String]=[:]/// URL中的查询参数 (如 ?page=1size=10)publicvarqueryParams:[String:String]=[:]/// 额外传递的对象参数publicvarextra:[String:Any]=[:]/// 来源URLpublicvarsourceURL:URL?/// 获取String参数publicfuncstring(_key:String)-String?{returnpathParams[key]??queryParams[key]??extra[key]as?String}/// 获取Int参数publicfuncint(_key:String)-Int?{ifletvalue=string(key){returnInt(value)}returnextra[key]as?Int}/// 获取Bool参数publicfuncbool(_key:String)-Bool{ifletvalue=string(key){return["true","1","yes"].contains(value.lowercased())}returnextra[key]as?Bool??false}/// 获取任意类型参数publicfuncgetT(_key:String)-T?{returnextra[key]as?T}/// 合并参数publicmutatingfuncmerge(_other:[String:Any]){for(key,value)inother{ifletstringValue=valueas?String{queryParams[key]=stringValue}else{extra[key]=value}}}}// ═══════════════════════════════════════════════════════════════════// MARK: - URLRouter 核心实现// ═══════════════════════════════════════════════════════════════════publicfinalclassURLRouter{publicstaticletshared=URLRouter()// MARK: - Properties/// 路由表:path pattern - handlerprivatevarrouteMap:[String:(URL,RouteParams)-RouteResult]=[:]/// 正则路由表(支持更复杂的匹配)privatevarregexRouteMap:[(NSRegularExpression,(URL,RouteParams)-RouteResult)]=[]/// 拦截器列表privatevarinterceptors:[RouteInterceptor]=[]/// 全局路由监听privatevarglobalListeners:[(URL,RouteParams)-Void]=[]/// 404处理器privatevarnotFoundHandler:((URL)-UIViewController)?/// 错误处理器privatevarerrorHandler:((RouteError,URL)-Void)?/// URL Schemeprivateletscheme:String/// 线程安全锁privateletlock=NSRecursiveLock()// MARK: - Initializationpublicinit(scheme:String="myapp"){self.scheme=scheme}// ═══════════════════════════════════════════════════════════════// MARK: - 路由注册// ═══════════════════════════════════════════════════════════════/// 注册页面路由(返回ViewController)@discardableResultpublicfuncregister(_pattern:String,handler:@escaping(URL,RouteParams)-UIViewController?)-Self{letnormalizedPattern=normalizePattern(pattern)lock.lock()defer{lock.unlock()}routeMap[normalizedPattern]={url,paramsinifletvc=handler(url,params){return.viewController(vc)}return.failed(.notFound(path:pattern))}print("🛣️ [Router] Registered:\(normalizedPattern)")returnself}/// 注册动作路由(执行闭包)@discardableResultpublicfuncregisterAction(_pattern:String,handler:@escaping(URL,RouteParams)-Void)-Self{letnormalizedPattern=normalizePattern(pattern)lock.lock()defer{lock.unlock()}routeMap[normalizedPattern]={url,paramsinreturn.handler{handler(url,params)}}print("⚡ [Router] Registered Action:\(normalizedPattern)")returnself}/// 注册正则路由@discardableResultpublicfuncregisterRegex(_pattern:String,handler:@escaping(URL,RouteParams)-UIViewController?)-Self{guardletregex=try?NSRegularExpression(pattern:pattern,options:[])else{print("❌ [Router] Invalid regex pattern:\(pattern)")returnself}lock.lock()defer{lock.unlock()}regexRouteMap.append((regex,{url,paramsinifletvc=handler(url,params){return.viewController(vc)}return.failed(.notFound(path:pattern))}))print("🔤 [Router] Registered Regex:\(pattern)")returnself}/// 注册重定向@discardableResultpublicfuncregisterRedirect(from source:String,to destination:String)-Self{letnormalizedSource=normalizePattern(source)lock.lock()defer{lock.unlock()}routeMap[normalizedSource]={[weakself]url,paramsinguardletdestURL=self?.buildURL(destination,params:params)else{return.failed(.notFound(path:destination))}return.redirect(destURL)}print("↪️ [Router] Registered Redirect:\(source)-\(destination)")returnself}/// 批量注册@discardableResultpublicfuncregisterBatch(_routes:[(String,(URL,RouteParams)-UIViewController?)])-Self{for(pattern,handler)inroutes{register(pattern,handler:handler)}returnself}// ═══════════════════════════════════════════════════════════════// MARK: - 路由导航// ═══════════════════════════════════════════════════════════════/// 通过URL字符串导航publicfuncopen(_urlString:String,from sourceVC:UIViewController?=nil,options:RouteOptions=.default,extra:[String:Any]?=nil){guardleturl=URL(string:urlString)else{print("❌ [Router] Invalid URL:\(urlString)")return}open(url:url,from:sourceVC,options:options,extra:extra)}/// 通过URL对象导航publicfuncopen(url:URL,from sourceVC:UIViewController?=nil,options:RouteOptions=.default,extra:[String:Any]?=nil){// 解析参数varparams=parseParams(from:url)ifletextra=extra{params.merge(extra)}params.sourceURL=urlprint("🔍 [Router] Opening:\(url.absoluteString)")// 通知全局监听器notifyListeners(url:url,params:params)// 执行拦截器链runInterceptors(url:url,params:params){[weakself]resultinswitchresult{case.continue:self?.performRouting(url:url,params:params,from:sourceVC,options:options)case.redirect(letnewURL):self?.open(url:newURL,from:sourceVC,options:options)case.reject(leterror):self?.handleError(error,for:url)case.handled:print("✅ [Router] Handled by interceptor")}}}/// 获取目标ViewController(不进行导航)publicfuncviewController(forurlString:String,extra:[String:Any]?=nil)-UIViewController?{guardleturl=URL(string:urlString)else{returnnil}returnviewController(for:url,extra:extra)}publicfuncviewController(forurl:URL,extra:[String:Any]?=nil)-UIViewController?{varparams=parseParams(from:url)ifletextra=extra{params.merge(extra)}letresult=matchRoute(url:url,params:params)switchresult{case.viewController(letvc):returnvcdefault:returnnil}}/// 检查URL是否可以被处理publicfunccanOpen(_urlString:String)-Bool{guardleturl=URL(string:urlString)else{returnfalse}returncanOpen(url:url)}publicfunccanOpen(url:URL)-Bool{letparams=parseParams(from:url)letresult=matchRoute(url:url,params:params)ifcase.failed=result{returnfalse}returntrue}// ═══════════════════════════════════════════════════════════════// MARK: - 拦截器管理// ═══════════════════════════════════════════════════════════════/// 添加拦截器publicfuncaddInterceptor(_interceptor:RouteInterceptor){lock.lock()defer{lock.unlock()}interceptors.append(interceptor)// 按优先级排序interceptors.sort{$0.priority$1.priority}}/// 移除拦截器publicfuncremoveInterceptorT:RouteInterceptor(_type:T.Type){lock.lock()defer{lock.unlock()}interceptors.removeAll{$0isT}}// ═══════════════════════════════════════════════════════════════// MARK: - 配置// ═══════════════════════════════════════════════════════════════/// 设置404处理器publicfuncsetNotFoundHandler(_handler:@escaping(URL)-UIViewController){notFoundHandler=handler}/// 设置错误处理器publicfuncsetErrorHandler(_handler:@escaping(RouteError,URL)-Void){errorHandler=handler}/// 添加全局路由监听publicfuncaddListener(_listener:@escaping(URL,RouteParams)-Void){lock.lock()defer{lock.unlock()}globalListeners.append(listener)}// ═══════════════════════════════════════════════════════════════// MARK: - Private Methods// ═══════════════════════════════════════════════════════════════/// 标准化路由模式privatefuncnormalizePattern(_pattern:String)-String{varresult=pattern.lowercased()if!result.hasPrefix("/"){result="/"+result}ifresult.hasSuffix("/")result.count1{result.removeLast()}returnresult}/// 解析URL参数privatefunc