
Angular Router 自定義路由匹配器 UrlMatcher 完全指南解析 URL 中的任意模式【免費(fèi)下載鏈接】angularDeliver web apps with confidence 項(xiàng)目地址: https://gitcode.com/GitHub_Trending/an/angularAngular Router 不僅支持靜態(tài)路徑、帶參數(shù)的變量路徑與**通配路由還暴露了一個(gè)強(qiáng)大的擴(kuò)展點(diǎn)——UrlMatcher自定義匹配函數(shù)。本指南將基于官方 tutorial出自 routing-with-urlmatcher.md帶你從零搭建一個(gè)識(shí)別 X原 Twitter賬號(hào) handle 的完整示例應(yīng)用并結(jié)合 Angular 路由器源碼剖析UrlMatcher、UrlMatchResult、UrlSegment的底層契約。學(xué)完本文你將能夠在 URL 結(jié)構(gòu)比pathpathMatch組合更復(fù)雜、更靈活的場(chǎng)景下獨(dú)立設(shè)計(jì)并落地自定義路由匹配方案。為什么要自定義路由匹配Angular Router 內(nèi)置的匹配策略已經(jīng)覆蓋了絕大多數(shù)導(dǎo)航需求靜態(tài)路由path: about變量路由path: user/:id通過:聲明參數(shù)通配路由path: **兜底捕獲。但當(dāng) URL 的形態(tài)不再規(guī)整例如希望匹配「以開頭的用戶名段」「以.html結(jié)尾的請(qǐng)求」「形如product/123/blue的多段組合」時(shí)僅靠path與pathMatch的表達(dá)力就不夠了。從源碼看Angular 官方為這一場(chǎng)景設(shè)計(jì)了UrlMatcher類型見 models.tsexport type UrlMatcher ( segments: UrlSegment[], group: UrlSegmentGroup, route: Route, ) UrlMatchResult | null;并且源碼注釋明確了一條硬性約束Implement a custom URL matcher forRoute.matcherwhen a combination ofpathandpathMatchis not expressive enough.Cannot be used together withpathandpathMatch.即同一個(gè)路由定義中matcher與path/pathMatch是互斥的二者只能取其一。UrlMatcher 的類型契約先讀懂三個(gè)核心類型在動(dòng)手寫代碼前理解函數(shù)簽名相關(guān)的三個(gè)類型能讓你避免大量踩坑。它們都定義于路由器的核心類型文件 models.ts1.UrlMatcher的三個(gè)入?yún)?shù)類型含義segmentsUrlSegment[]當(dāng)前待匹配的 URL 段數(shù)組每段為路徑中以/切分出的一個(gè)單元groupUrlSegmentGroup該段所屬的段組UrlSegmentGroup定義于 url_tree.tsrouteRoute正在嘗試匹配的路由配置對(duì)象2. 返回值UrlMatchResultmodels.tsexport type UrlMatchResult { consumed: UrlSegment[]; // 本路由實(shí)際消費(fèi)吃掉的 URL 段 posParams?: {[name: string]: UrlSegment}; // 可選的位置參數(shù)映射供路由參數(shù)使用 };consumed必填聲明當(dāng)前路由吞掉了哪些段剩余段交由子路由繼續(xù)匹配posParams可選等價(jià)于在 path 中用:name聲明出的命名參數(shù)只不過這里由你手動(dòng)把「某個(gè)段的一部分」映射成參數(shù)。若匹配失敗函數(shù)應(yīng)返回null路由器隨后繼續(xù)嘗試其余路由。3.UrlSegmenturl_tree.tsexport class UrlSegment { constructor( public path: string, // URL 段的路徑部分 public parameters: {[name: string]: string}, // 該段攜帶的矩陣參數(shù) ) {} ... }segments[0].path拿到的是去除了/的純文本例如 URL/Angular會(huì)被切分成一個(gè)段其path為Angular。而UrlSegment構(gòu)造函數(shù)中的parameters字段是「矩陣參數(shù)」與命名路由參數(shù)posParams不同本文示例中傳入空對(duì)象{}即可。理解了類型契約下面進(jìn)入完整的動(dòng)手實(shí)踐。教程目標(biāo)實(shí)現(xiàn) Angular 的UrlMatcher編寫一個(gè)自定義路由匹配器它能識(shí)別 URL 中的 X 賬號(hào)以符號(hào)開頭、后跟合法用戶名的段并把去掉的用戶名作為路由參數(shù)username注入到目標(biāo)組件中。第一步創(chuàng)建示例應(yīng)用使用 Angular CLI 創(chuàng)建新應(yīng)用angular-custom-route-match并生成一個(gè)profile組件。1. 創(chuàng)建帶路由的新工程ng new angular-custom-route-match創(chuàng)建過程的兩個(gè)交互式提示請(qǐng)如下選擇提示W(wǎng)ould you like to add Angular routing?→ 選擇YY 才會(huì)生成app.routes.ts與路由配置骨架提示W(wǎng)hich stylesheet format would you like to use?→ 選擇CSS。片刻后一個(gè)可直接運(yùn)行的項(xiàng)目angular-custom-route-match即創(chuàng)建完成。2. 進(jìn)入項(xiàng)目目錄并生成 profile 組件cd angular-custom-route-match ng generate component profile倉庫內(nèi)同樣提供了本教程的完整可運(yùn)行樣例位于 adev/src/content/examples/routing-with-urlmatcher含stackblitz.json可在線直接運(yùn)行后續(xù)所有文件均可對(duì)照該目錄核對(duì)。3. 改寫profile.html找到生成的profile.html用下面的模板替換占位內(nèi)容它會(huì)顯示傳入的用戶名pHello {{ username() }}!/p對(duì)應(yīng)樣例文件見 profile.html。4. 改寫app.html用下面的模板替換根組件的占位內(nèi)容它會(huì)渲染一個(gè)指向/Angular的鏈接并把路由器出口router-outlet /放入頁面h2Routing with Custom Matching/h2 Navigate to a routerLink/Angularmy profile/a router-outlet /對(duì)應(yīng)樣例見 app.html 與根組件 app.ts其為standalone組件通過imports: [RouterOutlet, RouterLink]顯式導(dǎo)入路由指令。第二步在 app.config.ts 中啟用路由并提供特性接下來把路由能力接入應(yīng)用配置app.config.ts。1. 打開app.config.ts引入provideRouter、withComponentInputBinding與路由表import {provideRouter, withComponentInputBinding} from angular/router; import {routes} from ./app.routes;2. 在providers數(shù)組中注冊(cè)路由export const appConfig: ApplicationConfig { providers: [provideRouter(routes, withComponentInputBinding())], };其中provideRouter(routes)是當(dāng)前 Angular 推薦的路由配置方式withComponentInputBinding()是一個(gè)特性啟用函數(shù)它允許 Router 把路由參數(shù)、查詢參數(shù)直接綁定為組件輸入稍后讀取username參數(shù)就依賴它。完整文件見 app.config.ts。第三步編寫自定義路由匹配器打開app.routes.ts為路由表添加自定義 matcher。倉庫樣例中的完整實(shí)現(xiàn)如下app.routes.tsimport {Routes, UrlSegment} from angular/router; import {Profile} from ./profile/profile; export const routes: Routes [ { matcher: (url) { if (url.length 1 url[0].path.match(/^[\w]$/gm)) { return {consumed: url, posParams: {username: new UrlSegment(url[0].path.slice(1), {})}}; } return null; }, component: Profile, }, ];逐段解讀這個(gè)匹配器這個(gè)自定義 matcher 是一個(gè)普通函數(shù)依次完成四件事① 校驗(yàn)段數(shù)量url.length 1只有恰好一段的 URL形如/Angular才可能是賬號(hào)頁/Angular/posts這樣的多段 URL 在此直接判失敗。② 用正則校驗(yàn)用戶名格式url[0].path.match(/^[\w]$/gm)^必須以開頭[\w]其后至少一個(gè)單詞字符字母、數(shù)字或下劃線$整個(gè)段必須恰好是賬號(hào)不允許附帶其它內(nèi)容修飾符gm多行 全局保證對(duì)完整段的嚴(yán)格匹配。③ 匹配成功則返回結(jié)果并產(chǎn)出路由參數(shù)return {consumed: url, posParams: {username: new UrlSegment(url[0].path.slice(1), {})}};consumed: url本路由消費(fèi)整段 URLposParams.username新建一個(gè)UrlSegment其path為去掉的用戶名url[0].path.slice(1)矩陣參數(shù)傳{}。該字段會(huì)成為路由參數(shù)username與內(nèi)置:username語法等價(jià)。④ 匹配失敗返回null路由器收到null后不會(huì)匹配此路由而是繼續(xù)在路由表中查找其它能夠匹配該 URL 的路由定義。與普通路由定義的等價(jià)性HELPFUL自定義 URL matcher 的表現(xiàn)與任何其它路由定義一致。你可以像對(duì)待普通路由一樣為它定義子路由children或懶加載路由loadChildren。唯一的區(qū)別僅是入口匹配條件由你控制的函數(shù)決定而不是由path字符串決定。這也是官方實(shí)現(xiàn)中matcher與path/pathMatch不能共用的原因——兩者都負(fù)責(zé)回答「這段 URL 是否屬于我」職責(zé)重疊。關(guān)于該約束的另一個(gè)佐證來自 shared.ts路由器內(nèi)部在匹配邏輯中會(huì)把「路徑是否滿足matcher」作為分流判斷的一部分使用自定義 matcher 時(shí)段的消費(fèi)情況也以返回的consumed為準(zhǔn)路由器不會(huì)再假設(shè)「消費(fèi)段數(shù) path 段數(shù)」。第四步在組件中讀取路由參數(shù)路由匹配與參數(shù)產(chǎn)出已經(jīng)就緒接下來讓Profile組件消費(fèi)username參數(shù)。打開profile.ts添加與參數(shù)同名的 required 輸入屬性完整文件見 profile.tsimport {Component, input} from angular/core; Component({ selector: app-profile, templateUrl: ./profile.html, styleUrls: [./profile.css], }) export class Profile { readonly username input.requiredstring(); }這里能直接用input.requiredstring()拿到值關(guān)鍵就在于前面在provideRouter中加入了withComponentInputBinding()特性它允許 Router 將路由參數(shù)來自posParams、查詢參數(shù)等直接綁定到路由組件的輸入于是組件無需再注入ActivatedRoute或訂閱params可觀察對(duì)象聲明式地寫一個(gè)input即可模板中的{{ username() }}會(huì)自動(dòng)輸出匹配到的用戶名。在示例鏈路中訪問/Angular→ matcher 命中并把username Angular注入Profile.username→ 頁面渲染Hello, Angular!。第五步啟動(dòng)開發(fā)服務(wù)器驗(yàn)證1. 運(yùn)行開發(fā)服務(wù)器ng serve2. 打開瀏覽器訪問http://localhost:4200應(yīng)看到首頁只有一行文字Navigate to my profile。3. 點(diǎn)擊my profile超鏈接地址欄會(huì)跳轉(zhuǎn)為http://localhost:4200/Angular頁面出現(xiàn)第二行文字Hello, Angular!。至此整條「URL → 自定義匹配 → 參數(shù)注入 → 組件渲染」鏈路驗(yàn)證成功若把鏈接改為/OtherUser同一 matcher 會(huì)解析出username OtherUser無需新增任何路由若訪問/posts之類不滿足[\w]格式的 URLmatcher 返回null路由不會(huì)命中Profile路由器繼續(xù)嘗試其它路由。更多實(shí)戰(zhàn)要點(diǎn)與拓展1. matcher 返回值的取舍只想消費(fèi)、不需要參數(shù)時(shí)可返回{consumed: url}省略posParams官方在 models.ts 中還給出了匹配.html文件的精簡(jiǎn)范例export function htmlFiles(url: UrlSegment[]) { return url.length 1 url[0].path.endsWith(.html) ? ({consumed: url}) : null; } export const routes [{ matcher: htmlFiles, component: AnyComponent }];2. 一次消費(fèi)多段consumed是數(shù)組說明一個(gè) matcher 可以吞掉多個(gè) URL 段如product/123兩段并把其中一部分映射為posParams——這是實(shí)現(xiàn)「多段動(dòng)態(tài)路由」的常用手段。3. 參數(shù)注入的兩種讀取方式聲明式withComponentInputBinding() 組件input本文所用模板可直接{{ username() }}命令式在組件中注入ActivatedRoute通過route.snapshot.paramMap.get(username)讀取適用于不啟用輸入綁定的場(chǎng)景。4. 適用邊界自定義 matcher 的核心價(jià)值是表達(dá)力與靈活性當(dāng)應(yīng)用存在大量動(dòng)態(tài)、非規(guī)整的 URL賬號(hào)頁、短鏈、舊鏈接兼容等時(shí)它比逐個(gè)枚舉path更可維護(hù)。但正如官方文檔強(qiáng)調(diào)的靜態(tài)路由、帶參路由與通配路由仍然是首選項(xiàng)——只有在pathpathMatch無法表達(dá)時(shí)才引入U(xiǎn)rlMatcher。更完整的 Router 主題如應(yīng)用內(nèi)路由導(dǎo)航、Router API 全量簽名可繼續(xù)查閱本倉庫adev/src/content/guide/routing/目錄下的路由指南以及 Router 源碼類型定義與匹配實(shí)現(xiàn)集中在 models.ts 與 shared.ts。【免費(fèi)下載鏈接】angularDeliver web apps with confidence 項(xiàng)目地址: https://gitcode.com/GitHub_Trending/an/angular創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考