Angular復習筆記7-路由(下)
這是angular路由的第二篇,也是最后一篇。繼續上一章的內容
路由跳轉
Web應用中的頁面跳轉,指的是應用響應某個事件,從一個頁面跳轉到另一個頁面的行為。對於使用Angular構建的單頁應用而言,頁面跳轉實質上就是從一個配置項跳轉到另一個配置項的行為。頁面跳轉流程如下圖所示,當某個事件引發了跳轉時,Angular會根據跳轉時的參數生成一個UrlTree實例來和配置項進行匹配,如果匹配成功,則顯示相應的組件並將新URL更新在瀏覽器地址欄中;如果匹配不成功,則報錯。本節將對Angular應用中進行頁面跳轉的兩種方式進行介紹。
使用指令進行跳轉
指令跳轉通過使用RouterLink指令來完成。該指令接收一個鏈接參數數組,Angular將根據該數組來生成UrlTree實例進行跳轉。
如果不借助於RouterLink指令而以純HTML的方式來定義超鏈接,所導致的結果是單擊超鏈接后會使得整個頁面被重新加載。
RouteLink的一個強大之處在於可以用在任何的HTML元素上。使得頁面跳轉不需要超鏈接。
此外,當RouterLink被激活時,還可以通過RouterLinkActive指令為其相應的HTML元素指定CSS類。下面的例子定義了一個CSS類.active,並通過routerLinkActive將其賦給收藏頁的鏈接。當單擊該鏈接后,.active類將被應用到<a>標簽上。示例代碼如下:
RouterLinkActive指令除可以作用於routerLink所在的元素之外,還可以作用於這些元素的任意祖先元素。當該祖先元素下的任意routerLink處於激活狀態時,該祖先元素都將獲得routerLinkActive指定的CSS類。下面的例子不管當前是處於聯系人列表頁還是收藏頁,<nav>標簽都將獲得.active類。示例代碼如下:
使用代碼跳轉
RouterLink僅僅相應click事件,如果需要其他形式的跳轉,在可以使用Router對象的Router.navigateByUrl()或其兄弟方法Router.navigate()來完成。下面的例子實現了在進入聯系人列表頁1秒后自動跳轉到收藏頁的功能。示例代碼如下:
Router.navigateByUrl()和Router.navigate()的不同之處在於傳入的參數不同,前者需要傳入一個表示url的字符串或UrlTree類型的參數,后者和RouterLink指令一樣,需要一個鏈接參數數組。
這兩個方法除可以通過第一個參數來指定目標配置項外,還支持用extras參數定義跳轉的具體行為。例如,如果想在不改變URL的情況下完成跳轉,則可以通過以下代碼來完成:
關於extras參數的其他用法,感興趣的讀者可以參考官方文檔來了解更多的內容,在此不再贅述。
路由參數
在“組件”章節中介紹了如何使用@Input裝飾器向組件傳遞數據,除此之外,Angular路由還提供了路由參數的功能,允許通過URL向組件傳遞數據。
path參數
顧名思義,Path參數是通過解析URL的path部分來獲取參數的。在定義一個配置項的path屬性時,可以使用“/”字符來對path屬性進行分段,如果一個分段以“:”字符開頭,則URL中與該分段進行匹配的部分將作為參數傳遞到組件中。下面的代碼為聯系人詳情頁的路由配置項,其定義了一個名為id的Path參數,對於http://localhost:3000/detail/1,參數id的值為1;對於http://localhost:3000/detail/2,參數id的值則為2;依此類推。
本例中path的分段數是2,只有URL解析出來的分段數和path的分段數一致時,才能得到匹配。
給路由參數賦值,除可以直接在瀏覽器地址欄中輸入URL外,還可以通過RouterLink指令或者跳轉方法來完成:
在組件中獲取Path參數,需要導入ActivatedRoute服務,該服務提供了兩種方式,分別適用於不同頁面間跳轉和同一頁面內跳轉。
Angular應用從一個頁面跳轉到另一個新的頁面,實質上是從一個配置項跳轉到另一個配置項。在這個過程中,Angular除會為配置項所對應的組件創建實例外,還會為該配置項本身創建一個ActivatedRoute實例來表示該配置項已被激活。該ActivatedRoute實例包含了一個快照(即snapshot屬性),記錄了從當前URL中解析出來的所有Path參數。下面展示了通訊錄例子中的DetailComponent組件是如何通過快照來獲取Path參數的。示例代碼如下:
此時通過http://localhost:3000/detail/1直接訪問聯系人詳情頁,可以在瀏覽器控制台上看到如下輸出,則表示通過快照獲取到的值是正確的。
創建DetailComponent組件實例
參數id的值為:1
但是當Angular在處理同一頁面內跳轉時,不會重新創建組件的實例,所以組件的構造函數和ngOnInit()方法都沒有被調用到。為了解決這個問題,ActivatedRoute服務提供了一個Observable對象,允許對參數的更新進行訂閱。示例代碼如下:
Query參數
我們也可以通過解析URL的query部分來獲取參數值。由於URL的query部分不用於和配置項進行匹配,因此每一個配置項都可以擁有任意多個查詢參數。下面的URL給聯系人列表頁定義了一個查詢參數,表示只希望在頁面上顯示5位聯系人。
http://localhost:3000/list?limit=5
與Path參數類似,Query參數同樣可以通過RouterLink指令或者跳轉方法來賦值。示例代碼如下:
Query參數的獲取,需要借助於ActivatedRoute服務提供的Observable類型對象queryParams來完成。下面的代碼片段展示了如何使用這個對象。
Matrix參數
頁面上所有組件都可以訪問Query參數的內容,如果想精准地向某一個組件傳遞參數,則需要使用Matrix參數。
Angular提供了Matrix參數,它通過在鏈接參數數組中插入一個對象來進行賦值。示例代碼如下:
Angular會將該對象的屬性轉化為以“;”為分隔符的鍵值對,拼接到與該對象左邊最近的URL分段上。依據上述鏈接參數數組生成的URL如下,DetailComponent組件和AlbumComponent組件都將獲得不同的參數值:http://localhost:3000/detail/6;after=2015-01-01;before=2015-12-31/album;after=2016-01-01;before=2016-12-31
這種在一個URL分段內使用“;”分隔鍵值對的方式稱為MatrixURI,由互聯網之父TimBerners-Lee於1996年提出。根據其定義,每一個URL分段都可以擁有任意多個鍵值對,每個鍵值對只為其所在的分段服務。雖然MatrixURI一直沒有進入HTML標准,但它能夠清晰地表示出每一個URL分段所具有的鍵值對。Angular利用這個特性,將Matrix參數精准地傳遞給分段所對應的組件。Matrix參數的獲取方式和Path參數一樣,可以通過ActivatedRoute服務提供的快照和Observable對象兩種方式來獲取,在此不再贅述。
路由攔截
Angular的路由攔截允許在從一個配置項跳轉到另一個配置項之前執行指定的邏輯,並根據執行的結果來決定是否進行跳轉。Angular提供了五類路由攔截:
- CanActivate,激活攔截。
- CanActivateChild,與CanActivate類似,用於控制是否允許激活子路由配置項。
- CanDeactivate,反激活攔截。
- Resolve,數據預加載攔截。
- CanLoad,模塊加載攔截。
關於路由攔截的內容會新開一個專題篇來講述這個功能的方方面面,這里先不贅述。
模塊的延遲加載
前文提到,Angular應用由一個根模塊和任意多個特性模塊組成。一個大型Web應用通常會包含為數不少的特性模塊,如果在首屏加載時便將所有的特性模塊加載進來,對於用戶體驗和服務器負載均會有所影響。為此,Angular路由提供了對特性模塊進行延遲加載的支持,使得只有在真正需要某一個模塊的時候,才將其加載進來。
與根模塊需要初始化各項路由服務不同,特性模塊僅需要對其路由配置進行解析,因此子路由模塊通過調用RouterModule.forChild()方法來創建。示例代碼如下:
最后,還需要對根模塊的路由配置進行修改:
loadChildren指定了延遲加載模塊的路徑,井號“#”后面的表示模塊類名。當用戶訪問地址/operate時,Angular才會加載operate.module.ts這個模塊。
模塊預加載
延遲加載使得首屏加載的資源包的大小減小很多,這些模塊只在用戶觸發的時候才開始加載。但對於某些模塊來說,觸發時才加載可能不是最優的解決方案。這樣的模塊雖然不需要首屏加載,但可能有很大的概率用戶會訪問使用到,因此最好不用等待用戶觸發,而是在首屏資源加載完后立即加載,這種加載模式就叫作預加載。預加載的模塊首先得是一個延遲加載的模塊,讓所有延遲加載的模塊加上預加載功能非常簡單,只需在根模塊的RouterModule中添加一個preloadingStrategy配置項即可。示例代碼如下:
加上這個配置后,所有的延遲加載模塊將不再等待用戶觸發,而是等待首屏資源加載完后立即加載。不過,這樣的配置顯然不夠靈活,更好的方式是對預加載的策略做自定義配置。開發者可以通過實現Angular提供的PreloadingStrategy接口自定義預加載策略。首先定義一個服務,並實現PreloadingStrategy接口。示例代碼如下:
preload()方法的返回類型必須是一個Observable對象,Angular會遍歷每一個route對象並執行preload()函數,以此來判斷該route對應的模塊是否需要進行預加載。它接受兩個參數:
route:當前處理中的route對象。
load:內置異步模塊加載器函數。上面這個例子直接返回Observable.of(null),表示不進行預加載。
MyPreloadingStrategy這個服務的目的是進行有選擇的預加載,可以根據route對象里的data屬性提供的信息進行判斷。示例代碼如下:
如果data對象里設置了preload為true,preload函數即返回load()加載器函數,這表示該路由對應的模塊需要進行預加載。這個MyPreloadingStrategy服務已經完成了,下面需要把原來的PreloadAllModules替換成新的MyPreloadingStrategy。示例代碼如下:
然后依據這個規則,控制模塊預加載就變得非常簡單了。在需要預加載的延遲加載路由配置項里進行配置:
在這個route對象里設置preload為true后,OperateModule的加載方式由原來的延遲加載變更為預加載,而其他延遲加載模塊並不會受到影響,還是會等待用戶觸發時才加載。