Angular项目优化之按需加载模块和组件

之前发表过一篇《Angular 项目过大?合理拆分它!》,通过路由形式的按需加载、使用DLL等方式来优化Angular项目。

本篇介绍如何不通过路由的形式来实现按需加载模块和组件。

前言

相信大家在开发中遇到过这种情况:A模块会被其他页面或模块使用,比较烦人的是这个A模块还比较大,如果按常规方式引入该模块就会导致一个问题:所有引入A模块的模块打包的时都会包含A模块的代码,导致模块体积变大,浏览器加载变慢、用户体验相对也就变差。

解决办法的核心很简单:我用的时候你再给我,不用的时候你别加载进来,这就是按需加载。

话不多说,先看效果:

Angular项目优化之按需加载模块和组件
上图中,在点击按钮的时候会按需加载模块,并使用该模块里的组件在页面中渲染,在控制台可以看到只有点击按钮的时候,所需要的模块和组件的代码才会被加载。

核心代码

话不多说直接上代码,下面代码的目的:当我点击按钮的时候 执行动态加载模块、渲染组件,并能正常的给组件传递数据、订阅组件的Output

@Component({
  selector: 'work-place',
  template:`
    <button (click)="lazyLoadModuleAndComponent()">
      lazy module and component
    </button>
    <ng-container #viewContainer></ng-container>
  `,
})
export class WorkPlaceComponent implements OnInit {
  @ViewChild('viewContainer', { read: ViewContainerRef }) containerRef!: ViewContainerRef;
​
  constructor(private complier: Compiler, private injector: Injector) {}
​
  ngOnInit() {}
​
  async lazyLoadModuleAndComponent() {
    const { InfoModule } = await import('./common-modules/info/info.module');
    // 异步编译模块,返回模块工厂
    const moduleFactory: NgModuleFactory<any> =  await this.complier.compileModuleAsync(InfoModule);
    // 通过工厂创建模块
    const moduleRef: NgModuleRef<any> = moduleFactory.create(this.injector);
    // 通过模块实例获取组件
    const component = moduleRef.instance.getInfoComponent();
    // 解析组件
    const componentFactory: any = moduleRef.componentFactoryResolver.resolveComponentFactory(component);
    // 清除container
    this.containerRef.clear();
    // 创建组件
    const componentRef: ComponentRef<InfoComponent> = this.containerRef.createComponent(componentFactory);
    // 给组件传递参数
    componentRef.instance.data = { name: 'Jack', age: 32 };
    // 子组件emit到父组件
    componentRef.instance.callbackEmit.subscribe((res) => {
      console.log(res);
    });
  }
}

上面代码的思路很简单,代码里有注释我就不多说了。

InfoModule.module.ts:

import { NgModule } from '@angular/core';
import { NzDatePickerModule } from 'ng-zorro-antd/date-picker';
import { InfoComponent } from './info.component';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
​
@NgModule({
  imports: [CommonModule, FormsModule, ReactiveFormsModule, NzDatePickerModule],
  exports: [InfoComponent],
  declarations: [InfoComponent],
  providers: [],
})
export class InfoModule {
  getInfoComponent() {
    return InfoComponent;
  }
}

这里要说的是在 InfoModule 类中定义了 getInfoComponent 方法,用于返回 InfoComponent,如果要返回其他组件,可自行编写。

分析

按需加载的核心是通过import()方法,然后通过 Complier 来动态编译模块,需要注意的是,compileModuleAsync 方法是在 JavaScript 运行时环境中使用预编译器(Precompiler)来编译模块。预编译器会在代码执行之前对模块进行静态分析和优化,以提高执行性能。这个过程不同于 JIT 编译,因为 JIT 编译是在代码运行时动态生成本地机器代码。

还有一点要注意,@ViewChild('viewContainer', { read: ViewContainerRef })要加上{ read: ViewContainerRef },否则是拿不到组件实例的引用,就无法给组件传递数据。

结束

本文涉及的代码在:github.com/Vibing/angu… 中可以找到,你可以 clone 下来安装好依赖后进行 demo 查看。

具体路由如图:

Angular项目优化之按需加载模块和组件

后面会继续针对 Angular 项目的优化和实战写一些文章或心得,欢迎评论。

原文链接:https://juejin.cn/post/7244497027758227516 作者:Ve

(0)
上一篇 2023年6月17日 上午11:03
下一篇 2023年6月17日 上午11:13

相关推荐

发表回复

登录后才能评论