Angular Lazy load学习笔记

举报
Jerry Wang 发表于 2022/05/14 19:20:22 2022/05/14
【摘要】 Lazy loading, also known as code splitting, lets you divide your JavaScript code into multiple chunks. The result is that you do not have to load all the JavaScript of the full application when a u...

Lazy loading, also known as code splitting, lets you divide your JavaScript code into multiple chunks. The result is that you do not have to load all the JavaScript of the full application when a user accesses the first page. Instead, only the chunks that are required for the given page are loaded.

懒加载可以允许我们将TypeScript编译出的JavaScript代码拆分成若干个chunk, 这样,当应用程序加载时,我们无需将整个应用所需的所有chunk都加载到浏览器中,而是可以实现按需加载的机制,即仅加载那些需要渲染的页面对应的chunk.

我们在Angular项目里执行命令行ng build,即可查看打包出来的chunk名称和对应的大小,如下图所示。

默认情况下,我们在Angular应用里编写的所有Component,会被ng build打包到一个main chunk里。比如我开发了一个MyCartComponent:

打包到main chunk后对应的JavaScript代码如下:

如何让一个Angular应用的Component支持lazy load,即将其和main chunk分开进行打包呢?

看个例子。

在AppRoutingModule里,配置路由信息时,不使用常规的Component属性,而是采用loadChildren,为某个path动态地指定要加载的Component名称:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: 'customers',
    loadChildren: () => import('./customers/customers.module').then(m => m.CustomersModule)
  },
  {
    path: 'orders',
    loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule)
  },
  {
    path: '',
    redirectTo: '',
    pathMatch: 'full'
  }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes)
  ],
  exports: [RouterModule],
  providers: []
})
export class AppRoutingModule { }

看看customers.module.ts的实现:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomersRoutingModule } from './customers-routing.module';
import { CustomersComponent } from './customers.component';

@NgModule({
  imports: [
    CommonModule,
    CustomersRoutingModule
  ],
  declarations: [CustomersComponent]
})
export class CustomersModule { }

里面导入了另一个CustomersRoutingModule:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { CustomersComponent } from './customers.component';

const routes: Routes = [
  {
    path: '',
    component: CustomersComponent
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class CustomersRoutingModule { }

这里才是常规的Angular router定义机制,path和component 字段pair的组合。

执行ng build之后,这次发现main chunk和customers以及orders chunk分别进行打包了,这说明Customers和Orders的lazy loading启用生效了。

在运行时能看得更清楚。浏览器里访问应用,因为没有打开customers或者orders页面,因此没有加载对应的chunk:

点击customers超链接:

直到此时,才观察到了customers chunk,这就是Angular Component 懒加载模式在运行时的表现效果。

Lazy loading, also known as code splitting, lets you divide your JavaScript code into multiple chunks. The result is that you do not have to load all the JavaScript of the full application when a user accesses the first page. Instead, only the chunks that are required for the given page are loaded. While navigating the storefront, additional chunks are loaded when needed.

code splitting技术发生在application build期间。

Code splitting provided by Angular is typically route-based, which means there is a chunk for the landing page, another chunk for the product page, and so on.

Angular Code split基于路由的,比如landing page分配一个代码chunk,product page分配另一个代码chunk,以此类推。

Since Spartacus is mostly CMS driven, the actual application logic for each route cannot be decided at build time. Business users will eventually alter the page structure by introducing or removing components.

在Spartacus里,每个route的应用逻辑无法在build阶段知晓,因为Spartacus是CMS驱动的,business user可以通过添加或者移除Component的方式来影响页面结构。


ConfigModule.withConfig({
      cmsComponents: {
        BannerComponent: {
          component: () =>
            import('./lazy/lazy-banner.component').then(
              (m) => m.LazyBanner
            ),
        }
      }
    }),

这个lazy-lazy-banner-component.js.map在哪里?ng build即可看到。

To make code spitting possible, your static JavaScript code (the main app bundle) should not have any static imports to code that you want to lazy load. The builder will notice that the code is already included, and as a result, will not generate a separate chunk for it. This is especially important in the case of importing symbols from libraries.

衡量lazy load的标志就是,builder为code生成单独的chunk.

在 CLI 生成的基本应用中,模块是急性加载的,这意味着它们都是由本应用启动的,Angular 会使用一个依赖注入体系来让一切服务都在模块间有效。对于急性加载式应用,应用中的根注入器会让所有服务提供者都对整个应用有效。

当使用惰性加载时,这种行为需要进行改变。惰性加载就是只有当需要时才加载模块,比如路由中。它们没办法像急性加载模块那样进行加载。这意味着,在它们的 providers 数组中列出的服务都是不可用的,因为根注入器并不知道这些模块。

当 Angular 的路由器惰性加载一个模块时,它会创建一个新的注入器。这个注入器是应用的根注入器的一个子注入器。想象一棵注入器树,它有唯一的根注入器,而每一个惰性加载模块都有一个自己的子注入器。路由器会把根注入器中的所有提供者添加到子注入器中。如果路由器在惰性加载时创建组件,Angular 会更倾向于使用从这些提供者中创建的服务实例,而不是来自应用的根注入器的服务实例。

任何在惰性加载模块的上下文中创建的组件(比如路由导航),都会获取该服务的局部实例,而不是应用的根注入器中的实例。而外部模块中的组件,仍然会收到来自于应用的根注入器创建的实例。

如下图所示:
在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。