【问题标题】:Pass global styles down to Svelte "custom element" components将全局样式传递给 Svelte“自定义元素”组件
【发布时间】:2021-07-22 09:20:22
【问题描述】:

我正在“尝试”将 Sveltejs 用作基于 Django 构建的项目的前端 Web 框架(相关,因为它定义了我的应用程序的结构)。我正在尝试将 Svelte 组件包含到我为 Django 应用程序构建的各种模板中。现在,使用customElement API,我能够直接在我的 HTML 模板中编译和使用我的 Svelte 组件作为自定义元素,这很好,除了一个主要问题:我的全局样式没有传播到组件。正如我发现的那样,这是因为所有自定义元素实际上都编译为 Web 组件,这意味着它们会包装其内部 HTML 以防止样式影响它们。这不是我想要的行为。然而,它似乎是唯一真正使用我的 Svelte 组件的方式,这种方式可以与我的 HTML 模板紧密结合。我该如何解决这个问题?

我使用webpack 作为我的模块捆绑器。如果有帮助,我可以评论指向我的webpack.config.js 的要点链接,但正如您可以想象的那样,我正在为多个 Django“应用程序”进行编译,所以我的配置有点混乱。作为参考,我已经得到了一切,包括使用 Sass 的本地样式和自定义元素按预期工作。这只是一个“如何做某事”的问题,尽管我花了几个小时在谷歌上搜索,但我一直无法找到明确的答案。

我知道你可以直接使用客户端API来访问组件,但这不仅繁琐和混乱(特别是如果我需要在不同的HTML模板上组合组件),而且我似乎无法获得webpack将我的 Svelte 组件公开为 Javascript 类。以下是我采用这种方法的方式(这是行不通的):

<!-- in the head -->
<script src="bundle.js"></script>

<!-- in the body -->
<script>new bundle.App{target: ...}</script>

它告诉我App 没有定义。这是我的webpack.config.js 中的个人output 配置的样子:

output: {
    library: 'bundle',
    // I do not understand at all what these two lines do -- I just found 
    // them somewhere on the interwebs as a suggestion to solve this problem
    libraryTarget: 'umd', 
    umdNamedDefine: true,
    // path, filename, etc.
}

总结一下,我真的有三个相互交织的问题:

  1. 我可以使用customElement API(我更喜欢它)并仍然应用全局样式吗?
  2. 如果我不能使用customElement API,有没有更好的方法来解决这个问题,允许全局样式?
  3. 如果没有其他选择,如何正确使用带有webpack的客户端API?

【问题讨论】:

  • 我不知道 svelte,但请注意全局 css 不会影响 web 组件(css 变量除外)。您需要加载 css 并将其作为样式节点插入到我应该认为的组件的模板或阴影根中。
  • 我知道。我在问是否有一种方法可以使全局 css 传播到 Web 组件——理想情况下不会弄乱影子根。

标签: javascript css webpack svelte-3 svelte-component


【解决方案1】:

TL;DR:对此没有明确/完美的答案。

这一次,没有办法将全局样式注入 Shadow Dom。话虽如此,您可以尝试的事情很少。

首先,如果您不使用slots,那么您可以编写自己的自定义元素注册函数并使用它来注册元素。您必须为从 HTMLElement 类扩展的 Web 组件编写自己的适配器。在这种方法中,每个 Svelte 组件都是独立的应用程序,您只需从 Web 组件初始化。这是您可以探索的最佳选择

此外,您还可以使用Constructable Stylesheets。它允许您以编程方式构造样式表对象并将其附加到 Shadow DOM。当然,这仅在您有扁平组件时才有效。当您的 Web 组件相互嵌套时,每个组件都会有自己的 Shadow DOM。您必须创建一个通用的全局样式作为可构造样式表并附加到每个组件。看here for the example

const sheet = new CSSStyleSheet();

// Replace all styles synchronously for this style sheet
sheet.replaceSync('p { color: green; }');

class FancyComponent1 extends HTMLElement {

  constructor() {
    super();

    const shadowRoot = this.attachShadow({ mode: 'open' });

    // Attaching the style sheet to the Shadow DOM of this component
    shadowRoot.adoptedStyleSheets = [sheet];

    shadowRoot.innerHTML = `
      <div>
        <p>Hello World</p>
      </div>
    `;

  }
}

class FancyComponent2 extends HTMLElement {

  constructor() {
    super();

    const shadowRoot = this.attachShadow({ mode: 'open' });

    // Same style sheet can also be used by another web component
    shadowRoot.adoptedStyleSheets = [sheet];

    // You can even manipulate the style sheet with plain JS manipulations
    setTimeout(() => shadowRoot.adoptedStyleSheets = [], 2000);

    shadowRoot.innerHTML = `
      <div>
        <p>Hello World</p>
      </div>
    `;

  }
}

在上面的例子中,sheet 是一个常见的样式表,它被用于两个单独的 Web 组件中。但同样,您必须编写自己的 Web 组件包装器来实现这一点。

【讨论】:

    【解决方案2】:

    在自定义元素中使用 @import 指令是否有任何性能(或其他)缺点?

    这就是我将全局样式传递给纤细的自定义元素的方式:

    /assets/theme.css
    ---
    
    :root,:host{
      --some-var:1rem;
    }
    

    然后在组件内部:

    CustomElement.svelte
    ---
    
    <svelte:options tag="custom-element"/>
    
    ...
    
    <style>
    
    @import "/assets/theme.css";
    
    :host{
      padding:var(--some-var)
    }
    
    </style>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-13
      • 2020-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-26
      • 1970-01-01
      相关资源
      最近更新 更多