资讯专栏INFORMATION COLUMN

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

hidogs / 1483人阅读

摘要:模块主要解决程序路由状态改变和懒加载模块问题。本文主要解释程序启动后,是如何注册开发者定义的路由集合的,和实例化对象的。第六个重要的对象就是,提供了初始导航功能。

@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,@angular/router 会调用 Router.parseUrl(), 实际上还是调用 UrlSerializer.parse() 来把 URL 字符串 "/section-one;test=one/(nav:navigation;test=two//main:about;test=three)?query=four#frag" 解析为 UrlTree 对象:

@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

第四个点是调用 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://www.ucloud.cn/yun/96511.html

相关文章

  • 源码分析 @angular/cdk Portal

    摘要:这些依赖对象也进一步暴露了其设计思想。关键功能包括在上下文内挂载在上下文外挂载在上下文外共享数据。在构造必须依赖,所以可以直接创建嵌入视图,然后手动强制执行变更检测。提供了两个指令和。 @angular/material 是 Angular 官方根据 Material Design 设计语言提供的 UI 库,开发人员在开发 UI 库时发现很多 UI 组件有着共同的逻辑,所以他们把这些共...

    BearyChat 评论0 收藏0
  • 利用angular4和nodejs-express构建一个简单的网站(四)—angular路由初步

    摘要:为了做到这一点,我创建了一个服务提供商,通过的消息推送来实现。最后声明一个来发送修改过的对象。根组件,创建它并插入宿主页面。路由的作用是在找不到任何路由时,访问组件。定义路由数组后,用装饰器导入,并将路由数组传递给的数组。 上一篇文章对用户发来的注册和登录信息进行了处理,并实现了将注册用户信息插入到mysql数据库的数据表和从mysql数据库的数据表中查询到用户的登录信息并返回用户认证...

    Zhuxy 评论0 收藏0
  • 利用angular4和nodejs-express构建一个简单的网站(十)—好友模块

    摘要:我们从这一章开始分析这个好友模块。在中提供了和一个请求拦截器,分别用于提供数据服务路由守卫服务和拦截服务。在这个模块下共有三个组件。路由路由模块负责整个模块的全部路由。和,对应同一个组件,当导航到路径时,,的为具体的。 上一章讲解了用户登录的相关代码。用户登录成功后,就会进入好友模块,在好友模块中会根据不同的用户ID显示相应的好友列表,点击好友列表中的单个好友就会进入编辑单个好友页面,...

    BicycleWarrior 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<