首页 > 解决方案 > 动态生成适当的 Angular Element 而不会膨胀构建大小?

问题描述

概括:

createCustomElement()switch case 中多次调用时,所有组件都将包含在构建中,而不仅仅是实际使用的组件。这会增加重复代码的构建大小。

细节

我有一个具有多站点架构的 Angular 11 应用程序。每个站点都有一个项目,因此它们可以独立构建并基于包含“siteCode”angular.json的相应文件生成自己的dist包。environment.ts

对于我网站中的一个大型组件——我们称之为myWidget——我还将它导出为通用 Web 组件(又名“Angular 元素”,又名“自定义元素”)以供其他网站使用。所以我在主应用程序的子项目中有myWidget,它也有自己的项目列在angular.json. 这意味着我可以运行一个只包含给定站点的myWidget的构建(显然还有核心 Angular 框架)。

myWidget 子项目的app.module.ts(简体):

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 文件,并将它们作为特定于组件的 CSS 捆绑导出的 MyWidget Web 组件中。它使用 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 组件的 shadow-root 中,而不是构建为单独的 CSS 文件,导入到消费页面的全局范围内. 这意味着我不能只在项目构建的“样式”部分中列出它们angular.json

我试图@import在 scss 上做一个动态声明,但这似乎是不可能的。

您能否以某种方式在构建过程中编写脚本以在构建时选择正确的 scss 文件?我不知道从哪里开始这样的事情。

标签: javascriptangularsassweb-componentangular-elements

解决方案


我想出了一个有趣的解决方案。

我可以摆脱对多个组件文件的需求,并@import通过使用“快捷方式”指向必要的 scss 文件而不是完整路径来有效地实现动态:

@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
  ]
}

所以现在我可以使用一个始终具有相同导入的组件,但在构建时它实际上会为正在构建的站点使用正确的文件。

有关快捷方式语法的信息:https ://www.digitalocean.com/community/tutorials/angular-shortcut-to-importing-styles-files-in-components


推荐阅读