我正在 Svelte 和 Tailwind 中构建自定义组件(Web 组件)。
我的目标是让这个组件具有独立的样式,但是我在将 Tailwind 样式表包含在我的自定义组件中时遇到了麻烦,因为它在 Shadow DOM 中被隔离。
MyComponent.svelte
<svelte:options
customElement={{
tag: "my-component",
}}
/>
<script>
import Child from "./Child.svelte";
</script>
<span class="text-blue-500 p-4">
Foo
<Child />
</span>
儿童苗条
<span class="p-4 text-red-500">Bar</span>
使用
<my-component></my-component>
时,它是无样式的,因为该组件位于 ShadowDOM 内部,因此无法访问全局 Tailwind 样式表。
有没有办法将我的组件的完整 Tailwind 样式表嵌入到 Shadow DOM 中?
我在使用 svelte 预处理器方面取得了一些成功
svelte.config.js
export default {
preprocess: [preprocess(), vitePreprocess()],
};
并在我的组件中包含指向我的顺风样式表的链接
<style lang="postcss" src="./../app.css"></style>
但这非常麻烦,因为我需要为 MyComponent.svelte 的每个子组件添加这一行
正如其他成员在评论中所说,对此没有明确的答案。 Shadow DOM 的全部目的是从外部环境中封装组件的内部部分。
adoptedStyleSheets
上提供的新 ShadowRoot
属性做一些事情。就像我们使用 JavaScript API 操作 HTML 树一样,我们也有 COM(CSS 对象模型)API,它允许您使用 JavaScript API 操作 CSS。
这些 API 最近添加的是
adoptedStyleSheets
和 constructed stylesheet
。这个想法很简单:
CSSStyleSheet
构造函数动态构造样式表。// Step 1: Construct a stylesheet:
const mySheet = new CSSStyleSheet();
// Step 2: Add the stylesheet rules:
// Specifically, add the Tailwind styles and rules
mySheet.replaceSync('h1 { padding: 2rem }');
// Apply the stylesheet to a document:
document.adoptedStyleSheets = [mySheet];
// Apply the stylesheet to a Shadow Root:
const node = document.createElement('my-component');
// Mode can be `closed` or `open`.
// Of course, doing this for `open` mode doesn't make sense.
const shadow = node.attachShadow({ mode: 'closed' });
shadow.adoptedStyleSheets = [mySheet];
为了将其附加到顶级
document
对象,您可以简单地执行以下操作:
// Combine existing sheets with our new one:
document.adoptedStyleSheets = [...document.adoptedStyleSheets, mySheet];
这应该可以解决您的问题,并使全局样式在您想要的所有影子根中可用。 但是现在,问题在哪里?
Tailwind 有两个关键事项需要注意。首先,它不是一个设计系统,您只需获得一个预编译的 CSS 文件即可在项目中引用。没有 CSS 文件。事实上,它更像是一个编译器,在项目编译期间生成 CSS 代码。
其次,它是一个实用性优先的样式框架(注意,样式不是 CSS。它只是碰巧使用与 CSS 相同的语法。)。因此,预计这将在您的 HTML 树、组件中全局可用(如果您使用框架)。现在,它完全违背了 ShadowDOM 的封装理念。
因此,为了使用 Tailwind,首先您必须从编译器/捆绑器(在您的情况下为 Vite.js 或 Svelte)单独生成它。这意味着您可能无法利用框架集成提供的 DX。
Tailwind 将生成一个独立的 CSS 文件。如果您使用像 Webpack 这样的捆绑器,那么这很容易。您可能会使用
svelte-loader
和 raw-loader
将 CSS 作为纯字符串导入,以便您可以创建构造的样式表:
// Ensure that raw-loader or equivalent is configured.
import tailWindCssAsString from '../tailwind.css';
const mySheet = new CSSStyleSheet();
mySheet.replaceSync(tailWindCssAsString);
// ... use the `mySheet` with `adoptedStyleSheets`
或者,如果您不这样做,那么您可能需要使用 CSS
@import
At 规则来指向 CSS,例如:
const mySheet = new CSSStyleSheet();
// Ensure to use correct URL path for loading CSS when app is deployed to the server.
mySheet.replaceSync(`
@import url("/tailwind.css");
`);
这肯定会增加初始延迟和额外的网络请求,这将导致 FOUC。
最后,如果不出意外,您还必须考虑何时何地应用构造的样式表。理想情况下,您希望确保样式应用于组件的所有实例,并且由于您使用的是 Web 组件,因此执行此操作的最佳位置是构造函数,如本文中所示。
由于我之前没有使用过Svelte,所以我不知道它是否提供了构造函数等效的生命周期方法。我想这可能是
onMount
方法,它可以工作,但不如构造函数那么好。
最后一点,您必须对
mode
为 closed
的每个组件执行此操作。