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(
     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(
    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 可以跳轉,也就是新的導航, 之前的導航重置啦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章