如何在导出的 Web 组件中封装 Angular 应用程序的全局 CSS?
How to encapsulate Angular app's global CSS within exported web component?
目标
我有一个 Angular 11 站点,其中包含使用 Angular Material 的大型表单组件 -- 我们称它为 myExportableComponent
。我想像往常一样在我的站点中使用 myExportableComponent
,但 也 将其导出为通用 Web 组件(a.k.a。“自定义元素”,a.k.a. "Angular 元素").这样,它就可以在任何第 3 方网站上使用,无论他们的技术堆栈如何,而且它的工作方式与在我的网站上完全相同。
myExportableComponent
应该像我网站中的普通 Angular 组件一样工作 - 它应该可以通过 styles.scss
中的全局 CSS 和组件特定 CSS 在 my-exportable.component.scss
.
中找到
当用作 Web 组件时,myExportableComponent
也应该像正常情况一样受到全局和组件特定 CSS 的影响,但是 CSS 中的 none 应该会泄漏出来并影响放置它的第 3 方页面的其余部分。这是我正在努力的部分。
设置
我在主项目的子项目中设置了 myExportableComponent
。我可以单独构建它并获取生成的 js/css 文件并在另一个站点中成功使用该 Web 组件。像这样:
<link rel="stylesheet" href="myExportableComponentStyles.css">
<script type="text/javascript" src="myExportableComponent.js"></script>
<div class="mat-app-background mat-typography">
<my-exportable-component></my-exportable-component>
</div>
我已经 myExportableComponent
通过进入 angular.json
并将其指向主应用程序的 theme.scss
文件(生成 Angular material 来与我的主站点共享所有样式主题)和 styles.scss
(其他全局样式)。像这样...
angular.json:
"mainApp": {
...
"styles": [
"src/theme.scss",
"src/styles.scss"
],
"myExportableComponentApp": {
...
"styles": [
"src/theme.scss", //NOT "projects/myExportableComponentApp/src/theme.scss"
"src/styles.scss" //NOT "projects/myExportableComponentApp/src/styles.scss"
],
问题
全局 CSS 当 Web 组件在另一个站点上使用时未封装。例如,如果我在 styles.scss
中为我的应用添加全局 link 样式,它也会影响消费网站上的所有 link。 theme.scss
(Material) 样式似乎没有泄漏,但我认为那是因为那些东西已经使用了自定义选择器。我担心如果消费站点碰巧也使用 Angular Material,它 会 泄漏。
想法 #1:封装设置
我想尝试在 myExportableComponent
上使用 encapsulation: ViewEncapsulation.ShadowDom
,但是 A) 破坏了一些 Material 样式和图标,并且 B) 这并不是我真正想要的,因为那是组件级封装,我的问题是全局CSS。实际上,我的组件特定 CSS 似乎已经用默认设置封装了。
想法 #2:将全局 CSS 定位到我的 HTML 仅
如果我将 class 添加到我的应用程序的 HTML 标记中,并让 Web 组件的使用者也将其添加到该组件周围,我就可以定位我所有的全局 CSS 规则只影响那些容器内的东西,比如:
.special-wrapper a {
/* some style*/
}
这实际上确实可以从第 3 方页面封装我的全局样式,但它会以某种方式翻转样式,以便全局样式在不应该的情况下优先于特定于组件的样式。
想法 #3:在我的全局 CSS?
上使用 :host 或 :host-context 选择器
老实说,我很难掌握这些,我无法让它们在我的应用程序中执行任何操作。
想法 #4:构建时脚本?
当 myExportableComponent
的子项目构建时,如果我可以自动从 theme.scss
和 styles.scss
中剪切所有全局 CSS 并将其粘贴到顶部my-exportable.component.scss
,我想那行得通。然后我的“全局”样式将被封装到我的 Web 组件中,因为它们位于特定于组件的文件中。
当我运行ng build myExportableComponentApp --prod
时,是否可以做这样的事情?我不知道从哪里开始。
我乐于接受建议!
这是我最后做的:
从 angular.json
中删除站点的全局 style.scss
用于子项目。
创建一个继承自myExportableComponent
的新组件,但除此之外没有代码。它也没有自己的模板——它共享基本组件的模板。实际上,您甚至不需要从子项目的模块中导出该组件。这样主app默认只能用base one就好了
新子组件的styleUrls有两个文件。首先是特定于组件的样式表。它的设置与正常一样,但在它内部只有一个引用主站点全局样式表的导入,类似于 @import '../../../../../../src/app/styles.scss';
。我想直接引用它,但一直收到奇怪的构建错误,所以将它导入“普通”组件特定样式表的这个技巧起作用了。第二个文件是对 base 组件的常规组件特定样式表的引用。
在为 Web 组件做 createCustomElement
时,使用子组件而不是父组件。
最重要的是,在构建通用 Web 组件时,主站点的全局 css 将包含在 myExportableComponent
的特定组件 css 的顶部,而不是全球范围内。然后全局样式被封装,并且仍然可以像正常一样被特定于组件的样式覆盖。
我想对包含 Material 主题的 theme.scss
做同样的事情,但似乎只有当它真正在全球范围内时才有效 (https://github.com/angular/components/issues/3386)。现在这对我来说不是一个交易破坏者。
优点:
- 主站点项目没有特殊设置或中断。它和
myExportableComponent
可以在我们的主应用程序中以完全标准的方式使用。
- 样式封装在导出的 Web 组件中的工作方式与在我们的主项目中的工作方式相同,并且不会泄漏到第 3 方站点。
- 不需要特殊的构建脚本
缺点:
- 这有点不正统,您需要确保其他开发人员没有在用作此构建解决方案管道的 dummy/inherited 文件中放置任何内容。
- 据我所知,Material 主题 css 还不可能。
目标
我有一个 Angular 11 站点,其中包含使用 Angular Material 的大型表单组件 -- 我们称它为 myExportableComponent
。我想像往常一样在我的站点中使用 myExportableComponent
,但 也 将其导出为通用 Web 组件(a.k.a。“自定义元素”,a.k.a. "Angular 元素").这样,它就可以在任何第 3 方网站上使用,无论他们的技术堆栈如何,而且它的工作方式与在我的网站上完全相同。
myExportableComponent
应该像我网站中的普通 Angular 组件一样工作 - 它应该可以通过 styles.scss
中的全局 CSS 和组件特定 CSS 在 my-exportable.component.scss
.
当用作 Web 组件时,myExportableComponent
也应该像正常情况一样受到全局和组件特定 CSS 的影响,但是 CSS 中的 none 应该会泄漏出来并影响放置它的第 3 方页面的其余部分。这是我正在努力的部分。
设置
我在主项目的子项目中设置了 myExportableComponent
。我可以单独构建它并获取生成的 js/css 文件并在另一个站点中成功使用该 Web 组件。像这样:
<link rel="stylesheet" href="myExportableComponentStyles.css">
<script type="text/javascript" src="myExportableComponent.js"></script>
<div class="mat-app-background mat-typography">
<my-exportable-component></my-exportable-component>
</div>
我已经 myExportableComponent
通过进入 angular.json
并将其指向主应用程序的 theme.scss
文件(生成 Angular material 来与我的主站点共享所有样式主题)和 styles.scss
(其他全局样式)。像这样...
angular.json:
"mainApp": {
...
"styles": [
"src/theme.scss",
"src/styles.scss"
],
"myExportableComponentApp": {
...
"styles": [
"src/theme.scss", //NOT "projects/myExportableComponentApp/src/theme.scss"
"src/styles.scss" //NOT "projects/myExportableComponentApp/src/styles.scss"
],
问题
全局 CSS 当 Web 组件在另一个站点上使用时未封装。例如,如果我在 styles.scss
中为我的应用添加全局 link 样式,它也会影响消费网站上的所有 link。 theme.scss
(Material) 样式似乎没有泄漏,但我认为那是因为那些东西已经使用了自定义选择器。我担心如果消费站点碰巧也使用 Angular Material,它 会 泄漏。
想法 #1:封装设置
我想尝试在 myExportableComponent
上使用 encapsulation: ViewEncapsulation.ShadowDom
,但是 A) 破坏了一些 Material 样式和图标,并且 B) 这并不是我真正想要的,因为那是组件级封装,我的问题是全局CSS。实际上,我的组件特定 CSS 似乎已经用默认设置封装了。
想法 #2:将全局 CSS 定位到我的 HTML 仅
如果我将 class 添加到我的应用程序的 HTML 标记中,并让 Web 组件的使用者也将其添加到该组件周围,我就可以定位我所有的全局 CSS 规则只影响那些容器内的东西,比如:
.special-wrapper a {
/* some style*/
}
这实际上确实可以从第 3 方页面封装我的全局样式,但它会以某种方式翻转样式,以便全局样式在不应该的情况下优先于特定于组件的样式。
想法 #3:在我的全局 CSS?
上使用 :host 或 :host-context 选择器老实说,我很难掌握这些,我无法让它们在我的应用程序中执行任何操作。
想法 #4:构建时脚本?
当 myExportableComponent
的子项目构建时,如果我可以自动从 theme.scss
和 styles.scss
中剪切所有全局 CSS 并将其粘贴到顶部my-exportable.component.scss
,我想那行得通。然后我的“全局”样式将被封装到我的 Web 组件中,因为它们位于特定于组件的文件中。
当我运行ng build myExportableComponentApp --prod
时,是否可以做这样的事情?我不知道从哪里开始。
我乐于接受建议!
这是我最后做的:
从
angular.json
中删除站点的全局style.scss
用于子项目。创建一个继承自
myExportableComponent
的新组件,但除此之外没有代码。它也没有自己的模板——它共享基本组件的模板。实际上,您甚至不需要从子项目的模块中导出该组件。这样主app默认只能用base one就好了新子组件的styleUrls有两个文件。首先是特定于组件的样式表。它的设置与正常一样,但在它内部只有一个引用主站点全局样式表的导入,类似于
@import '../../../../../../src/app/styles.scss';
。我想直接引用它,但一直收到奇怪的构建错误,所以将它导入“普通”组件特定样式表的这个技巧起作用了。第二个文件是对 base 组件的常规组件特定样式表的引用。在为 Web 组件做
createCustomElement
时,使用子组件而不是父组件。
最重要的是,在构建通用 Web 组件时,主站点的全局 css 将包含在 myExportableComponent
的特定组件 css 的顶部,而不是全球范围内。然后全局样式被封装,并且仍然可以像正常一样被特定于组件的样式覆盖。
我想对包含 Material 主题的 theme.scss
做同样的事情,但似乎只有当它真正在全球范围内时才有效 (https://github.com/angular/components/issues/3386)。现在这对我来说不是一个交易破坏者。
优点:
- 主站点项目没有特殊设置或中断。它和
myExportableComponent
可以在我们的主应用程序中以完全标准的方式使用。 - 样式封装在导出的 Web 组件中的工作方式与在我们的主项目中的工作方式相同,并且不会泄漏到第 3 方站点。
- 不需要特殊的构建脚本
缺点:
- 这有点不正统,您需要确保其他开发人员没有在用作此构建解决方案管道的 dummy/inherited 文件中放置任何内容。
- 据我所知,Material 主题 css 还不可能。