动态生成适当的 Angular 元素而不增加构建大小?
Generate appropriate Angular Element dynamically without bloating the build size?
总结:
当 createCustomElement()
在 switch case 中被多次调用时,所有 组件都将包含在构建中,而不仅仅是实际包含的组件用过的。这会增加带有重复代码的构建大小。
详情
我有一个具有多站点架构的 Angular 11 应用程序。每个站点在 angular.json
中都有一个项目,因此它们可以独立构建并根据包含“siteCode”的适当 environment.ts
文件生成自己的 dist 包。
对于我网站中的一个重要组件——我们称之为 myWidget——我还将其导出为通用 Web 组件 (a.k.a "Angular Element", a.k.a. "custom element") 供其他网站使用。所以我在主应用程序的子项目中有 myWidget,它也有自己的项目列在 angular.json
中。这意味着我可以 运行 构建一个应该只包含 myWidget 的给定站点(显然还有核心 Angular 框架)。
app.module.ts of myWidget 子项目(简体):
import { MyWidgetSite1Component } from './my-widget/site-widgets/my-widget-site1.component';
import { MyWidgetSite2Component } from './my-widget/site-widgets/my-widget-site2.component';
import { MyWidgetSite3Component } from './my-widget/site-widgets/my-widget-site3.component';
@NgModule({
declarations: [AppComponent],
imports: [MyWidgetModule]
})
export class AppModule {
constructor(private injector: Injector) {
//Create generic web component version of myWidget. Use correct child version per site.
switch (environment.siteCode) {
case "site1": {
const myWidgetCustomElement = createCustomElement(MyWidgetSite1Component , { injector: this.injector });
customElements.define('site1-myWidget', myWidgetCustomElement);
break;
}
case "site2": {
const myWidgetCustomElement = createCustomElement(MyWidgetSite2Component , { injector: this.injector });
customElements.define('site2-myWidget', myWidgetCustomElement);
break;
}
case "site3": {
const myWidgetCustomElement = createCustomElement(MyWidgetSite3Component , { injector: this.injector });
customElements.define('site3-myWidget', myWidgetCustomElement);
break;
}
}
}
}
问题:它在构建中包含所有这三个组件,而不仅仅是将用于该站点的组件(使用 webpack 包分析器验证)。
更多背景
这里的三个站点特定的 myWidget 组件都继承自一个公共基础组件,所有真正的逻辑都在这个基础组件中,因此它们几乎相同。我这样做是为了可以为该站点加载适当的 CSS 文件并将它们捆绑 在导出的 MyWidget Web 组件中 作为组件特定 CSS。它使用 shadowDom 封装,通过这种方式 Web 组件与其插入的父页面完全隔离。所以有问题的组件看起来像这样:
我的小部件-site1.component.ts
@Component({
selector: 'my-widget-site1',
templateUrl: '../my-widget/my-widget.component.html', //Shares base myWidget template
//First file does nothing but import appropriate site's stylesheets from main project. It would
//have been better to directly include them here, but that resulted in odd build errors.
styleUrls: [
'./my-widget-site1.component.scss',
'../my-widget/my-widget.component.scss'],
encapsulation: ViewEncapsulation.ShadowDom
})
export class MyWidgetSite1Component extends MyWidgetComponent implements OnInit {
//Do not add any code here
}
我的小部件-site1.component.scss
@import '../../../../../../src/app/sites/site1/theme.scss';
@import '../../../../../../src/styles.scss';
@import '../../../../../../src/app/sites/site1/styles.scss';
结论
我能想到一些解决这个问题的一般思路:
1) 一些技巧可以动态加载所需的组件而不是在 switch case 中?
我找不到任何东西。看来只要我要 import
组件,它就会包含在构建中。
2) 完全避免每个站点有多个版本的组件?
我很想这样做,但我不知道如何解决 CSS 问题。给定站点的适当 CSS 文件需要在构建时捆绑到此组件中,因此它们被封装在 Web 组件的影子根中,而不是构建为单独的 CSS 文件导入进入消费页面的全局范围。这意味着我不能只在 angular.json
中构建的项目的“样式”部分中列出它们
我试图在 scss 上做一个动态的 @import
语句,但这似乎不可能。
您能否以某种方式将某些内容编写到构建过程中以在构建时选择正确的 scss 文件?我不知道从哪里开始做类似的事情。
我想出了一个有趣的解决方案。
我可以摆脱对多个组件文件的需要,并通过使用 'shortcut' 指向必要的 scss 文件而不是完整路径来有效地实现动态 @import
:
@import "theme";
@import "styles";
@import "site-styles";
您可以通过 angular.json
:
配置它将在其中查找指定文件的文件夹
"stylePreprocessorOptions": {
"includePaths": [
"src/app/sites/site1/", //theme.scss and site-styles.scss are here
"src/" //styles.scss is here
]
}
所以现在我可以使用一个始终具有相同导入的组件,但在构建时它实际上会为正在构建的站点使用正确的文件。
总结:
当 createCustomElement()
在 switch case 中被多次调用时,所有 组件都将包含在构建中,而不仅仅是实际包含的组件用过的。这会增加带有重复代码的构建大小。
详情
我有一个具有多站点架构的 Angular 11 应用程序。每个站点在 angular.json
中都有一个项目,因此它们可以独立构建并根据包含“siteCode”的适当 environment.ts
文件生成自己的 dist 包。
对于我网站中的一个重要组件——我们称之为 myWidget——我还将其导出为通用 Web 组件 (a.k.a "Angular Element", a.k.a. "custom element") 供其他网站使用。所以我在主应用程序的子项目中有 myWidget,它也有自己的项目列在 angular.json
中。这意味着我可以 运行 构建一个应该只包含 myWidget 的给定站点(显然还有核心 Angular 框架)。
app.module.ts of myWidget 子项目(简体):
import { MyWidgetSite1Component } from './my-widget/site-widgets/my-widget-site1.component';
import { MyWidgetSite2Component } from './my-widget/site-widgets/my-widget-site2.component';
import { MyWidgetSite3Component } from './my-widget/site-widgets/my-widget-site3.component';
@NgModule({
declarations: [AppComponent],
imports: [MyWidgetModule]
})
export class AppModule {
constructor(private injector: Injector) {
//Create generic web component version of myWidget. Use correct child version per site.
switch (environment.siteCode) {
case "site1": {
const myWidgetCustomElement = createCustomElement(MyWidgetSite1Component , { injector: this.injector });
customElements.define('site1-myWidget', myWidgetCustomElement);
break;
}
case "site2": {
const myWidgetCustomElement = createCustomElement(MyWidgetSite2Component , { injector: this.injector });
customElements.define('site2-myWidget', myWidgetCustomElement);
break;
}
case "site3": {
const myWidgetCustomElement = createCustomElement(MyWidgetSite3Component , { injector: this.injector });
customElements.define('site3-myWidget', myWidgetCustomElement);
break;
}
}
}
}
问题:它在构建中包含所有这三个组件,而不仅仅是将用于该站点的组件(使用 webpack 包分析器验证)。
更多背景
这里的三个站点特定的 myWidget 组件都继承自一个公共基础组件,所有真正的逻辑都在这个基础组件中,因此它们几乎相同。我这样做是为了可以为该站点加载适当的 CSS 文件并将它们捆绑 在导出的 MyWidget Web 组件中 作为组件特定 CSS。它使用 shadowDom 封装,通过这种方式 Web 组件与其插入的父页面完全隔离。所以有问题的组件看起来像这样:
我的小部件-site1.component.ts
@Component({
selector: 'my-widget-site1',
templateUrl: '../my-widget/my-widget.component.html', //Shares base myWidget template
//First file does nothing but import appropriate site's stylesheets from main project. It would
//have been better to directly include them here, but that resulted in odd build errors.
styleUrls: [
'./my-widget-site1.component.scss',
'../my-widget/my-widget.component.scss'],
encapsulation: ViewEncapsulation.ShadowDom
})
export class MyWidgetSite1Component extends MyWidgetComponent implements OnInit {
//Do not add any code here
}
我的小部件-site1.component.scss
@import '../../../../../../src/app/sites/site1/theme.scss';
@import '../../../../../../src/styles.scss';
@import '../../../../../../src/app/sites/site1/styles.scss';
结论
我能想到一些解决这个问题的一般思路:
1) 一些技巧可以动态加载所需的组件而不是在 switch case 中?
我找不到任何东西。看来只要我要 import
组件,它就会包含在构建中。
2) 完全避免每个站点有多个版本的组件?
我很想这样做,但我不知道如何解决 CSS 问题。给定站点的适当 CSS 文件需要在构建时捆绑到此组件中,因此它们被封装在 Web 组件的影子根中,而不是构建为单独的 CSS 文件导入进入消费页面的全局范围。这意味着我不能只在 angular.json
我试图在 scss 上做一个动态的 @import
语句,但这似乎不可能。
您能否以某种方式将某些内容编写到构建过程中以在构建时选择正确的 scss 文件?我不知道从哪里开始做类似的事情。
我想出了一个有趣的解决方案。
我可以摆脱对多个组件文件的需要,并通过使用 'shortcut' 指向必要的 scss 文件而不是完整路径来有效地实现动态 @import
:
@import "theme";
@import "styles";
@import "site-styles";
您可以通过 angular.json
:
"stylePreprocessorOptions": {
"includePaths": [
"src/app/sites/site1/", //theme.scss and site-styles.scss are here
"src/" //styles.scss is here
]
}
所以现在我可以使用一个始终具有相同导入的组件,但在构建时它实际上会为正在构建的站点使用正确的文件。