angular11源码探索二十六[Router整体路由配置]
Router
配置信息
class Router {
constructor(rootComponentType: Type<any>, urlSerializer: UrlSerializer, rootContexts: ChildrenOutletContexts, location: Location, injector: Injector, loader: NgModuleFactoryLoader, compiler: Compiler, config: Routes)
// 在此NgModule中路由事件的事件流
events: Observable<Event>
// 当前的路由状态
routerState: RouterState
errorHandler: ErrorHandler
malformedUriErrorHandler: (error: URIError, urlSerializer: UrlSerializer, url: string) => UrlTree
// 如果至少有一个导航事件发生,则为True。默认false
navigated: boolean
// 提取和合并url的策略。
// 用于从AngularJS迁移到Angular。
urlHandlingStrategy: UrlHandlingStrategy
// 路由策略
routeReuseStrategy: RouteReuseStrategy
//forRoot 介绍过
onSameUrlNavigation: 'reload' | 'ignore'
// 如何从父路由到子路由合并参数
// 'emptyOnly' :为无路径或无组件路由继承父参数,数据和已解析的数据。
// 'always' :继承所有子路由的父参数,数据和已解析的数据。
paramsInheritanceStrategy: 'emptyOnly' | 'always'
// 定义路由器何时更新浏览器URL
// 默认 deferred 在成功导航后进行更新。如果希望在导航开始时更新URL
// eager 尽早更新URL,可以通过显示带有失败URL的错误消息来处理导航失败
urlUpdateStrategy: 'deferred' | 'eager'
//空路由的相对路由报错修复机制,V11默认是corrected修复已有的 具体查看ExtraOptions
relativeLinkResolution: 'legacy' | 'corrected'
config: Routes
// 当前URL
url: string
initialNavigation(): void
setUpLocationChangeListener(): void
// 检索最新的导航对象
getCurrentNavigation(): Navigation | null
// 重置用于导航和生成链接的路线配置。
resetConfig(config: Routes): void
dispose(): void
createUrlTree(commands: any[], navigationExtras: UrlCreationOptions = {}): UrlTree
// 路由绝对导航
navigateByUrl(url: string | UrlTree, extras: NavigationBehaviorOptions = {...}): Promise<boolean>
//路由相对导航
navigate(commands: any[], extras: NavigationExtras = { skipLocationChange: false }): Promise<boolean>
//把树转成绝对路径的代码
serializeUrl(url: UrlTree): string
// 输入 url字符串返回 UrlTree类型
parseUrl(url: string): UrlTree
// 是否激活
isActive(url: string | UrlTree, exact: boolean): boolean
}
urlUpdateStrategy
定义路由器何时更新浏览器URL
'deferred' | 'eager'
eager 尽早更新URL,可以通过显示带有失败URL的错误消息来处理导航失败
export class TwoComponent implements OnInit {
constructor(
private router: Router,
private location: Location
) {
// router.urlUpdateStrategy = 'eager';
this.router.events.subscribe(e=>{
if (e instanceof NavigationStart) {
console.log(location.path());
}
// 当路由被识别时触发的事件。
if(e instanceof RoutesRecognized){
console.log(location.path());
}
})
}
默认情况下返回的是上一个url
例如本来的路由是`/c`=> 跳转 `/d`, 然后两个都是返回的 `/c`
// 当设置 router.urlUpdateStrategy = 'eager' 尽早更新URL
那么跳转
NavigationStart 返回的上一个 `/c`
RoutesRecognized 返回的当前的 `/d`
Router.urlHandlingStrategy
提取和合并url的策略。
urlHandlingStrategy: UrlHandlingStrategy = new DefaultUrlHandlingStrategy();
// 提供了一种将AngularJS应用迁移到Angular的方法。
export class DefaultUrlHandlingStrategy implements UrlHandlingStrategy {
// 告诉路由器这个URL是否应该被处理。
// 当它返回true时,路由器将执行常规导航。
// 当返回false时,路由器将把路由器状态设置为空状态。所有的活性成分都会被销毁
shouldProcessUrl(url: UrlTree): boolean {
return true;
}
// 提取URL中应该由路由器处理的部分。URL的其余部分将保持不变。
extract(url: UrlTree): UrlTree {
return url;
}
// 将URL片段与URL的其余部分合并。
merge(newUrlPart: UrlTree, wholeUrl: UrlTree): UrlTree {
return newUrlPart;
}
}
案例
// 强制不处理URL更改
this.router.urlHandlingStrategy.shouldProcessUrl=(url:UrlTree)=> false;
// 我们发现设置之后所有的url的点击跳转都失效啦
this.router.navigateByUrl('/home/d')
Router.getCurrentNavigation()
Router.getCurrentNavigation() 方法检索最新的导航对象
下面是关于导航操作的信息
type Navigation = {
// 当前导航的唯一标识符。
id: number;
// 导航前传递给调用的目标URL 。这是路由器解析或对其应用重定向之前的值
initialUrl: string | UrlTree;
// 使用解析后的初始目标
extractedUrl: UrlTree;
// 应用重定向后提取的URL。该网址可能不会立即可用,因此该属性可以是undefined
finalUrl?: UrlTree;
// 触发方式
// 'imperative'-由router.navigateByUrl或者触发router.navigate
// 'popstate'-由popstate事件触发。
// 'hashchange'-由hashchange事件触发
trigger: 'imperative' | 'popstate' | 'hashchange';
// 用于控制用于此导航的策略。
extras: NavigationExtras;
// 先前成功的Navigation对象。只有一个先前的导航可用,因此该先前的Navigation对象具有null自己的值
previousNavigation: Navigation | null;
};
//修改Router导航策略的选项
interface NavigationExtras extends UrlCreationOptions, NavigationBehaviorOptions {
relativeTo?: ActivatedRoute | null
queryParams?: Params | null
fragment?: string
queryParamsHandling?: QueryParamsHandling | null
preserveFragment?: boolean
skipLocationChange?: boolean
replaceUrl?: boolean
state?: {...}
}
使用方法
constructor(
private viewport: ViewportScroller,
private router:Router
) {
router.events.subscribe(e => {
if (e instanceof NavigationEnd) {
console.log(router.getCurrentNavigation());
}
});
}
navigateByUrl
绝对路径导航
navigateByUrl(
url: string|UrlTree,
extras: NavigationBehaviorOptions = {skipLocationChange: false}
): Promise<boolean>
export interface NavigationBehaviorOptions {
/**
为true时,导航时不会将新状态推入历史记录。
this.router.navigate(['/view'], { skipLocationChange: true });
*/
skipLocationChange?: boolean;
/**
当为true时,在替换历史中的当前状态时进行导航
* this.router.navigate(['/view'], { replaceUrl: true });
*/
replaceUrl?: boolean;
/**
location.getState 可以查到
*/
state?: {[k: string]: any};
}
举个小案例
const state = {foo: 'bar'};
router.navigateByUrl('/simple', {state});
//也可以类似多出口 router.navigateByUrl('/team/22/(user/victor//right:simple)');
路径等于当前路径时应替换状态
// 点击三次的状态
router.navigateByUrl('/team/33/simple');
router.navigateByUrl('/team/22/user/victor');
router.navigateByUrl('/team/22/user/victor');
location.back();
// location.path() '/team/33/simple'
动态ID的技巧
{
path: 'd/:name', component: DComponent,
children: [
{path: 'a', component: AComponent},
{path: 'c/:age', component: CComponent, outlet: 'left'},
]
},
DComponent
<p>d works!</p>
<router-outlet></router-outlet>
<router-outlet name="left"></router-outlet>
this.router.navigateByUrl('/d/123/(a//left:c/123)')
只用left出口
this.router.navigateByUrl('/home/d/123/(left:c/123)')
// 这种情况
this.router.navigateByUrl('/home/d/123/a(left:F)')
// ()是这种在根路由添加一个left的新路由出口, 实际开发中用到的概率不高
父空路由多出口出现报错的问题
path: '', component: TwoComponent, children: [
{path: 'a', component: AComponent},
{path: 'c', component: CComponent},
{
path: 'd', component: DComponent,
outlet: 'right'
},
]
TwoComponent的html记得导入出口
<router-outlet name="right"></router-outlet>
我们发现当使用 navigateByUrl会报错
this.router.navigateByUrl('/(c//right:d)')
但是当我们父路由不是空路由就可以正常使用
{
path: 'd', component: DComponent,
children:[
{path: 'c', component: CComponent},
{path: 'a', component: AComponent,outlet:'right'},
]
},
this.router.navigateByUrl('/home/d/(c//right:a)')
这个时候如何解决这种问题呢?
其实我们可以尝试使用navigate
, 然后我试过了,发现还是报错,那么暂时我思考的点
如果多出口的情况下,父路由不应该为空
navigate
功能丰富的导航
navigate(
commands: any[],
extras: NavigationExtras = { skipLocationChange: false }
): Promise<boolean>
导航策略
interface NavigationExtras extends UrlCreationOptions, NavigationBehaviorOptions {
// inherited from router/UrlCreationOptions
relativeTo?: ActivatedRoute | null
queryParams?: Params | null
fragment?: string
queryParamsHandling?: QueryParamsHandling | null
preserveFragment?: boolean
// inherited from router/NavigationBehaviorOptions
skipLocationChange?: boolean
replaceUrl?: boolean
state?: {...}
}
案例
router.navigate(
['/a'],
{queryParams: {a: true}, queryParamsHandling: 'merge', replaceUrl: true});
相对的
router.navigate(['team', 33, 'user', 11], {relativeTo: route});
多入口
{path: 'c', component: CComponent,children:[
{path: 'd', component: DComponent,outlet:'left'},
{path: 'e', component: EComponent},
]},
router.navigate([{outlets: {left: 'd',primary:'e'}}], {relativeTo: this.route});
我们发现把值设置为null,我们的left出口没了
this.router.navigate([{outlets: {left: null,primary:'e'}}], {relativeTo: this.route});
// 当问号传参设置的undefined/null
this.router.navigate(['/home/d'],{queryParams:{name:1,age:null,page:undefined}})
//结果 /home/d?name=1
动态id跳转
[
{path: 'old/team/:id', redirectTo: 'team/:id'},
{path: 'team/:id', component: TeamCmp}
]
location.go('old/team/22');
// /team/22
resetConfig
重置用于导航和生成链接的路线配置。
记住重点**重置**和**生成**
export class OneComponent implements OnInit {
constructor(
private router:Router
) {
router.resetConfig([{path: 'show', component: DComponent}]);
}
clickDown() {
this.router.navigate(['/show']);
}
}
<a [routerLink]="['./c']">ccc</a> <br>
<a [routerLink]="['./d']">ddd</a> <br>
<button (click)="clickDown()">Click</button>
<p>
我们发现/show
可以跳转,也就是新的导航, 之前的导航重置啦
决定自己的高度的是你的态度,而不是你的才能
记得我们是终身初学者和学习者
总有一天我也能成为大佬