更新: 2019-08-05 html
以前都沒有什麼用到 matrix url, 由於項目難度不大,用 queryparams 挺好的,可是越來月複雜後開始感受到 matrix 的好了.html5
要改動 matrix 比較難, 沒有相似 'merge' 啊等等的東西. node
經常使用的方式是 git
<a [routerLink]="['../',{}]" >gogo</a>
不過要留意哦, root path 就不能夠用這招了,root path should not have matrix.github
而後說到 ../ 我就氣! web
https://github.com/angular/angular/issues/13011 這個 issue 幾年前我就 like 了. 而後我也不記得那時候怎樣閃掉了. api
今天又給我與到... 我根本就不記得有過這種事情了... n 年前... 盡然沒有 fix !!! 什麼鬼嘛數組
看了幾眼原來是有 fix 的.. 只是 fix 的不全面而已 安全
relativeLinkResolution?: 'legacy' | 'corrected' 就是用來解決這類問題的 (問題不僅一種) 服務器
幸虧我此次遇到的能夠用這個 fix. 但願不要再有問題了。
Angular 的 Router 是真的很強, 可是也不少坑 ! 一堆問題沒人理.. 唉..
更新 : 2018-7-25
Base href 的奇妙
默認狀況下, ng 會爲每一個項目加上一個 base href="/"
<base href="/">
依據規範, base href 的結尾必須是 slash "/" 好比 "/en/", "/cn/"
看看下面的狀況
能夠獲取圖 <base href="/"> <img src="/images/stickman.gif" width="24" height="39"> 能夠獲取圖 <base href="/"> <img src="images/stickman.gif" width="24" height="39"> 不能夠獲取圖 <base href="/images/"> <img src="/stickman.gif" width="24" height="39"> 能夠獲取圖 <base href="/images/"> <img src="stickman.gif" width="24" height="39">
是否是有點奇葩... 反正 best pratice 就是必定要寫 base href, 若是沒有特別的就放 "/"
全部路徑都不要使用 slash "/" 做爲開始, 這樣就安全了.
要在 ng 裏獲取 base href 的值能夠注入服務 PlatformLocation.getBaseHrefFromDOM()
更新 : 2017-12-19
今天遇到一個 關於 router empty path + relative 的 bug
https://github.com/angular/angular/issues/18059
https://github.com/angular/angular/issues/13011
https://github.com/angular/angular/issues/17957
也可能這個不是 bug 是 ng 的設計思路.
總只能結果就是若是你想在 component 內使用 relative routerLink 好比 "../" or "./" or "child-path"
那麼請你確保你這個 component 所在的 router 必定要有 parent path. 若是全部 parent path 都是 empty string 那麼你就 gg.com 了.
1 imports: [RouterModule.forRoot([ 2 { path: '', pathMatch: 'full', redirectTo: '/products' }, 3 { 4 path: '', 5 children: [ 6 { 7 path: '', 8 component: HomeComponent, 9 children: [{ 10 path: '', 11 component: CategoryComponent, // <a routerLink="products" >category go</a> 《--result is products/(products) .... 12 children: [ 13 { 14 path: 'products', 15 component: ProductComponent 16 } 17 ] 18 }] 19 } 20 ] 21 } 22 ])],
下面這個就 ok
imports: [RouterModule.forRoot([ { path: '', pathMatch: 'full', redirectTo: 'dada/products' }, { path: '', children: [ { path: 'dada', // 要有 parent path component: HomeComponent, children: [{ path: '', component: CategoryComponent, // <a routerLink="products" >category go</a> OK ! children: [ { path: 'products', component: ProductComponent } ] }] } ] } ])],
更新 : 2017-11-04
lazy load vs common chunk
by default ng 會把共用的模塊放入 common chunk 裏頭, 確保即便在 lazy load 的狀況下, 模塊都不會被加載 2 次 (不重複)
可是這樣的設置並不必定就最好的,由於 lazy load 的目的原本就是最小化的加載丫.
這只是一個平衡的問題. ng 視乎只給了 2 個極端的作法, 要嘛使用 common chunk 要嘛徹底不要 common chunk
ng build --prod --no-common-chunk
更新 : 2017-10-18
angular 的 router 有一個原則, 若是你觸發一個 <a href> 或則調用 router.navigate(...) 可是最終它發現 url 沒變更,那麼什麼不會發生, route event 通通沒有運行.
還有另外一個是當 url change 時 angular 不會輕易 rebuild component, 若是它的 path 依然是激活的 angular 會保留它哦.
更新 : 2017-08-04
今天我才發現其實 Preload 不像我說的那樣, 咱們能夠在 preload 方法中把 load 這個方法保持起來.
export class AppCustomPreloader implements PreloadingStrategy { loaders: Function[] = []; preload(route: Route, load: Function): Observable<any> { this.loaders.push(load); return Observable.of(null); } }
而後在任何一個 component 注入 AppCustomePreloader 並調用 load 就能夠了.
ng 是很聰明的,若是 load() 運行時發現其模塊已經加載了, 那麼它並不會報錯. 因此鼓勵你們去使用它 .
更新 : 2017-04-05
this.router.navigate(['../../'], { relativeTo: this.activatedRoute, queryParamsHandling: 'preserve' });
<a routerLink="../../" queryParamsHandling="preserve">
queryParamsHandling and preserveFragment 能夠在移動 router 時保留當前的 queryParams and fragment 很方便哦。
queryParamsHandling 不僅是能夠 preserve, 還能夠 merge 哦
更新 : 2017-03-27
matcher
若是我想本身寫 url 匹配, 咱們能夠經過 matcher
export function matcher(segments: UrlSegment[], group: UrlSegmentGroup, route: Route) { //判斷 return { consumed: segments.slice(1), //若是你的 route 還有 child 的話,這裏要注意,只放入你所匹配到的範圍,後面的交給 child 去判斷. posParams: { Id: segments[1] } // 傳入 params, url matrix 等等 } } @NgModule({ imports: [RouterModule.forChild([ { matcher : matcher, //幫發放放進來, 這裏不要使用匿名方法或則箭頭函數哦, aot 不過 component : FirstComponent, children : [] } ])], exports: [RouterModule], }) export class DebugRoutingModule { }
更新 2017-03-05
preloading module
lazy load 的好處是 first load 很快, 由於不少 module 都沒有 load 嘛, 可是後續的操做就會變成卡卡的, 由於後來要 load 嘛.
2.1 開始 ng 支持 preloading 的概念. 就是經過 lazyload 讓你 firstload 很快以後, 立馬去預先加載其它的 module.
好比你的 first page 是一個登入界面, 用戶就能夠很快看到頁面,讓後乘着客戶在輸入密碼時,你偷偷去加載後續會用到的模塊。這樣客戶接下來的操做就不會卡卡的了.
這聽上去不錯哦.
要注意的是, ng 並不太智能, 它不會等到 browser idle 的時候纔去加載. 它會立刻去加載.
若是你的首頁有不少圖片或者視屏, ng 不會等待這些圖片視屏加載完了纔去加載其它模塊, 它會立刻去加載. 這可能會形成一些麻煩 (因項目而定, 本身作平衡哦)
@Injectable() export class MyPreloadingStrategy implements PreloadingStrategy { constructor(private route : ActivatedRoute, private router : Router) { //能夠注入 router route 任何 service 來幫助咱們判斷也不要 preload } preload(route: Route, load: () => Observable<any>): Observable<any> {
// ng 會把每個 lazyload 的 module 丟進來這個函數, 問問你是否要 preload, 若是要, 你就返回 load() 不要 preload 的話就返回 Observable.of(null); return (true) ? load() : Observable.of(null); } } @NgModule({ imports: [RouterModule.forRoot([ { path: 'home', loadChildren: "app/+home/home.module#HomeModule" }, { path: "about", loadChildren: "app/+about/about.module#AboutModule" }, { path: "contact", loadChildren: "app/+contact/contact.module#ContactModule" }, { path: "", redirectTo: "home", pathMatch: "full" } ], { preloadingStrategy: MyPreloadingStrategy })], // { preloadingStrategy: PreloadAllModules } <--ng 自帶的
exports: [RouterModule], providers: [MyPreloadingStrategy] }) export class AppRoutingModule { }
只要在 forRoot 裏添加 preloadingStrategy 就能夠了. 上面我用了一個自定義的處理, 若是你想簡單的表示所有都預加載的話,可使用 ng 提供的 PreloadAllModules
更新 2017-01-29
提醒 :
路由是有順序的, 在用 import 特性模塊時, 位置要留意.
例如, 若是你 app-routing 最後是處理 404
可是在 app-module 卻把 routing 限於特性模塊 IdentityModule, 那麼 IdentityModule 的 routing 就進不去了。由於已經被匹配掉了.
2016-08-26
參考 :
https://angular.cn/docs/ts/latest/guide/router.html#!#can-activate-guard
https://angular.cn/docs/ts/latest/api/ -@angular/router 部分
ng 路由的概念和遊覽器相似, 和 ui-router 也相似, 下面會把具體功能逐一解釋
1. html5 和 hash #
ng 默認模式是 html5, 在開發階段咱們喜歡使用 hash 模式, 這樣能夠不用部署服務器.
要從 html5 轉換成 hash 只要作一個小設定 :
(update:用 angular cli 開發的話,不須要 hash 模式了.)
2.child
和 ui-view 同樣 ng 也支持嵌套
就是一個路由的組件模板內又有另外一個路由組件
const appRoutes: Routes = [ { path: "", redirectTo: "home", pathMatch: "full" }, { path: "home", component: TopViewComponent, //view 內也有 <router-outlet> children: [ { path: "" //若是沒有設置一個空路由的話, "/home" 會報錯, 必定要 "/home/detail" 才行. }, { path: "detail", component: FirstChildViewComponent } ] } ];
3. 獲取 params ( params 是 Matrix Url 和 :Id , 要拿 search 的話用 queryParams )
class TestComponent implements OnInit, OnDestroy { //home/xx private sub : Subscription; constructor(private route: ActivatedRoute) { } ngOnInit() { //監聽變化 this.sub = this.route.params.subscribe(params => { console.log(params); //{ id : "xx" } }); //若是隻是要 get 一次 value, 用快照 console.log(this.route.snapshot.params); //{ id : "xx" } } ngOnDestroy() { this.sub.unsubscribe(); //記得要取消訂閱, 防止內存泄露 (更新 : 其實 ActivatedRoute 能夠不須要 unsubscribe,這一個 ng 會智能處理,不過養成取消訂閱的習慣也是很好的) } }
4. 導航
導航有個重要概念, 相似於遊覽器, 當咱們表示導航時 ../path, /path, path 它是配合當下的區域而作出相對反應的. 記得是 path+區域 哦.
<a [routerLink]="['data',{ key : 'value' }]" [queryParams]="{ name : 'keatkeat' }" fragment="someWhere" >go child</a>
export class TopViewComponent { constructor(private router: Router, private route: ActivatedRoute) { console.clear(); } click(): void { this.router.navigate( ["data", { key: "value" }], //data 是 child path, {key : "value"} 是 Matrix Url (矩陣 URL) 長這樣 /data;key=value { relativeTo: this.route, //表示從當前route開始, 這個只有在 path not start with / 的狀況下須要放. //通常的 queryParams, 這裏只能 override 整個對象, 若是你只是想添加一個的話,你必須本身實現保留以前的所有. queryParams: { 'name': "keatkeat" // ng 會對值調用 toString + encode 才放入 url 中, 解析時會 decode, 而後咱們本身把 str convert to 咱們要的值 }, //#座標 fragment: "someWhere",
replaceUrl : true //有時候咱們但願 replace history 而不是 push history } ); //redirect by url let redirectUrl = this.route.snapshot.queryParams["redirectUrl"]; if (redirectUrl != undefined) { this.router.navigateByUrl(redirectUrl); } else { this.router.navigate(["/admin"]); } } }
大概長這樣, 最終個結果 : /home/data;key=value?name=keatkeat#someWhere
經過指令 routerLink , 或則使用代碼均可以 (寫法有一點點的不一樣, 看上面對比一下吧)
導航使用的是數組, ["path1","path2",{ matrix : "xx" },"path3"], 你也不必定要一個 path 一個格子, ['/debug/a/b','c'] 也是 work 的
和遊覽器相似
"/path" 表示從 root 開始
"../path" 表示從當前route往上(parent)
"path" 表示從當前往下(child)
這裏有個關鍵概念, 在不一樣的 component 獲取到的 this.route 是不一樣的, 組件和 route 是配合使用的
好比上面 click() 方法,若是你放在另外一個組件,結果就不一樣了,this.route 會隨着組件和改變.
沒有 route name 的概念 (以前好像是有,不知道是否是改了..沒找到../.\), 就是用 path 來操做.
matrix url 和 params 獲取的手法是同樣的. 他的好處就是不須要把全部子孫層頁面的參數都放到 params 中,放到 matrix url 纔對嘛.
提醒 : { path : "product?name&age" } 註冊 route 的時候不要定義 queryParam. ?name&age 刪掉 (ui-router need, ng not)
5. 攔截進出
一般進入時須要認證身份,出去時須要提醒保存資料.
ng 爲咱們提供了攔截點
{ path: ":id", component: TestComponent, data: { title : "test" }, canActivate: [BlockIn], //進入 canDeactivate : [BlockOut] //出去 }
BlockIn, BlockOut 分別是2個服務
@Injectable() export class BlockIn implements CanActivate { constructor( private currentRoute: ActivatedRoute, private router: Router, ) { } canActivate(nextRoute: ActivatedRouteSnapshot, nextState: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { //this.currentRoute vs nextRoute some logiz let nextUrl = nextState.url; let currentUrl = this.router.url; return new Promise<Boolean>((resolve, reject) => { setTimeout(() => { resolve(true); },5000); }); } }
實現了 CanActivate 接口方法, 裏頭咱們能夠獲取到即將進入的路由, 這樣就足夠讓咱們作驗證了, 途中若是要跳轉頁是能夠得哦..
@Injectable() export class BlockOut implements CanDeactivate<TestComponent> { canDeactivate( component: TestComponent, route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> | Promise<boolean> | boolean { console.log("leave"); return true; } }
CanDeactivate 還多了一個 Component, 讓咱們在切出的時候能夠調用 component 內容來作檢查或者保存資料等等, 很方便呢.
還有一個叫 CanActivateChild, 依據子層來決定可否訪問... 它和 CanActivate 的區別是
canActivate(nextRoute: ActivatedRouteSnapshot) vs canActivateChild(childRoute: ActivatedRouteSnapshot)
有點像 dom 事件的 event.target vs event.currenttarget 的概念.
6. resolve
@Injectable() export class DataResolve implements Resolve<String> { constructor(private router: Router) { } resolve(route: ActivatedRouteSnapshot): Observable<any> | Promise<any> | any { console.log("masuk"); if ("xx" === "xx") { return "xx"; } else { this.router.navigate(['/someWhere']); //隨時能夠跳轉哦 } } }
{ path: "home", component: TopViewComponent, resolve: { resolveData: DataResolve }, }
註冊在 route 裏
providers: [appRoutingProviders, Title, BlockIn, BlockOut, DataResolve],
還要記得註冊服務哦
ngOnInit() { console.log("here"); console.log(this.route.snapshot.data); }
在 onInit 裏面就可使用啦.
提醒 : 和 ui-router 不一樣的時, ng 的 resolve 和 data 是不會滲透進子路由的,可是咱們在子路由裏調用 this.route.parent.... 來獲取咱們想要的資料.
7. set web browser title
這個和 router 沒有直接關係,只是咱們一般會在還 route 時改動 browser title 因此記入在這裏吧
ng 提供了一個 service 來處理這個title
import { BrowserModule, Title } from '@angular/platform-browser'; providers: [appRoutingProviders, Title, BlockIn, BlockOut, DataResolve] constructor(private route: ActivatedRoute, private titleService: Title) { this.titleService.setTitle("data"); }
註冊 & 注入 service 就能夠用了
8.auxiliary routes / multi view
參考 :
const appRoutes: Routes = [ { path: "home", children: [ { path: "detail", }, { path: "popup", outlet : "popup" } ] }, { path: "chat", outlet : "chat" } ];
結構提醒 :
根層是 /home(chat:chat) 而不是 /(home//chat:chat)
子層是 /home/(detail//popup:popup) 而不是 /home/detail(popup:popup)
要留意哦.
// create /team/33/(user/11//aux:chat) router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: 'chat'}}]); // remove the right secondary node router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: null}}]);
生成的方式.
9.異步加載特性模塊
首先特性模塊和主模塊的 routing 設置不一樣
一個用 .forRoot, 一個用 .forChild 方法
export const routing: ModuleWithProviders = RouterModule.forChild(routes);
要異步加載特性模塊的話,很是簡單.
在主路由填入 loadChildren 屬性,值是模塊的路徑#類的名字
const appRoutes: Routes = [ { path: "", redirectTo: "/home", pathMatch: "full" }, { path: "home", component: HomeComponent }, { path: "product", loadChildren: "app/product/product.module#ProductModule" //ProductModule 是類的名字, 若是是用 export default 的話,這裏能夠不須要代表 } ];
在特性模塊的路由, 把 path 設置成空
const routes: Routes = [ { path: "", component: ProductComponent } ];
這樣就能夠啦.
小記:
1.Router : 用於 redirect 操做
2.ActivateRoute : 用於獲取 data, params 等等
3.Route : 就是咱們每次註冊時寫的資料咯, 裏面有 data, path 哦