更新: 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 哦