【问题标题】:Web component stylsheet is re-downloaded everytime an instance of it is created每次创建 Web 组件样式表时都会重新下载它的实例
【发布时间】:2023-01-15 08:00:52
【问题描述】:

我正在制作一对自定义 Web 组件来显示通知。 wc-notifier 是父级,它在自身上创建wc-notifications,可能同时显示多个通知,在这种情况下,一个父级,多个子级。

每个 Web 组件都有指向两个外部样式表的链接:

  1. base.mins.css
  2. 是自己的shadow.mins.css

    正如您在下面看到的,我复制了链接,因为我正在尝试使用浏览器的 preload 功能:

    我知道我可以内联样式表,但我想链接样式,原因与我们通常链接样式表的原因相同。

    <template id="TEMPLATE_wc-notification">
    <link rel="stylesheet" type="text/css" href="/static/csslib/base.min.css">
    <link rel="preload" as="style" type="text/css" href="/static/csslib/base.min.css">
    <link rel="stylesheet" type="text/css" href="/static/wclib/wc-notification/shadow.min.css">
    <link rel="preload" as="style" type="text/css" href="/static/wclib/wc-notification/shadow.min.css">
    <div id="CONTAINER" class="d:f a-i:s o-y:a max-h:80vh"><div name="icon" class="o-align:c-m h:64"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="INFO"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="SUCCESS"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="WARNING"><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="ERROR"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg></div><slot id="SLOT" class="d:b f-s:24 f-w:500 o-w:a m-t:10"></slot><div id="CLOSE" class="o-align:c-m a-r:1/1 c:p bg-c:black|15a@h b-r-r:6 h:64"><svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 0 24 24" width="32px" fill="black" draggable="false" class="p:4"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></div></div>
    </template><script type="module" async src="/static/wclib/wc-notification/component.min.js"></script>
    
    
    <template id="TEMPLATE_wc-notifier">
    <link rel="stylesheet" type="text/css" href="/static/csslib/base.min.css">
    <link rel="preload" as="style" type="text/css" href="/static/csslib/base.min.css">
    <link rel="stylesheet" type="text/css" href="/static/wclib/wc-notifier/shadow.min.css">
    <link rel="preload" as="style" type="text/css" href="/static/wclib/wc-notifier/shadow.min.css">
    <div id="CONTAINER" class="p:f r:0 t:0 d:f f-d:c a-i:e z-i:1000" hidden><slot></slot></div>
    </template><script type="module" async src="/static/wclib/wc-notifier/component.min.js"></script>
    

    假设我执行了一次提取,但服务器没有响应。然后我希望显示提交失败的通知。现在问题来了,我想显示有关获取失败的通知。

    然而,当我尝试创建通知时,浏览器随后希望重新下载通知的样式表,但是现在“没有连接”,所以通知没有样式。

    这是响应标头(显示其未设置为 no-cache 等): 请注意,我在页面加载后大约 10 秒内测试了失败的提取:

    Cache-Control: max-age=6000
    Content-Disposition: inline; filename="component.min.js"
    Content-Length: 1105
    Content-Type: application/javascript
    Date: Wed, 21 Sep 2022 20:47:35 GMT
    Last-Modified: Wed, 21 Sep 2022 20:47:35 GMT
    Server: WSGIServer/0.2 CPython/3.9.13
    Set-Cookie: ... expires=Sat, 22 Oct 2022 20:47:35 GMT; HttpOnly; Max-Age=2678400; Path=/; SameSite=Lax
    Vary: Cookie
    

    为什么浏览器会尝试重新下载样式表?我如何让它在启动时下载一次,而不是每次创建 wc-notification 元素时都下载一次?上面的场景是最坏的情况,但即使是好的情况,组件样式设置也很慢,因为每次添加 Web 组件时,样式表都会重新下载。

    我也试过@import,但结果是同样的问题,每次都下载样式表。

    <template id="TEMPLATE_wc-notification"><style>@import "/static/csslib/base.min.css"</style>
    <style>@import "/static/wclib/wc-notification/shadow.min.css"</style>
    <div id="CONTAINER" class="d:f a-i:s o-y:a max-h:80vh"><div name="icon" class="o-align:c-m h:64"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="INFO"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="SUCCESS"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="WARNING"><path d="M0 0h24v24H0z" fill="none"/><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"/></svg><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000" draggable="false" class="icon-svg" id="ERROR"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg></div><slot id="SLOT" class="d:b f-s:24 f-w:500 o-w:a m-t:10"></slot><div id="CLOSE" class="o-align:c-m a-r:1/1 c:p bg-c:black|15a@h b-r-r:6 h:64"><svg xmlns="http://www.w3.org/2000/svg" height="32px" viewBox="0 0 24 24" width="32px" fill="black" draggable="false" class="p:4"><path d="M0 0h24v24H0z" fill="none"/><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg></div></div>
    </template><script type="module" async src="/static/wclib/wc-notification/component.min.js"></script>
    
    
    <template id="TEMPLATE_wc-notifier"><style>@import "/static/csslib/base.min.css"</style>
    <style>@import "/static/wclib/wc-notifier/shadow.min.css"</style>
    <div id="CONTAINER" class="p:f r:0 t:0 d:f f-d:c a-i:e z-i:1000" hidden><slot></slot></div>
    </template><script type="module" async src="/static/wclib/wc-notifier/component.min.js"></script>
    

    在尝试寻找解决方案时,我确实遇到了 Constructable Style Sheets,但 Safari 不支持 replacereplaceSync 方法,这是 Web 组件的核心。

【问题讨论】:

    标签: web-component native-web-component


    【解决方案1】:

    我假设:

    • 您将模板包含在网页的 HTML 中
    • 因为您的一个 css 文件名为“shadow.min.css”,所以您正在使用 Shadow DOM。
    • 您想要为您的 css 文件进行单个 http 调用,而不管网页中的自定义标记的数量。

    由于您正在构建 Web 组件,因此您应该遵守划分原则,这意味着:组件的用户只需在 HTML 中包含一个 [script] 标签,其他所有内容都由组件加载 - 并保留在组件中.

    当您从组件中将 [link] 标记注入 Shadow DOM 时,浏览器将缓存检索到的文件。通常,这就是您所要做的。

    对于模板,由于我们目前没有 HTML 导入,最简单的尽管网页中有很多自定义标签,但仅获取一次模板的方法是使用本机 Javascript 模块。现代浏览器似乎对原生 JS 模块有很好的支持。警告,您的模板文件必须有一个 .js 扩展名,否则它将不起作用。模板.js。如果这对您的特定情况不起作用,那么您必须以某种方式存储模板的内容 - 请参阅此问题的答案:Single HTTP request for HTML template file for multiple instances of the same Web Component

    因此,假设目录结构如下:

    Document-root
    - index.html
    -/static
    -- /components
    --- /wc-notification
    ---- component.js
    ---- template.js
    --/csslib
    --- base.min.css
    --- /wclib
    ---- /wc-notification
    ----- shadow.min.css
    

    索引.html

    <head>
        <script type="module" src="/static/components/wc-notification/component.js"></script>
    </head>
    <body>
        <wc-notification>Foo</wc-notification>
        <wc-notification>Bar</wc-notification>
        <wc-notification>Baz</wc-notification>
    </body>
    

    组件.js

    import { template } from "./template.js";
    export class WCNotificaton extends HTMLElement {
        #css_file1 = "/static/csslib/base.min.css";
        #css_file2 = "/static/wclib/wc-notification/shadow.min.css"
    
        constructor() {
            super();
            const style = document.createElement('style');
            const fragment = document.createRange().createContextualFragment(template);
            style.setAttribute("rel", "stylesheet");
            this.style1 = style.cloneNode();
            this.style1.setAttribute("href", this.css_file1);
            this.style2 = style.cloneNode();
            this.style2.setAttribute("href", this.css_file2);
            this.notification_template = fragment.querySelector('#TEMPLATE_wc-notification').content;
            this.notifier_template = fragment.querySelector('#TEMPLATE_wc-notifier').content;
            this.shadow = this.attachShadow({ mode: "open" })
        }
    
        connectedCallback() {
            this.doSomethingwithTheTemplates();
            this.render();
        }
    
        doSomethingwithTheTemplates() {
          this.notify = // the result of processing the templates, should be a DOM Node;
        }
    
        render() {
            this.shadow.append(this.style1);
            this.shadow.append(this.style2);
            this.shadow.append(this.notify);
    }
    
    document.addEventListener('DOMContentLoaded', customElements.define('wc-notification', WCNotificaton);
        
    

    template.js(我在这里使用模板文字因为它很简单)

    export const template = `
        <template id="TEMPLATE_wc-notification">
            <!-- Do not include <link> or @import, <style> is OK -->
        </template>
    
        <template id="TEMPLATE_wc-notifier">
            <!-- Do not include <link> or @import, <style> is OK -->
        </template>
    `;
    

    【讨论】:

      猜你喜欢
      • 2018-06-25
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2022-10-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多