我有一个单一的 Angular 15 应用程序,不使用路由器。它内部的组件数量在增加,我想将大部分组件分解成一个单独的模块并单独加载它们。
我的应用程序已经有一个带有“加载”进度条的初始屏幕,该进度条随着从服务器获取数据而前进。我希望主 AppModule 包含一组最小的组件来启动,然后我将加载其余组件作为进度条监控的启动任务之一。
现状……
app.module.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 { }
shared.module.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 { }
lazy.modules.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 { }
虽然我想问的问题是如何从启动画面中进行延迟加载,但我什至无法构建上面的内容。
LazyModule
组件有很多构建错误,比如:
'mat-slider' 不是已知元素
(但 MatSliderModule 是 LazyModule 的一个包含进口)
找不到名称为“number”的管道。
(但 CommonModule 也是一个包含的导入)
无法绑定到“ngModel”,因为它不是“输入”的已知属性。
(但 FormsModule 是包含的导入)
'my-second' 不是已知元素:
(惰性组件 MyFirstComponent 的 HTML 引用 MySecondComponent)
如果我在
LazyModule
导入中的 SharedModule
之后添加 AppModule
,上述所有问题都会消失。奇怪的是,即使不将 ./lazy.module
添加到“导入”列表,它也会仅通过导入 LazyModule
来构建。但是,当然,这会使我返回到我试图分解的单个整体 main.js
文件。所以两个问题:
如何从
LazyModule
中分离出 AppModule
并构建它?
我在初始化时调用什么来加载 LazyModule,我如何收到加载完成的通知?
Update1: 我设法通过将
mat-slider
的导入从 MatSliderModule
移动到 lazy.module
来修复第一个错误(shared.module
未知)。这对我来说毫无意义,因为 mat-slider
只被一个惰性组件使用。这种技术对“找不到数字管道”(CommonModule
)或“无法绑定 ngModel”(FormsModule
)没有帮助。
您可以在没有路由的情况下延迟加载所有组件。 您只需要手动解析组件工厂并编译组件即可。 对于 angular 13,它更自动,代码看起来像:
export class AppComponent {
@ViewChild("formComponent", { read: ViewContainerRef })
formComponent!: ViewContainerRef;
constructor(private compiler: Compiler, private injector: Injector) {}
async loadForm() {
const { LazyFormModule } = await import("./lazy-form.component");
const moduleFactory = await this.compiler.compileModuleAsync(
LazyFormModule
);
const moduleRef = moduleFactory.create(this.injector);
const componentFactory = moduleRef.instance.getComponent();
this.formComponent.clear();
this.formComponent.createComponent(componentFactory, {ngModuleRef: moduleRef});
}
}
export class LazyFormModule {
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
getComponent() {
return LazyFormComponent
}
}
有关更多示例,请查看 https://www.wittyprogramming.dev/articles/lazy-load-component-angular-without-routing/ 或 https://medium.com/@ckyidr9/lazy-load-feature -modules-without-routing-in-angular-9-ivy-220851cc7751
为了让事情正常进行,我建议你只留下带有启动画面的空白应用程序并删除所有依赖模块(例如,评论它们)。然后使用第 1 部分中的技术一个接一个地添加惰性加载模块。所有必需的模块都应该转到惰性模块,而不是应用程序模块。
只有当你看到一些重复的模块时,你才应该为公共逻辑添加惰性共享模块(不要在应用程序中也使用它)。