微软提出CSS Modules V1 :通过import语句将CSS模块导入到组件中
Posted 前端之巅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微软提出CSS Modules V1 :通过import语句将CSS模块导入到组件中相关的知识,希望对你有一定的参考价值。
-
副作用,比如将<style>元素附加到文档中的操作就会有副作用。如果在文档的顶级作用域内完成此操作,那么它会破坏 shadow 根样式作用域。如果这个操作在 shadow 根内部完成,则组件的每个独立实例必须在其 shadow 根实例中包含它自己的<style>元素。 -
内联 CSS 文本作为 JavaScript 中的字符串。这种操作没有作充分的性能优化(它同时由 JS 和 CSS 解析器处理),并且会为开发人员带来糟糕的体验。 动态 fetch()ing CSS 通常不是静态可分析的,并且需要开发人员对复杂应用程序的依赖项做非常细致的管理工作。
CSS 模块扩展了 ES 模块基础结构,允许从 CSS 文件导入 CSSStyleSheet 对象,然后就可以通过 adoptStyleSheets 数组 将其添加到文档或 shadowRoot 中了。引入 CSS 模块后,上述问题也都能得到解决。
这个功能也是开发社区呼吁的——可以参阅这里的讨论: https://github.com/w3c/webcomponents/issues/759,其中有许多开发人员对其表示了兴趣。JS bundler 中的 CSS 加载器大受欢迎,也是这项功能潜在需求的佐证之一。
可以继续使用导入其他 ES 模块所用的import
语句导入 CSS 模块:
import styles from "styles.css";
document.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];
模块的默认导出是从 CSS 文件生成的 CSSStyleSheet。CSS 模块没有命名导出。
程序会检查 HTTP 响应头中的 MIME 类型以确定如何解析指定模块。MIME 类型的 text/css 将被视为 CSS 模块。导入的每个 CSS 模块都有自己的模块记录,符合 ES6 规范的定义;这些模块还会参与模块映射,并进入模块依赖关系图。
-
通过 constructor 创建一个 CSSStyleSheet()。 -
在新样式表上调用 CSSStyleSheet.replaceSync,并将文件内容作为参数(关于为什么这里用 replaceSync 而不是 replace,后文会具体说明)。此调用抛出的错误会导致模块创建失败并出现解析错误。 -
通过 CreateSyntheticModule 创建一个新的 Synthetic 模块,其中“default”作为 exportNames 的唯一条目,并使用 evaluateSteps 调用 SetMutableBinding(“default”, sheet),其中 sheet 是在步骤 1 中创建的 CSSStyleSheet。 使用在步骤 3 中创建的 Synthetic 模块创建新的 CSS 模块脚本作为其记录。
-
CSS 模块是 leaf module,且不允许 @import 引用(遵循可构造样式表中的 replaceSync 示例: https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync)。这就是 CSS 模块 V1 版本采用的实现方法,这也是为什么上文描述的 CSS 模块创建的第 2 步使用 replaceSync 而不是 replace;如果给定输入包含 @import 规则,则抛出 replaceSync。 -
CSS 模块是 leaf module;在为 CSS 模块创建模块记录之前,加载其样式表的完整 @import 树,如果无法解析,则将其视为模块的解析错误。 CSS 模块不是 leaf modul。将 CSS 模块的 @import 后的样式表作为模块图中请求的子模块处理,子模块带有自己的模块记录。它们将被实例化并作为不同的模块评估。
从长远来看,1 号选项会引入不必要的限制。
选项 2 和 3 之间的主要区别之一是,3 意味着如果 CSS 文件对于给定领域多次 @import,则每个导入都会共享单独的一份 CSSStyleSheet(因为对于给定的模块 specifier,一个模块仅会被实例化 / 评估一次)。如果开发人员错误地多次包含一个样式表或者由于共享的 CSS 依赖,就有可能带来内存 / 性能损失。另一方面这也是与现有行为背道而驰,现在同一.css 文件的多个 @import 各自带有自己的 CSSStyleSheet。
@justinfagnani 在这里指出: https://github.com/w3c/webcomponents/issues/759#issuecomment-490670571,在选项 3 中共享 @imported 样式表后,开发人员用工具编辑 CSS 或主题系统时,可以动态更改共享样式表,并在样式表的所有不同导入器上应用更改。
但正如 @tabatkins 在这里提到的那样: https://github.com/w3c/webcomponents/issues/759#issuecomment-490685490,选项 3 与当前的 @import 行为有很大的不同,它无法动态再现:CSS 对象模型不能用来使多个样式表依赖于单个子样式表。.parentStyleSheet 和.ownerRule 引用这里也存在问题,因为这些引用当前仅引用单个表,并且如果样式表具有多个导入器就会糊涂了。
这里的讨论更加深入一些: https://github.com/w3c/webcomponents/issues/759#issuecomment-490256626。鉴于目前大家尚未达成共识,我们现在的 V1 版本会使用选项 1 来回避问题。这是向前兼容的,因为在 CSS 模块中只要使用 @import 就会阻止模块加载。我们不准备等待选项 2 和 3 争出结果后才推进工作,因为早早发布版本后我们就可以获得早期开发人员对该功能的反馈,更好地了解它在实践中的使用方式,从而作出更加合适的决策。此外,CSS 模块的 V1 版本完成后,html 模块的开发工作也能正常推进了。
以下是关于定义自定义元素的示例,其中 CSS 是作为 JavaScript 字符串内联置入的:
<!doctype html>
<html>
<head>
<script type="module">
class HTML5Element extends HTMLElement {
constructor() {
super();
let shadowRoot = this.attachShadow({ mode: "open" });
let style = document.createElement("style");
style.innerText = `
.outerDiv {
border:0.1em solid blue;
display:inline-block;
padding: 0.4em;
}
.devText {
font-weight: bold;
font-size: 1.2em;
text-align: center;
margin-top: 0.3em;
}
.mainImage {
height:254px;
}
`;
let outerDiv = document.createElement("div");
outerDiv.className = "outerDiv";
let mainImage = document.createElement("img");
mainImage.className = "mainImage";
mainImage.src = "https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png";
let devText = document.createElement("div");
devText.className = "devText";
devText.innerText = "CSS Modules Are Great!";
this.shadowRoot.appendChild(outerDiv);
outerDiv.appendChild(mainImage);
outerDiv.appendChild(devText);
this.shadowRoot.appendChild(style);
}
}
window.customElements.define("my-html5-element", HTML5Element);
</script>
</head>
<body>
<my-html5-element></my-html5-element>
</body>
</html>
以下示例中,上面的自定义元素定义合并到一个 CSS 模块,以避免 CSS-as-a-JS-string(或插入<style>标记等):
<!doctype html>
<html>
<head>
<script type="module">
import styles from './html5Element.css';
class HTML5Element extends HTMLElement {
constructor() {
super();
let shadowRoot = this.attachShadow({ mode: "closed" });
this.shadowRoot.adoptedStyleSheets = [styles];
let outerDiv = document.createElement("div");
outerDiv.className = "outerDiv";
let mainImage = document.createElement("img");
mainImage.className = "mainImage";
mainImage.src = "https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png";
let devText = document.createElement("div");
devText.className = "devText";
devText.innerText = "CSS Modules Are Great!";
shadowRoot.appendChild(outerDiv);
outerDiv.appendChild(mainImage);
outerDiv.appendChild(devText);
}
}
window.customElements.define("my-html5-element", HTML5Element);
</script>
</head>
<body>
<my-html5-element></my-html5-element>
</body>
</html>
英文原文: https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/CSSModules/v1Explainer.md
以上是关于微软提出CSS Modules V1 :通过import语句将CSS模块导入到组件中的主要内容,如果未能解决你的问题,请参考以下文章
当您通过 NPM 安装 CSS 框架并排除 node_modules 文件夹时,如何在 VS Code 中使用 CSS 框架自动完成?