使用共享样式表设置 Web 组件样式

已发表: 2022-03-10
快速总结↬ Web 组件是 Web 的一项惊人的新功能,允许开发人员定义自己的自定义 HTML 元素。 当与样式指南结合使用时,Web 组件可以创建组件 API,它允许开发人员停止复制和粘贴代码片段,而只需使用 DOM 元素。 通过使用 shadow DOM,我们可以封装 Web 组件,而不必担心与页面上的任何其他样式表发生特异性战争。 但是,Web 组件和样式指南目前似乎相互矛盾。

Web 组件是 Web 的一项惊人的新功能,允许开发人员定义自己的自定义 HTML 元素。 当与样式指南结合使用时,Web 组件可以创建组件 API,它允许开发人员停止复制和粘贴代码片段,而只需使用 DOM 元素。 通过使用 shadow DOM,我们可以封装 Web 组件,而不必担心与页面上的任何其他样式表发生特异性战争。

但是,Web 组件和样式指南目前似乎相互矛盾。 一方面,样式指南提供了一组全局应用于页面的规则和样式,并确保整个网站的一致性。 另一方面,带有影子 DOM 的 Web 组件会阻止任何全局样式穿透它们的封装,从而防止样式指南影响它们。

关于 SmashingMag 的进一步阅读

  • 在基于组件的系统中实施最佳实践
  • 如何使用 LESS CSS 预处理器实现更智能的样式表
  • 深入了解 Adob​​e Edge 回流

那么,如果全球样式指南继续提供一致性和样式,甚至对于带有影子 DOM 的 Web 组件,这两者如何共存呢? 值得庆幸的是,现在有一些可行的解决方案,而且还会有更多的解决方案,使全球样式指南能够为 Web 组件提供样式。 (在本文的其余部分,我将使用术语“Web 组件”来指代具有影子 DOM 的自定义元素。)

跳跃后更多! 继续往下看↓

Web 组件中的全局样式指南样式应该是什么?

在讨论如何获得全局样式指南来设置 Web 组件的样式之前,我们应该讨论它应该和不应该尝试设置什么样式。

首先,当前 Web 组件的最佳实践表明 Web 组件(包括其样式)应该被封装,以便它不依赖任何外部资源来运行。 这使得它可以在网站内外的任何地方使用,即使样式指南不可用。

下面是一个简单的登录表单 Web 组件,它封装了它的所有样式。

 <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } p { margin: 0; } p + p { margin-top: 20px; } a { color: #1f66e5; } label { display: block; margin-bottom: 5px; } input[type="text"], input[type="password"] { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } input[type="submit"] { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <div class="container"> <form action="#"> <p> <label for="username">User Name</label> <input type="text" name="username"> </p> <p> <label for="password">Password</label> <input type="password" name="password"> </p> <p> <input type="submit" value="Login"> </p> <p class="footnote">Not registered? <a href="#">Create an account</a></p> </form> </div> </template> <script> const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); const root = this.attachShadow({mode: 'closed'}); const temp = document.importNode(template.content, true); root.appendChild(temp); } }); </script>

注意:代码示例是在 Web 组件的版本 1 规范中编写的。

带有用户名和密码的登录表单。
一个简单的登录表单 web 组件

然而,完全封装每个 Web 组件将不可避免地导致大量重复的 CSS,尤其是在设置原生元素的排版和样式时。 如果开发人员想在其 Web 组件中使用段落、锚标记或输入字段,则其样式应与网站的其余部分一样。

如果我们完全封装了 Web 组件所需的所有样式,那么用于样式化段落、锚标记、输入字段等的 CSS 将在所有使用它们的 Web 组件中复制。 这不仅会增加维护成本,还会导致用户下载量大得多。

Web 组件不应封装所有样式,而应仅封装其独特的样式,然后依赖一组共享样式来处理其他所有样式的样式。 这些共享样式本质上将成为一种 Normalize.css,Web 组件可以使用它来确保根据样式指南对本机元素进行样式设置。

在前面的示例中,登录表单 Web 组件将仅声明其两个唯一类的样式: .container.footnote 。 其余样式将属于共享样式表,并对段落、锚标记、输入字段等进行样式设置。

简而言之,样式指南不应该尝试设置 Web 组件的样式,而是应该提供一组共享样式,Web 组件可以使用这些样式来实现一致的外观。

过去如何使用外部样式表对 Shadow DOM 进行样式化

Web 组件的初始规范(称为版本 0)允许任何外部样式表通过使用::shadow/deep/ CSS 选择器来穿透 shadow DOM。 ::shadow/deep/的使用使您能够有一个样式指南穿透 shadow DOM 并设置共享样式,无论 Web 组件是否希望您这样做。

 /* Style all p tags inside a web components shadow DOM */ login-form::shadow p { color: red; }

随着最新版本的 Web 组件规范(称为版本 1)的出现,作者已经删除了外部样式表穿透影子 DOM 的能力,并且他们没有提供替代方案。 取而代之的是,哲学已经从使用龙来设计 Web 组件样式转变为使用桥接器。 换句话说,Web 组件作者应该负责允许哪些外部样式规则对其组件进行样式设置,而不是被迫允许它们。

不幸的是,这种哲学还没有真正赶上网络,这让我们有点不知所措。 幸运的是,今天有一些可用的解决方案,以及在不久的将来会出现的一些解决方案,将允许共享样式表来设置 Web 组件的样式。

你今天可以做什么

您现在可以使用三种技术来允许 Web 组件共享样式: @import 、自定义元素和 Web 组件库。

使用@import

今天将样式表引入 Web 组件的唯一本地方法是使用@import 。 虽然这可行,但它是一种反模式。 然而,对于 Web 组件,这是一个更大的性能问题。

 <template> <style> @import "styleguide.css" </style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>

通常,@ @import是一种反模式,因为它以串行方式而不是并行方式下载所有样式表,尤其是在它们嵌套的情况下。 在我们的情况下,连续下载单个样式表是无济于事的,所以理论上应该没问题。 但是当我在 Chrome 中对此进行测试时,结果显示使用@import导致页面的渲染速度比将样式直接嵌入 Web 组件时慢了半秒。

注意:由于 HTML 导入的 polyfill 与原生 HTML 导入的工作方式不同,WebPagetest.org 只能用于在原生支持 HTML 导入的浏览器(即 Chrome)中提供可靠的结果。

本机 Web 组件性能的条形图。
三个性能测试的结果表明, @import导致浏览器的渲染速度比将样式直接嵌入 Web 组件时慢半秒。

最后,@ @import仍然是一种反模式,并且可能是 Web 组件中的性能问题。 所以,这不是一个很好的解决方案。

不要使用 Shadow DOM

因为尝试为 Web 组件提供共享样式的问题源于使用影子 DOM,所以完全避免该问题的一种方法是不使用影子 DOM。

通过不使用 shadow DOM,您将创建自定义元素而不是 Web 组件(见下文),唯一的区别是缺少 shadow DOM 和范围。 您的元素将受页面样式的影响,但我们今天已经必须处理它,所以我们不知道如何处理。 webcomponentjs polyfill 完全支持自定义元素,它具有很好的浏览器支持。

自定义元素的最大好处是您今天可以使用它们创建模式库,而不必等到共享样式的问题得到解决。 并且因为 web 组件和自定义元素之间的唯一区别是 shadow DOM,所以一旦共享样式的解决方案可用,您总是可以在自定义元素中启用 shadow DOM。

如果您决定创建自定义元素,请注意自定义元素和 Web 组件之间的一些差异。

首先,因为自定义元素的样式受页面样式的影响,反之亦然,您需要确保您的选择器不会引起任何冲突。 如果您的页面已经使用样式指南,则将自定义元素的样式保留在样式指南中,并让元素输出预期的 DOM 和类结构。

通过将样式保留在样式指南中,您将为您的开发人员创建一条平滑的迁移路径,因为他们可以像以前一样继续使用样式指南,但随后在能够使用新的自定义元素时慢慢迁移到使用。 一旦每个人都在使用自定义元素,您可以将样式移动到元素内部,以便将它们保持在一起,并允许以后更轻松地重构 Web 组件。

其次,确保将任何 JavaScript 代码封装在立即调用的函数表达式 (IFFE) 中,这样您就不会将任何变量泄漏到全局范围内。 除了不提供 CSS 范围之外,自定义元素不提供 JavaScript 范围。

第三,您需要使用自定义元素的connectedCallback函数将模板 DOM 添加到元素。 根据 Web 组件规范,自定义元素不应在构造函数期间添加子元素,因此您需要将 DOM 添加到connectedCallback函数中。

最后, <slot>元素不能在 shadow DOM 之外工作。 这意味着您必须使用不同的方法为开发人员提供一种将他们的内容插入到您的自定义元素中的方法。 通常,这需要自己操作 DOM 以将其内容插入到您想要的位置。

然而,由于阴影 DOM 和带有自定义元素的浅色 DOM 之间没有分离,因此您还必须非常小心,不要对插入的 DOM 进行样式设置,因为您的元素具有级联样式。

 <!-- login-form.html --> <template> <style> login-form .container { max-width: 300px; padding: 50px; border: 1px solid grey; } login-form .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> (function() { const doc = (document._currentScript || document.currentScript).ownerDocument; const template = doc.querySelector('#login-form-template'); customElements.define('login-form', class extends HTMLElement { constructor() { super(); } // Without the shadow DOM, we have to manipulate the custom element // after it has been inserted in the DOM. connectedCallback() { const temp = document.importNode(template.content, true); this.appendChild(temp); } }); })(); </script>
 <!-- index.html --> <link rel="stylesheet" href="styleguide.css"> <link rel="import" href="login-form.html"> <login-form></login-form>

在性能方面,自定义元素几乎与未使用的 Web 组件一样快(即在head链接共享样式表并仅使用本机 DOM 元素)。 在您今天可以使用的所有技术中,这是迄今为止最快的。

自定义元素性能的条形图。
两项性能测试的结果表明,自定义元素几乎与完全不使用 Web 组件一样快。

另外:自定义元素仍然是所有意图和目的的 Web 组件。 术语“Web 组件”用于描述四种独立的技术:自定义元素、模板标签、HTML 导入和影子 DOM。

不幸的是,该术语已被用来描述使用这四种技术的任意组合的任何事物。 这导致人们对人们所说的“Web 组件”的含义产生了很多混淆。 正如 Rob Dodson 发现的那样,我发现在讨论带有和不带有 shadow DOM 的自定义元素时使用不同的术语很有帮助。

与我交谈过的大多数开发人员都倾向于将术语“Web 组件”与使用影子 DOM 的自定义元素联系起来。 因此,出于本文的目的,我在 Web 组件和自定义元素之间进行了人为的区分。

使用 Web 组件库

您今天可以使用的另一个解决方案是 Web 组件库,例如 Polymer、SkateJS 或 X-Tag。 这些库有助于填补当今支持的漏洞,还可以简化创建 Web 组件所需的代码。 它们通常还提供使编写 Web 组件更容易的附加功能。

例如,Polymer 让您只需几行 JavaScript 就可以创建一个简单的 Web 组件。 另一个好处是 Polymer 提供了使用影子 DOM 和共享样式表的解决方案。 这意味着您现在可以创建共享样式的 Web 组件。

为此,请创建他们所谓的样式模块,其中包含所有共享样式。 它可以是内联共享样式的<style>标记,也可以是指向共享样式表的<link rel=“import”>标记。 在任何一种情况下,使用<style include>标记在您的 Web 组件中包含样式,然后 Polymer 将解析样式并将它们作为内联<style>标记添加到您的 Web 组件中。

 <!-- shared-styles.html --> <dom-module> <!-- Link to a shared style sheet --> <!-- <link rel="import" href="styleguide.css"> --> <!-- Inline the shared styles --> <template> <style> :host { color: #333333; font: 16px Arial, sans-serif; } /* Rest of shared CSS */ </style> </template> </dom-module>
 <!-- login-form.html --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../shared-styles/shared-styles.html"> <dom-module> <template> <!-- Include the shared styles --> <style include="shared-styles"></style> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template> <script> Polymer({ is: 'login-form' }); </script> </dom-module>

使用库的唯一缺点是它会延迟 Web 组件的渲染时间。 这应该不足为奇,因为下载库的代码并处理它需要时间。 在库完成处理之前,页面上的任何 Web 组件都无法开始呈现。

在 Polymer 的案例中,与原生 Web 组件相比,它可以将页面渲染时间最多延迟半秒。 嵌入样式的样式模块比链接样式的样式模块稍慢,并且将样式直接嵌入到 Web 组件中与使用样式模块一样快。

同样,Polymer 并没有做任何特别的事情来使渲染时间变慢。 下载 Polymer 库并处理其所有出色的功能,以及创建所有模板绑定,都需要时间。 这只是使用 Web 组件库必须做出的权衡。

Polymer Web 组件性能的条形图。

性能测试结果表明,使用 Polymer,W​​eb 组件的渲染速度将比原生 Web 组件慢半秒。

未来的承诺

如果当前的解决方案都不适合您,请不要绝望。 如果一切顺利,在几个月到几年内,我们将能够使用几种不同的方法来使用共享样式。

自定义属性

自定义属性(或称为 CSS 变量)是在 CSS 中设置和使用变量的一种方式。 这个概念对于 CSS 预处理器来说并不新鲜,但作为原生 CSS 功能,自定义属性实际上比预处理器变量更强大。

要声明自定义属性,请使用自定义属性表示法–my-variable: value ,并使用property: var(–my-variable)访问该变量。 自定义属性像任何其他 CSS 规则一样级联,因此它的值继承自其父级并且可以被覆盖。 对自定义属性的唯一警告是,它们必须在选择器中声明,并且不能单独声明,这与预处理器变量不同。

 <style> /* Declare the custom property */ html { --main-bg-color: red; } /* Use the custom property */ input { background: var(--main-bg-color); } </style>

使自定义属性如此强大的一件事是它们能够穿透影子 DOM。 这与/deep/::shadow选择器不同,因为它们不会强行进入 Web 组件。 相反,Web 组件的作者必须在其 CSS 中使用自定义属性才能应用它。 这意味着 Web 组件作者可以创建自定义属性 API,Web 组件的消费者可以使用该 API 来应用他们自己的样式。

 <template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>

浏览器对自定义属性的支持出奇的好。 它不是您今天可以使用的解决方案的唯一原因是没有自定义元素版本 1 就没有可用的 polyfill。webcomponentjs polyfill 背后的团队目前正在努力添加它,但它尚未发布并处于构建状态,这意味着如果您将资产散列用于生产,则无法使用它。 据我了解,它将于明年初的某个时候发布。

即便如此,自定义属性也不是在 Web 组件之间共享样式的好方法。 因为它们只能用于声明单个属性值,所以 Web 组件仍然需要嵌入样式指南的所有样式,尽管它们的值被替换为变量。

自定义属性更适合主题选项,而不是共享样式。 因此,自定义属性不是我们问题的可行解决方案。

/* 使用自定义属性 */ input { background: var(–main-bg-color); } </style>

使自定义属性如此强大的一件事是它们能够穿透影子 DOM。 这与/deep/::shadow选择器不同,因为它们不会强行进入 Web 组件。 相反,Web 组件的作者必须在其 CSS 中使用自定义属性才能应用它。 这意味着 Web 组件作者可以创建自定义属性 API,Web 组件的消费者可以使用该 API 来应用他们自己的样式。

 <template> <style> /* Declare the custom property API */ :host { --main-bg-color: brown; } .one { color: var(--main-bg-color); } </style> <div class="one">Hello World</div> </template> <script> /* Code to set up my-element web component */ </script> <my-element></my-element> <style> /* Override the custom variable with own value */ my-element { --main-bg-color: red; } </style>

浏览器对自定义属性的支持出奇的好。 它不是您今天可以使用的解决方案的唯一原因是没有自定义元素版本 1 就没有可用的 polyfill。webcomponentjs polyfill 背后的团队目前正在努力添加它,但它尚未发布并处于构建状态,这意味着如果您将资产散列用于生产,则无法使用它。 据我了解,它将于明年初的某个时候发布。

即便如此,自定义属性也不是在 Web 组件之间共享样式的好方法。 因为它们只能用于声明单个属性值,所以 Web 组件仍然需要嵌入样式指南的所有样式,尽管它们的值被替换为变量。

自定义属性更适合主题选项,而不是共享样式。 因此,自定义属性不是我们问题的可行解决方案。

@apply 规则

除了自定义属性之外,CSS 还获得了@apply规则。 应用规则本质上是 CSS 世界的 mixin。 它们以与自定义属性类似的方式声明,但可用于声明属性组而不仅仅是属性值。 就像自定义属性一样,它们的值可以被继承和覆盖,并且它们必须在选择器中声明才能工作。

 <style> /* Declare rule */ html { --typography: { font: 16px Arial, sans-serif; color: #333333; } } /* Use rule */ input { @apply --typography; } </style>

浏览器对@apply规则的支持基本上是不存在的。 Chrome 目前在功能标志后面支持它(我找不到),但仅此而已。 与没有用于自定义属性的 polyfill 相同的原因,也没有可用的 polyfill。 webcomponentjs polyfill 团队也在努力添加@apply规则以及自定义属性,因此一旦新版本发布,两者都将可用。

与自定义属性不同, @apply规则是共享样式的更好解决方案。 因为它们可以设置一组属性声明,所以您可以使用它们为所有原生元素设置默认样式,然后在 Web 组件中使用它们。 为此,您必须为每个原生元素创建一个@apply规则。

但是,要使用样式,您必须手动将它们应用到每个原生元素,这仍然会在每个 Web 组件中重复样式声明。 虽然这比嵌入所有样式要好,但也不是很方便,因为它成为每个 Web 组件顶部的样板,您必须记住添加这些样板才能使样式正常工作。

 /* styleguide.css */ html { --typography: { color: #333333; font: 16px Arial, sans-serif; } --paragraph: { margin: 0; } --label { display: block; margin-bottom: 5px; } --input-text { background-color: #eaeaea; border: 1px solid grey; border-radius: 4px; box-sizing: border-box; color: inherit; font: inherit; padding: 10px 10px; width: 100%; } --input-submit { font: 16px/1.6 Arial, sans-serif; color: white; background: cornflowerblue; border: 1px solid #1f66e5; border-radius: 4px; padding: 10px 10px; width: 100%; } /* And so on for every native element */ }
 <!-- login-form.html --> <template> <style> :host { @apply --typography; } p { @apply --paragraph; } label { @apply --label; } input-text { @apply --input-text; } .input-submit { @apply --input-submit; } .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- Rest of template DOM --> </template>

由于需要大量样板文件,我不认为@apply规则会成为在 Web 组件之间共享样式的好解决方案。 不过,它们是主题化的绝佳解决方案。

在 Shadow DOM 中

根据 web 组件规范,浏览器会忽略 shadow DOM 中的任何<link rel=“stylesheet”>标签,就像在文档片段中一样对待它们。 这使我们无法在我们的 Web 组件中链接任何共享样式,这是不幸的——也就是说,直到几个月前,当 Web 组件工作组提议<link rel=“stylesheet”>标签应该在影子 DOM。 经过一周的讨论,他们都同意应该这样做,几天后他们将其添加到 HTML 规范中。

 <template> <link rel="stylesheet" href="styleguide.css"> <style> .container { max-width: 300px; padding: 50px; border: 1px solid grey; } .footnote { text-align: center; } </style> <!-- rest of template DOM --> </template>

如果这对于工作组就规范达成一致来说听起来有点太快了,那是因为它不是一个新提案。 让link标签在影子 DOM 中工作实际上是至少三年前提出的,但它被积压,直到他们能够确保它不会成为性能问题。

如果提案的接受还不够令人兴奋,Chrome 55(目前是 Chrome Canary)添加了使link标签在影子 DOM 中工作的初始功能。 甚至似乎这个功能已经登陆当前版本的 Chrome。 甚至 Safari 也已在 Safari 18 中实现了该功能。

能够在共享样式中链接是迄今为止在 Web 组件之间共享样式的最方便的方法。 您所要做的就是创建link标签,所有原生元素都将被相应地设置样式,而无需任何额外的工作。

当然,浏览器制造商实现该功能的方式将决定该解决方案是否可行。 为使其正常工作,需要对link标签进行重复数据删除,以便多个 Web 组件请求同一个 CSS 文件时只会产生一个 HTTP 请求。 CSS 也只需要解析一次,这样 Web 组件的每个实例就不必重新计算共享样式,而是重用计算的样式。

Chrome 已经做到了这两点。 因此,如果所有其他浏览器制造商都以相同的方式实现它,那么在 shadow DOM 中工作的link标签肯定会解决如何在 Web 组件之间共享样式的问题。

可构造样式表

你可能很难相信,因为我们还没有得到它,但是在影子 DOM 中工作的link标签并不是一个长期的解决方案。 相反,它只是让我们得到真正解决方案的短期解决方案:可构造样式表。

可构造样式表是允许通过构造函数在 JavaScript 中创建StyleSheet对象的提议。 然后可以通过 API 将构建的样式表添加到影子 DOM,这将允许影子 DOM 使用一组共享样式。

不幸的是,这就是我能从提案中收集到的全部信息。 我试图通过询问 Web 组件工作组来了解有关可构造样式表的更多信息,但他们将我重定向到 W3C 的 CSS 工作组的邮件列表,我再次询问,但没有人回应。 我什至无法弄清楚该提案的进展情况,因为它已经两年多没有更新了。

尽管如此,Web Components Working Group 还是使用它作为在 Web 组件之间共享样式解决方案。 希望提案将得到更新,或者 Web 组件工作组将发布有关它及其采用的更多信息。 在那之前,“长期”解决方案似乎不会在可预见的未来发生。

得到教训

经过几个月的研究和测试,我对未来充满希望。 令人欣慰的是,在多年没有在 Web 组件之间共享样式的解决方案之后,终于有了答案。 这些答案可能还要再过几年才能确定,但​​至少它们在那里。

如果您现在想使用共享样式指南来设置 Web 组件的样式,您可以不使用 shadow DOM 而是创建自定义元素,或者您可以使用 polyfill 支持共享样式的 Web 组件库。 两种解决方案各有利弊,因此请选择最适合您的项目的解决方案。

如果您决定在深入研究 Web 组件之前稍等片刻,那么几年后我们应该有一些很好的解决方案来在它们之间共享样式。 因此,请继续检查它的进展情况。

要记住的事情

如果您今天决定使用自定义元素或 Web 组件,请记住几件事。

最重要的是,Web 组件规范仍在积极开发中,这意味着事情可以并且将会改变。 Web 组件在很大程度上仍然是最前沿的,因此请准备好在使用它进行开发时保持警惕。

如果您决定使用 shadow DOM,请知道它在 polyfill 浏览器中非常缓慢且性能不佳。 正是出于这个原因,Polymer 的开发人员创建了他们的 shady DOM 实现并将其设为默认值。

Chrome、Opera 和最近的 Safari 是唯一支持 shadow DOM 版本 0 的浏览器。Firefox 仍在开发中,尽管它从版本 29 开始在实验中支持它。微软仍在考虑将它用于 Edge 并将其作为其路线图上的高度优先事项。

但是,shadow DOM 版本 0 是旧规范。 Shadow DOM 版本 1 是新版本,只有 Chrome、Safari 和 Opera 完全支持它。 更不用说自定义元素版本 0 经历了相同的升级,只有 Chrome 完全支持自定义元素版本 1,而 Safari 技术预览版从版本 17 开始支持它。自定义元素版本 1 在 Web 组件的编写方式上有一些重大变化,所以一定要完全理解这意味着什么。

最后,webcomponentjs polyfill 仅支持 shadow DOM 和自定义元素的版本 0 实现。 polyfill 的版本 1 分支将支持版本 1,但尚未发布。