@angular/router 源码分析之注册路由

news/2024/4/19 3:25:19

@angular/router 模块主要解决程序路由状态改变和懒加载模块问题。

比如,程序从路由状态 state1: /advisors/1/households/1 转变为路由状态 state2: /advisors/1/accounts/2,需要实例化的组件集合也从 components1: Advisor+Household 转变为 components2: Advisor+Account(准确的说应该是先是 Module 的实例化,然后才是组件的实例化),这个过程是如何实现的?

另外,对于按需加载的模块,又该如何加载该模块,并且将该模块显示在对应位置处?

@angular/router 模块就是用来解决路由状态改变和懒加载模块问题的。本文主要解释程序启动后,@angular/router 是如何注册开发者定义的路由集合的,和实例化 Router 对象的。

程序启动后,即调用 PlatformRef.bootstrapModule(AppModule) 后,会执行导入的 RouterModule.forRoot(routes: Routes) 来合并 RouterModule 提供的服务,且 routes 路由集合是由开发者自定义的,比如:

routes: Routes = [{path: 'advisors/:id', component: AdvisorComponent, children: [{path: 'households/:id', component: HouseholdComponent},{path: 'accounts/:id', component: AccountComponent},]},
];

一起看看 RouterModule 能给我们提供哪些重点对象吧:RouterModule.forRoot(routes)

第一个对象是来自于 @angular/common 的 Location,用来表示浏览器的 url,并提供了 forward(),back(),go() 等重要方法用来改变 url,同时提供了 HashLocationStrategyPathLocationStrategy 两种策略生成是否带有 '#' 的 url,至于为何需要两种不同风格的 url 原因可以看 中文官网描述

第二个对象是序列化 URL 的对象 UrlSerializer,@angular/router 使用 UrlTree 对象存储一个 URL,比如 '/advisors/1/accounts/2?type=loan#fragment',并且 UrlTree 对象又使用 UrlSegmentGroup 对象来表示 URL 的 path 部分,这里 UrlSegmentGroup 表示的就是 '/advisors/1/accounts/2' 这部分,UrlSegmentGroup 对象也可以存储 '/advisors/1/accounts/2/(user/john//bank:abc)?type=loan#fragment' 这样的多重 URL,该多重 URL 可以表示为两个 URL: '/advisors/1/accounts/2/user/john?type=loan#fragment''/advisors/1/accounts/2/abc?type=loan#fragment'(该 URL 的出口 outlet 是 bank),虽然是多重 URL,但只需要用一个对象 UrlSegmentGroup 就可以存储,而 UrlSegmentGroup 又使用 Segment 对象来表示当前 group 内的每一个 '/' 之间的部分。Url 的格式可见下图:

url format

对于上图中的 URL,@angular/router 会调用 Router.parseUrl(), 实际上还是调用 UrlSerializer.parse() 来把 URL 字符串 '/section-one;test=one/(nav:navigation;test=two//main:about;test=three)?query=four#frag' 解析为 UrlTree 对象:

url-tree

@angular/router 使用 UrlTree 对象来存储 URL 字符串,并使用 UrlSerializer 来解析和序列化 URL。这块知识点还是很重要的。

第三个对象 Router,也是 @angular/router 模块中最重要的对象,使用 setupRouter 方法来初始化 Router,初始化逻辑主要是它的 构造函数,开发者自定义的 routes 集合 也是作为依赖来构造 Router 对象。第一个点就是首先调用 createEmptyUrlTree 方法创建一个 空的 UrlTree;第二个点就是实例化一个路由加载器 loader,当开发者定义了 route.loadChildren 属性时,该 loader 就会使用 loader.load() 方法去异步加载模块,所以该 loader 对象是用来解决懒加载问题的;第三个点是调用 createEmptyState 方法创建一个空 RouterState,RouterState 对象表示当前激活路由的状态(RouterState is a tree of activated routes.),它也是一个树形数据结构,用来存储 当前激活路由 的数据,该树的节点使用 ActivatedRoute 对象表示,比如对于上文中开发者定义的路由列表,当 URL 为 '/advisors/1/households/1' 时,这时 RouterState 对象表示的状态树,如下红色显示部分的子树,而每一个包含组件的层级即是 ActivatedRoute

router_state

第四个点是调用 processNavigations() 执行路由状态切换,实际上 @angular/router 的作用就是控制路由状态的切换,所以 整个 @angular/router 的核心代码就是 processNavigations() 方法。该方法订阅了一个 BehaviorSubject 对象,只要该 BehaviorSubject 流对象弹射出一个新值,就会运行 executeScheduledNavigation(),不管是不是刷新 URL,都会运行 runNavigate(),所以精确的说,runNavigate() 这一百行左右代码才是 @angular/router 包最最核心的代码。这一百来行代码具体分为几个步骤:

  • 1. Apply redirects(relative/absolute)
  • 2. Construct router state by current URL(这段也就是第二篇文章将要探讨的 查找路由 逻辑)
  • 3. PreActivation: Run Guard3. PreActivation: Run Resolver
  • 4. Activation: Activate Components(这段也就是第三篇文章将要探讨的 运行路由 逻辑)

第四个重要的对象就是模块工厂加载器 NgModuleFactoryLoader,该对象来自于 @angular/core 核心包,主要用来辅助 RouterConfigLoader 对象,懒加载模块时可以异步加载远程模块。

第五个重要的对象就是提供了预加载对象 RouterPreloader,用来预加载所有懒加载模块,从而提高性能。

第六个重要的对象就是 RouterInitializer,提供了初始导航功能。当程序首次初始化和启动时,调用 RouterInitializer.appInitializer()RouterInitializer.bootstrapListener() 来进行初始化导航,最后还是调用 Router.initialNavigation() 来首次导航到 URL 对应的 RouterState

所以,@angular/router 首次初始化时,提供的最重要对象是 Router,其他一切对象和逻辑都是围绕着 Router 对象展开。@angular/router 是如何根据当前 URL 查找到对应的 route 的呢?见本系列第二篇文章。


https://dhexx.cn/news/show-4371749.html

相关文章

线段树维护区间最值操作与区间历史最值 P6242

题目链接 题解&#xff1a; #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<vector> #include<map> #include<set> #include<cmath> #include<queue>…

P6329 【模板】点分树 | 震波

题目连接 关于点分树的一些地方今天终于弄明白了&#xff0c;这个题理解的还可以&#xff0c;就是有点卡常&#xff0c;调了好久还是跑的很慢。。。。。 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<st…

Android中的Looper类

2019独角兽企业重金招聘Python工程师标准>>> Android中的Looper类&#xff0c;是用来封装消息循环和消息队列的一个类&#xff0c;用于在android线程中进行消息处理。handler其实可以看做是一个工具类&#xff0c;用来向消息队列中插入消息的。 (1) Looper类用来为…

圆方树、仙人掌

一、仙人掌的最大独立集&#xff08;就是拆分为多棵基环树来做的&#xff09;&#xff1a; 注意&#xff1a;使用Tarjan算法时&#xff0c;注意题目要求需不需要处理重边。 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring&…

利用联合体通过串口收发浮点数

以下介绍两种方法&#xff0c;一种是利用指针&#xff0c;一种是利用联合体&#xff0c;时间宝贵的看客可以直接跳到第二种方法这里只是讨论数据发送前和接收后的处理&#xff0c;不讨论具体发送过程方法一&#xff1a;指针UART串口有一个缺点&#xff0c;就是发送和接受是一个…

python中单斜杆和双斜杠的区别

参考&#xff1a; https://stackoverflow.com/questions/1535596/what-is-the-reason-for-having-in-python 转载于:https://www.cnblogs.com/neverguveip/p/9457213.html

广义圆方树

一、例题①&#xff1a;P4320 道路相遇 可能是因为把树封装了&#xff0c;倍增怎么写都超时。 不过倍增确实慢&#xff0c;树剖快好多。 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #inclu…

MongoDB开发技巧12例笔记一

2019独角兽企业重金招聘Python工程师标准>>> 技巧一&#xff1a;速度与完整性的折中 在关系数据库中&#xff0c;我们一般范式化设计&#xff0c;而且有时候又会有冗余来提高查询效率&#xff0c;这里说的就是范式化和反范式化两种策略取舍建议。 已商品订单为例&…

Linux 嵌入式驱动开发:移植I2C-EEPROM 驱动

2019独角兽企业重金招聘Python工程师标准>>> 1 在内核中配置I2C 驱动 Linux-2.6.32.2 对S2C2440 的I2C 接口提供了完善的驱动&#xff0c;因此我们只需在内核中配置一下即可使用。 在内核源代码目录执行&#xff1a;make menuconfig&#xff0c;进…

P5043 【模板】树同构([BJOI2015]树的同构 // P4323 [JSOI2016]独特的树叶

一、P5043 【模板】树同构&#xff08;[BJOI2015]树的同构&#xff09;&#xff1a; #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<vector> #include<map> #include&l…