【问题标题】:Using external CSS link ref in a web component在 Web 组件中使用外部 CSS 链接 href
【发布时间】:2025-12-08 04:25:01
【问题描述】:

我正在尝试在自定义 Web 组件中使用 CDN 中的 Bulma,但它似乎不起作用。

我的 html 有这个:

<!DOCTYPE html>
<html lang="en">
<head>
  <title>hello</title>
  <meta charset="utf-8">
</head>
<body>
    <my-element></my-element>
    <script src="index.js"></script>

</body>
</html>

这是我的 js 文件:

const sheet = new CSSStyleSheet()

sheet.replace('@import url("https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css")');

class MyElement extends HTMLElement {
    connectedCallback(){
        let that = this
        let id = Math.random()
        this.id = id

        const shadowRoot = this.attachShadow({ mode: 'open' })
        shadowRoot.adoptedStyleSheets = [sheet]

        let child = document.createElement('button')
        child.classList.add("button")
        child.innerText = id

        child.id = count

        shadowRoot.appendChild(child)

        this.addEventListener('click', e => {
            e.target
            console.log(that.id)
            that.remove()
        })
    }
}

if(!customElements.get('my-element')){
    customElements.define('my-element', MyElement)
}

let count = Math.floor(Math.random() * 10) + 1
for(i = 0; i <= count; i++){
    let el = document.createElement('my-element')
    document.body.appendChild(el)
}

值得注意的是,如果我使用sheet.replaceSync('button { color: green; }') 而不是sheet.replace(...),它可以正常工作。但是为什么外部 CSS 链接引用导入不起作用?

更新:我意识到我在控制台中收到以下警告:

index.js:6 @import rules are not allowed here. See https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418.

作为说明,我正在尝试使用这种方法,因此我可以以相同的方式设置多个自定义 Web 组件的样式,而无需多次导入样式表。

谢谢!

【问题讨论】:

  • 在 Devtools 的 Network 选项卡中是否有任何 Cors 错误?
  • @zergski - 没有 CORS 错误,但我刚刚意识到我收到了已添加到问题中的相关警告。
  • 我认为@import规则不能动态使用,您可以通过将&lt;link type="stylesheet" href="url"&gt;附加到您的文档来添加样式表
  • 顺便说一句,您应该在构造函数中创建影子 DOM,而不是在 connectedCallback 中,因为后者可以被多次调用(例如在 DOM 中添加或移动元素时)。
  • const shadowRoot 这个变量是不必要的。 shadowRoot 在this.shadowRoot 的实例上始终可用。

标签: javascript css web-component


【解决方案1】:

我的方法是为所有组件使用单个实例,如下所示:

import bulmaStyles from 'https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css';
const bulmaCSS = new CSSStyleSheet;
bulmaCSS.replaceSync(bulmaStyles);

export class Styles {
  static get bulma() { return bulmaCSS }
}

然后,在你的组件中,导入这个类

import { Styles } from 'path/to/Styles.js';

class MyElement extends HTMLElement {
    constructor() {
      super();
      // and use it in the constructor after creating the shadow DOM
      this.attachShadow({ mode: 'open' })
      this.shadowRoot.adoptedStylesheets = [ Styles.bulma ];
    }
}

为了能够将import 样式表的CSS 转换为变量,您需要构建堆栈来支持它。 Webpack 支持它,例如在webpack-raw-loader 的帮助下。

如果您不捆绑代码,则必须等待 CSS 模块,或从 DOM 中获取样式。

【讨论】:

  • 我收到此错误:无法加载模块脚本:服务器以“text/css”的非 JavaScript MIME 类型响应。根据 HTML 规范对模块脚本强制执行严格的 MIME 类型检查。
  • 我尝试通过在 Styles.js static get bulma(){...} 函数中返回 fetch 来解决此错误,然后将返回的 CSS 与 bulbmaCSS.replaceSync(bulmaStyles) 一起应用。我可以在组件构造函数中看到样式,但是我附加到自定义元素的子元素没有采用该样式。
  • 如前所述,在 CSS 模块进入规范和浏览器之前,您需要像 webpack 这样的构建堆栈以及 raw-loader 才能将 CSS 导入 Javascript 变量。
  • 只是为了仔细检查,我没有看到提到的浏览器;你知道adoptyedStyleSheets 在Firefox 和Safari 中还不能工作吗? caniuse.com/mdn-api_document_adoptedstylesheets
  • @Danny'365CSI'Engelman Firefox 现在支持在标志后面的可构造样式表。在about:config 中将layout.css.constructable-stylesheets.enabled 设置为true