Angular,Lazy Loading,模块间依赖关系,最佳实践

时间:2018-03-13 07:27:06

标签: angular angular2-routing lazy-loading angular2-services angular-router

我有以下示例应用程序布局:

app.module
 |
 |-- dashboard.module                 (lazy loaded)
 |-- dashboard-routing.module         (imported by dashboard.module)
 |-- dashboard.service                (provided by dashboard.module)
 |-- dashboard.component              (depends on dashboard.service)
 |
 |-- notifications.module             (lazy loaded)
 |-- notifications-routing.module     (imported by notifications.module)
 |-- notifications.service            (provided by notifications.module)
 |-- notifications.component          (depends on notifications.service)
 |
 |-- my-notifications-dashboard-widget.component <- this should be added
 |
 |-- ...

每个模块(dashboardnotifications...)都会延迟加载。

现在我想添加一些MyNotificationsDashboardWidgetComponent,这取决于notifications.service。然后,这个小部件应该被注入dashboard.service

只要提供的WidgetComponent位于dashboard.module内,这种方法效果很好。但是如果我把组件放在延迟加载的notifications.module中,它当然不起作用。

现在我想知道,如何以良好的,面向模块的方式解决这个问题。

我的要求:

  • 应该只有一个 notifications.servicenotifications.componentmy-notifications-dashboard-widget.component应该使用

  • 大型模块(dashboardnotifications)应该是延迟加载的。

  • dashboard.module不应该对notifications.module和/或my-notifications-dashboard-widget.component了解任何信息。

思路:

我想,我需要一些预先加载的模块,其中包含my-notifications-dashboard-widget.component。因此,在dashboard.module加载之前, 已知此组件及其提供商

但是我必须把notifications.service放在这个模块中,我想?!

当前(非工作)方法的一些代码片段:

dashboard.service:

@Injectable()
export class DashboardService {
    constructor(@Inject(DASHBOARD_WIDGET) widgets) {
        // inject all provided widgets
        widgets.forEach(w => console.log(w.type));
    }
}

notifications.module:

@NgModule({
    imports: [ NotificationsRoutingModule ],
    declarations: [ NotificationsComponent ],
    providers: [
        NotificationsService,
        // this is currently not working, because of lazy loading
        {
            provide: DASHBOARD_WIDGET,
            useValue: {
                type: 'my-notifications'
                component: MyNotificationsDashboardWidgetComponent
            },
            multi: true
        }
    ],
    entryComponents: [ MyNotificationsDashboardWidgetComponent ]
})
export class NotificationsModule { }

notifications.component:

@Component({ ... })
export class NotificationsComponent {
    constructor(service: NotificationsService) { ... }
}

我的通知的仪表板-widget.component:

@Component({ ... })
export class MyNotificationsDashboardWidgetComponent {
    constructor(service: NotificationsService) { ... }
}

3 个答案:

答案 0 :(得分:3)

请记住,NgModule级别的提供程序始终使用根注入器进行注册。只有一个根注入器,并且使用根注入器注册的任何提供程序都可在应用程序范围内使用。这个规则的例外是延迟加载的模块 - 延迟加载的模块创建自己的范围注入器。换句话说,使用延迟加载的模块注册的任何提供程序都不会向根注入器注册。

为了使用根注入器注册提供者,无论它们是否是延迟加载的,都应遵循forRoot约定。我们的想法是forRoot方法返回带有提供程序的模块,以便AppModule可以导入它。

@NgModule({
    providers: []
})
export class MyModule {
     static forRoot(): ModuleWithProviders {
         return {
             NgModule: MyModule,
             providers: [
                 MyProvider
             ]
         }
     }
}

注意:您还可以使用forChild方法返回没有提供程序的模块。此方法的目的是使您可以重用模块中的组件(包括延迟加载的模块),而无需实际注册任何提供程序。

答案 1 :(得分:2)

在这种情况下,您只创建一个小部件,将来可能需要添加更多小部件。

解决方案是,创建anohter共享模块并在该模块中创建此类组件。新的共享模块将独立于其他模块及其中的所有组件。

在其他模块中导入共享模块并根据需要使用它。

答案 2 :(得分:-1)

感谢您的灵感。我现在已经创建了一个有效的解决方案它对我来说仍然有点奇怪,有些事情对我来说是一个谜,但至少它是有效的。

如果您可以发表评论并附上一些其他信息,那就太好了

app.module:

@NgModule({
    imports: [
        AppRoutingModule,
        NotificationModule.forRoot()
    ]
})
export class AppModule { }

APP-routing.module:

@NgModule({
    imports: [
        RouterModule.forRoot([
            {
                path: '/dashboard',
                loadChildren: 'app/dashboard/dashboard.module#DashboardModule'
            },
            {
                path: '/notifications',
                loadChildren: 'app/notification/notification.module#NotificationModule'
            }
        ])
    ]
})
export class AppRoutingModule { }

dashboard.module:

@NgModule({
    providers: [ DashboardService ]
})
export class DashboardModule {
    provideWidget(type: string, component: Type<any>) {
        return [
            {
                provide: ANALYZE_FOR_ENTRY_COMPONENTS,
                useValue: component,
                multi: true
            },
            {
                provide: DASHBOARD_WIDGET,
                useValue: {
                    type: type,
                    component: component
                },
                multi: true
            }
        ];
    }
}

notification.module:

@NgModule({
    imports: [
        NotificationRoutingModule
    ],
    declarations: [
        NotificationComponent
    ]
})
export class NotificationModule {
    static forRoot(): ModuleWithProviders {
        return {
            ngModule: NotificationRootModule,
            providers: [ ]
        }
    }
}

通知-root.module:

@NgModule({
    declarations: [
        TestWidgetComponent
    ],
    providers: [
        NotificationService,
        DashboardModule.provideWidget('TEST', TestWidgetComponent)
    ]
})
export class NotificationRootModule { }

问题:

我想我完全不理解forRootModuleWithProviders。我不明白为什么我必须为此创建一个单独的NotificationRootModule

现在TestWidgetComponentNotificationService已编译到app模块中。虽然一切都按预期工作。

但是:如果我在ngModule: NotificationModule中使用NotificationModule.forRoot(),那么它会放置整个模块(包括NotificationRoutingModule和{app}模块中的NotificationComponent}。 (我可以看到浏览器在打开/notifications URL时不会延迟加载任何内容。)

现在我不知道providers属于哪里。我可以将它们放在@NgModule({ providers })的{​​{1}}内或NotificationRootModule内。它似乎没有任何区别。

我希望你能开导我。

谢谢!