Web Components中引入外部CSS的3种方法

这篇文章发布于 2021年02月13日,星期六,23:09,归类于 CSS相关, JS实例。 阅读 16323 次, 今日 1 次 6 条评论

 

爱心纹理

为了重复利用,本文的Web Components组件就使用前两天写的“巧用两个type=range input实现区域范围选择”这篇文章中的Range组件示意。

原Range组件的CSS都是直接内联在Shadow DOM中的,如下图所示:

style样式内联

实际开发时候,往往会碰到需要引入外部CSS到Shadow DOM中的情况,此时该如何处理才合适呢?

本文介绍3种处理方法。

假设Range组件需要的CSS都放在了一个名为range.css的文件中。

一、@import方法

代码示意:

class uiRange extends HTMLElement {
    constructor () {
        super();
        // 附加Shadow DOM
        this.attachShadow({
            mode: 'open'
        });
        // 创建样式
        let node = document.createElement('style');
        node.innerHTML = `@import './range.css';`;
        this.shadowRoot.append(node);
    }
    ...
}
if (!customElements.get('ui-range')) {
    customElements.define('ui-range', uiRange);
}

引用的核心就是下面这样CSS语句:

@import './range.css'

实际DOM渲染效果如下截图(截自Firefox):

@import语句使用示意

优缺点

优点是兼容性不错,支持Shadow DOM的元素均支持此语法;以及使用比较方便。

缺点是性能著名的差。根据我看到的某些资料的测试数据,会有半秒左右的阻塞。

总的阻塞时间示意

不过有些站点对于小小的阻塞延时并不在意,反而更在意使用的便捷性,此时使用@import也是可以的,比方说一些demo原型页面,内部的文档页面等。

如果对性能比较在意,可以试试下面的fetch方法。

二、fetch获取

直接以请求的方式去load CSS文件的资源内容,并在Shadow DOM中输出。

代码示意:

class uiRange extends HTMLElement {
    constructor () {
        super();
        // 附加Shadow DOM
        this.attachShadow({
            mode: 'open'
        });
        // 获取样式
        fetch('./range.css').then(response => response.text()).then(data => {
            let node = document.createElement('style');
            node.innerHTML = data;
            this.shadowRoot.appendChild(node);
        });
    }
    ...
}
if (!customElements.get('ui-range')) {
    customElements.define('ui-range', uiRange);
}

由于fetch执行的时候是个异步的过程,因此并不会发生加载阻塞,理论性能会好一点。

优缺点

优点是兼容性不错,支持Shadow DOM的元素均支持此语法;以及性能还OK。

缺点是使用的时候比较啰嗦,也就是语句较多,以及感觉不是很正统。

如果从正统角度来说的话,下面的方法是最合适,也是理论上应该推崇的外部CSS引入方法。

三、作为CSS module import

此方法使用浏览器原生的import语法,但是import的是CSS文件而不是JS文件。

也就是把CSS文件直接作为模块引入。

相关代码演示如下:

import styles from './range.css';
class uiRange extends HTMLElement {
    constructor () {
        super();

        let shadow = this.attachShadow({
            mode: 'open'
        });
        shadow.adoptedStyleSheets = [styles];
    }
    ...
}
if (!customElements.get('ui-range')) {
    customElements.define('ui-range', uiRange);
}

简单的2步:

  1. import引入;
  2. adoptedStyleSheets采用;

这个方法是本文介绍重点,因此有demo,您可以狠狠地点击这里:Web Components中CSS样式import引用demo

说明:由于CSS文件直接import这个语法目前仅Chrome及其同样内核浏览器支持,因此Firefox和Safari下使用的是fetch方法。

关于adoptedStyleSheets

adoptedStyleSheets这个API方法是随着样式表构造(Constructed StyleSheets)一起出现的。

存在与shadowRoot和document两个对象上,用来设置样式。

例如:

document.adoptedStyleSheets;   // 默认返回 []

adoptedStyleSheets牵扯到的新的知识点很多,这里不具体展开,以后有机会更详细介绍。

优缺点

优点是使用方便快捷且是官方推荐方法,或者是import CSS模块就是为了这个场景才支持的;以及性能OK,import本身就是异步过程。

缺点就是兼容性不行,如下图(数据源自MDN文档),Firefox和Safari还没有支持。

adoptedStyleSheets兼容性

目前使用还不太成熟,不过现在浏览器支持新特性都是很快的。

说不定2年后就可以用起来了。

其实,配合一些打包工具,实际项目中也是可以使用的。

四、3个方法对比总结

最后,把本文介绍的3个方法再一起对比总结下,参见下表:

性能好 使用便捷 兼容性好
@import
fetch
import

可以发现,缺点每个方法各占一项。

其中,import方法未来最有潜力,因为,兼容性可以慢慢变好,而其他两个方法的缺点已经定型没救了。

大家可以根据实际使用场景选择合适的方法。

以上就是正文内容,感谢你的阅读,行为仓促,如有错误,欢迎指正。

如果觉得文章不错,也欢迎分享。

看到上面爱心图标突然想到明天是情人节,哎呀,突然脑壳疼~~

(本篇完)

分享到:


发表评论(目前6 条评论)

  1. 徐顺发Allan说道:

    其实还可以通过属性从外部覆盖

  2. mfk说道:

    你那个最后的“import引用”Demo无法执行。控制台报了”Failed to load module script: The server responded with a non-JavaScript MIME type of “text/css”. Strict MIME type checking is enforced for module scripts per HTML spec.”错误。

    需要服务器端设置MIME为application/javascript.

  3. 文章中的疑问说道:

    在 (二、fetch获取 )的讲解中, fetch的回调里有一句(shadow.appendChild(node))。 其中shadow是不是没有定义? 还是我理解有问题,shadow在上下文中本来就有?

  4. xxf说道:

    从工程上来看,可以直接把css文件当做文本来import,实际上就是作为文本进行插入了