Bri*_*ite 6 lazy-loading angular
我有一个不使用 Router 的整体 Angular 15 应用程序。它内部的组件数量不断增加,我想将大部分组件分解成一个单独的模块并单独加载它们。
我的应用程序已经有一个带有“加载”进度条的启动屏幕,该进度条随着从服务器获取数据而前进。我希望主 AppModule 包含一组最小的组件来开始工作,然后我将加载其余组件作为进度条监视的启动任务之一。
目前的情况...
应用程序模块.ts:
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from "@angular/forms";
import {MatProgressBarModule} from '@angular/material/progress-bar';
import {MatSidenavModule} from "@angular/material/sidenav";
import {SharedModule} from "./shared.module";
import {AppComponent} from './app.component';
import {ResizeableSidenavDirective} from "../components/resizeable-sidenav.directive";
import {SplashScreenComponent} from "../components/splash-screen/splash-screen.component";
@NgModule({
declarations: [
AppComponent,
SplashScreenComponent,
ResizeableSidenavDirective,
],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormsModule,
MatProgressBarModule,
MatSidenavModule,
SharedModule
],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)
共享.模块.ts:
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {CommonModule} from "@angular/common";
import {LeafletModule} from "@asymmetrik/ngx-leaflet";
import {AppComponent} from './app.component';
import {MapViewComponent} from "../components/map-view/map-view.component";
import {Toaster} from "../components/toaster";
@NgModule({
declarations: [
MapViewComponent,
Toaster,
],
imports: [
BrowserAnimationsModule,
CommonModule,
LeafletModule,
],
exports: [
MapViewComponent,
Toaster,
],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class SharedModule { }
Run Code Online (Sandbox Code Playgroud)
惰性模块.ts:
import {NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {CommonModule} from "@angular/common";
import {FormsModule} from "@angular/forms";
import {MatDialogModule} from "@angular/material/dialog";
import {MatSliderModule} from "@angular/material/slider";
import {SharedModule} from "./shared.module";
... big list of component imports ...
@NgModule({
declarations: [
MyFirstComponent,
MySecondComponent,
MyThirdComponent,
...
],
imports: [
CommonModule,
FormsModule,
MatDialogModule,
MatSliderModule,
SharedModule
],
exports: [
MyFirstComponent,
MySecondComponent,
MyThirdComponent,
...
],
providers: [],
bootstrap: [],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class LazyModule { }
Run Code Online (Sandbox Code Playgroud)
虽然我想问的问题是如何从启动屏幕内进行延迟加载,但我什至无法构建上述内容。组件中存在许多构建错误,LazyModule如下所示:
“mat-slider”不是已知元素
(然而 MatSliderModule 是 LazyModule 的包含导入)
未找到名称为“number”的管道。
(然而 CommonModule 也是包含的导入)
无法绑定到“ngModel”,因为它不是“input”的已知属性。
(但 FormsModule 是包含的导入)
'my-second' 不是已知元素:
(惰性组件 MyFirstComponent 的 HTML 引用 MySecondComponent)
如果我在导入中添加LazyModuleafter ,所有上述问题都会消失。奇怪的是,它也将仅通过导入来构建,甚至无需添加到“导入”列表中。但是,当然,这让我回到了我试图分解的单个整体文件。那么两个问题:SharedModuleAppModule./lazy.moduleLazyModulemain.js
我如何分离LazyModule并AppModule构建它?
我在初始化中调用什么函数来加载 LazyModule,以及如何收到加载完成的通知?
Update1:我设法通过将from的导入移至来修复第一个错误(mat-slider未知)。这对我来说毫无意义,因为除了一个惰性组件之外,它没有在任何地方使用。此技术对于“未找到数字管道”( ) 或“无法绑定 ngModel”( )没有帮助。MatSliderModulelazy.moduleshared.modulemat-sliderCommonModuleFormsModule
尤金给出了非常好的答案,这对我弄清楚我想做什么有很大帮助。
正如我所说的,我的问题实际上并不是我想知道的。它应该是这样的:
我可以将源代码分成几个部分,以便在“启动”启动过程中先下载必需的内容,然后再下载其余部分吗?
这个问题的简短答案是“不”。我的调查显示,可能有一个很长的答案,围绕文件中的包含/排除指令angular.json,但它似乎很复杂,可能会带来维护问题,并且通常与 Angular 的设计相反。
最后,我使用内置的 Angular 支持来创建和加载模块。我下面将介绍的内容与我在其他地方找到的片段没有显着不同。然而,这些对我来说都没有意义,因为我错过了一个每个人似乎都认为理所当然的非常基本的概念:
代码会自动分离到独立加载的 JavaScript 文件中。
Angular 就是为你做这件事的。与 C/C++ 手动将文件分组到.a库中或将 Java 分组到.jar文件不同,使用 Angular,您不需要显式实例化任何想要单独加载的内容。只要不直接创建该类型的对象,就可以导入new它以便访问类的字段和方法。
无论组件是分组为模块还是声明standalone:true,技巧都是不直接引用它们。独立的片段被收集到单独的.js文件中,包括仅由它们使用的代码node_modules,然后可以按需加载(也称为“延迟加载”)。
这是一个例子:
发布.模块.ts:
import {CommonModule} from "@angular/common";
import {FormsModule} from "@angular/forms";
import {NgModule} from "@angular/core";
import {PublishStartComponent} from "./publish-start/publish-start.component";
import {PublishContinueComponent} from "./publish-continue/publish-continue.component";
import {PublishFinalComponent} from "./publish-final/publish-final.component";
import {PublishResultsComponent} from "./publish-results/publish-results.component";
import {RegionPlacerComponent} from "./region-placer.component";
@NgModule({
declarations: [
PublishStartComponent,
PublishContinueComponent,
PublishFinalComponent,
PublishResultsComponent,
RegionPlacerComponent,
],
imports: [
CommonModule, // |number
FormsModule, // ngModel
]
})
export class PublishModule {
getPublishStartFactory() { return PublishStartComponent }
getPublishContinueFactory() { return PublishContinueComponent }
getPublishFinalFactory() { return PublishFinalComponent }
getPublishResultsFactory() { return PublishResultsComponent }
getRegionPlacerFactory() { return RegionPlacerComponent }
}
Run Code Online (Sandbox Code Playgroud)
在 Angular 15 中,延迟加载文件并访问该文件只需两行:
const {PublishModule} = await import ("../../components/publish/publish.module")
const pminstance = createNgModule(PublishModule, this.injector).instance
Run Code Online (Sandbox Code Playgroud)
然后可以使用“工厂”返回类型来实例化这些类:
let thing = new (pmintstance.getPublishStartFactory())(...)
Run Code Online (Sandbox Code Playgroud)
或者它可以传递给需要类型的函数:
this.dialogService.open(pmintstance.getPublishStartFactory(), {...})
Run Code Online (Sandbox Code Playgroud)
就我而言,它看起来像这样:
myapp.ts:
...
import {PublishModule} from "../../components/publish/publish.module";
...
@Component({...})
export class MyApp {
...
private pmInstance: PublishModule|undefined
...
constructor(dialogService: MatDialog, injector: Injector) {...}
...
private async onPublishButtonFirstClick() {
const {PublishModule} = await import ("../../components/publish/publish.module")
this.pmInstance = createNgModule(PublishModule, this.injector).instance
let dref = this.dialogService.open(this.pmInstance.getPublishStartFactory(), {
...
})
dref.afterClosed().subscribe((rid: string) => {
if (rid == null || rid == "") return
this.regionName = dref.componentInstance.regionName
this.regionProjection = dref.componentInstance.regionType
this.imageUrl = dref.componentInstance.imageUrl!
this.imageSize = dref.componentInstance.imageSize!
const rpc = document.getElementById("overlay-container")!
const injector = Injector.create({
providers: [
{provide: 'imageUrl', useValue: this.imageUrl!},
{provide: 'imageSize', useValue: this.imageSize!},
]
})
this.regionPlacerView = this.injector.get<ViewContainerRef>(ViewContainerRef);
const rp = this.regionPlacerView.createComponent(this.pmInstance!.getRegionPlacerFactory(), {
injector: injector
})
this.regionPlacer = rp.instance
})
}
...
}
Run Code Online (Sandbox Code Playgroud)
除此之外,还有更多内容,例如为基本代码和惰性代码使用的那些组件创建“共享”模块,但无需更改其使用方式。通常,只需在基本/惰性侧访问共享模块的组件,Angular 就会负责做正确的事情(在本例中:将“共享”拆分为自己的文件,但将其作为 的一部分加载index.html)。
| 归档时间: |
|
| 查看次数: |
1256 次 |
| 最近记录: |