Javascript 将全局样式应用于阴影DOM的正确方法

Javascript 将全局样式应用于阴影DOM的正确方法,javascript,css,web-component,shadow-dom,Javascript,Css,Web Component,Shadow Dom,这个问题与StackOverflow上的其他一些问题类似,但我找不到任何适用于我的情况和非弃用方法的答案(我开始思考,对于这种情况可能没有任何好的解决方案) 假设我们有一些main.css文件,其中包括按钮、列表、链接等的常见样式。因此,它只是一些标准的.css文件,其中包含我们希望在整个应用程序中重用的常见样式。我们希望将相同的样式应用于具有阴影DOM的Web组件 我知道有几种方法可以实现这一目标: 使用一种不推荐的方法:::shadow、>>>、/deep/selector。但是这些选择器现

这个问题与StackOverflow上的其他一些问题类似,但我找不到任何适用于我的情况和非弃用方法的答案(我开始思考,对于这种情况可能没有任何好的解决方案)

假设我们有一些main.css文件,其中包括按钮、列表、链接等的常见样式。因此,它只是一些标准的.css文件,其中包含我们希望在整个应用程序中重用的常见样式。我们希望将相同的样式应用于具有阴影DOM的Web组件

我知道有几种方法可以实现这一目标:

  • 使用一种不推荐的方法:::shadow、>>>、/deep/selector。但是这些选择器现在已经被弃用了,所以我想这不是一个好的方法
  • 使用css变量。如果我们需要设置一些属性,这种方法对于定制目的是很好的。但是如果我们想从main.css文件中迁移10-20种常见样式,那么这就太复杂了
  • 在影子DOM中使用@import语句或“link”标记。它可以工作,但会复制每个组件的所有样式。如果我们有10个web组件,我们将得到10个完全相同样式的副本。这听起来还不够好。特别是如果我们有很多共同的风格,听起来从性能的角度来看,这可能是一个糟糕的解决方案
  • 根本不使用阴影DOM,而是使用全局样式:),但这并不是当前问题的解决方案
  • 我还检查了同一问题在Angular框架中是如何解决的(我检查了Angular的版本5)。当我将封装行为设置为Native时,它实际上只是复制样式(如上面描述的#3),我认为这不是最好的方式(但可能是目前存在的最好方式)


    那么,有人知道有没有其他方法可以解决这个问题而不存在上述缺点吗?听起来阴影DOM目前的缺点带来的问题比它试图解决的问题还要多

    解决方案3没有真正的缺点:

  • 无论将CSS样式应用于主文档中的n个元素,还是应用于n个Shadow DOM中的1个元素,该样式都将复制到整个n个元素

  • 如果在n个Shadow DOM中导入文档n次,il实际上只加载一次,并通过浏览器缓存重用

  • 在此之后,il将依赖于ShadowDOM和CSS样式的浏览器实现,您应该只看到数千个ShadowDOM的性能下降


    Chrome 73+和Opera 60+2019年更新

    现在,您可以直接实例化
    CSSStyleSheet
    对象,并将其分配给不同的阴影DOM

    这样HTML就不会被复制

    var css = new CSSStyleSheet()
    css.replaceSync( "@import url( main.css )" )
    host.shadowRoot.adoptedStyleSheets = [css] 
    host2.shadowRoot.adoptedStyleSheets = [css] 
    
    您还可以将其应用于全局文档:

    document.adpotedStyleSheets = [css]
    
    另一个优点是样式表上的更新将应用于所有采用它的影子DOM(和文档)

     css.replaceSync( '.color { color: red }' )
    

    我设法用javascript模块实现了这一点,但我怀疑这是最干净的解决方案。 您可以创建一个GlobalStyles.js文件,该文件将包含各种组件中常见的css样式。将编辑器上的语言模式更改为“html”将为css提供语法突出显示

    const GlobalStyles = {
        main: `
            <style>
                body {
                    overflow: hidden;
                    margin: 0;
                    font-family: 'Poppins';
                }
                h3 {
                    font-size: 39px;
    
                }
            </style>
        `,
    
        button: `
            <style>
                button {
                    display: block;
                    cursor: pointer;
                    outline: none;
                    font-family: 'Poppins Medium';
                    line-height: 17px;
                    padding: 9px 13px;
                    font-size: 15px;
                    background-color: #9f28d8;
                    color: white;
                    border: 2px solid;
                    border-radius: 5px;
                    border-color: #9f28d8;
                    width: max-content;
                }
            </style>
        `
    }
    
    export default GlobalStyles;
    
    const GlobalStyles={
    主要内容:`
    身体{
    溢出:隐藏;
    保证金:0;
    字体系列:“Poppins”;
    }
    h3{
    字体大小:39px;
    }
    `,
    按钮:`
    钮扣{
    显示:块;
    光标:指针;
    大纲:无;
    字体系列:“Poppins中等”;
    线高:17px;
    填充:9px 13px;
    字体大小:15px;
    背景色:#9f28d8;
    颜色:白色;
    边框:2倍实心;
    边界半径:5px;
    边框颜色:#9f28d8;
    宽度:最大含量;
    }
    `
    }
    导出默认全局样式;
    
    之后,您可以将其导入另一个js文件,该文件包含自定义元素的影子dom的代码

    import GlobalStyles from './GlobalStyles.js';
    
    const template = document.createElement('template');
    template.innerHTML = `
    
       ${GlobalStyles.button}
    
       <style>
           ul {
               font-family: Helvetica, Arial, sans-serif;
               font-size: 13px;
               width: 20em;
               list-style-type: none;
            }
       </style>
    
    
    
       <ul></ul>
    
       <button>Click me</button>
    `;
    
    export class CustomList extends HTMLElement {
        constructor() {
            super();
            this.attachShadow({ mode: 'open' });
            this.shadowRoot.appendChild(document.importNode(template.content, true));
        }
    }
    
    从“/GlobalStyles.js”导入GlobalStyles;
    const template=document.createElement('template');
    template.innerHTML=`
    ${GlobalStyles.button}
    保险商实验室{
    字体系列:Helvetica、Arial、无衬线字体;
    字体大小:13px;
    宽度:20em;
    列表样式类型:无;
    }
    
      点击我 `; 导出类CustomList扩展HtmleElement{ 构造函数(){ 超级(); this.attachShadow({mode:'open'}); this.shadowRoot.appendChild(document.importNode(template.content,true)); } }

      这种方法的缺点是,仅当您纯粹使用js文件时,它才起作用。

      FWIW.。似乎@import for replaceSync现在被忽略了