我正在学习Web组件。在设计自定义元素时,我必须决定在影子DOM中将隐藏什么。然后,其余部分将在轻型DOM中曝光。

据我了解,API允许两个极端的用例,但需要权衡取舍:

  • 在阴影DOM中几乎什么都没有隐藏,大多数元素的内容都在浅色DOM和元素的属性中:
  • 这允许HTML作者在不编写JS的情况下为组件提供任何显示的内容。
  • 这接近于可搜索性和可访问性
  • 的现状
  • ,但是所涉及的工作没有什么返回;我增加了组件的复杂性,但它们没有封装任何东西(所有东西都暴露在外)。
  • 几乎将所有内容都隐藏在影子DOM中,该元素的innerHTML为空:
  • 这需要从JS实例化元素;
  • 会更多地锁定用法,因为从JS实例化(在类型上)比使用HTML插槽和属性更严格。
  • 这可能不太容易搜索和访问(我不确定是否是这种情况);

  • 由于以下原因,我目前倾向于将所有内容都隐藏在影子DOM中:
  • 我打算实例化JS中的所有内容。我不会手动编写HTML页面。同时对HTML API和JS API进行编码将需要更多的工作。
  • 隐藏所有内容要少花些精力。我不需要在轻型DOM中可见的信息之间找到适当的平衡。
  • 它更接近我熟悉的大多数JS框架。

  • 我想念什么吗?

    编辑

    谢谢,我得到答复,这取决于部分回答了我的问题的用例。但是我仍然找不到关于我所处情况的答案:我宁愿不为我的某些组件提供插槽。

    我将为频谱的每个极端添加一个示例:
  • 轻型DOM繁重的组件:该组件用户必须将元素插入插槽
    <template id=light-email-view>
      <div>
        <div><slot name=from></slot></div>
        <ul><slot name=to></slot></ul>
        <h1><slot name=subject></slot></h1>
        <div><slot name=content></slot></div>
        <ul><slot name=attachements></slot></ul>
        <div class=zero-attachment-fallback>no attachments</div>
      </div>
    </template>
    

  • Shadow-DOM-heavy组件:该组件用户必须使用JS API
    <template id=shadow-email-view>
      <div></div>
    </template>
    <script>
    ...
    let view = document.createElement('shadow-email-view');
    // this method renders the email in the shadow DOM entirely
    view.renderFromOject(email);
    container.appendChild(view);
    </script>
    

    在第一个示例中,组件作者需要做更多的工作,因为他们需要“解析” DOM:他们必须计算附件数量以切换回退。基本上,输入的任何转换(不是浏览器将元素从浅DOM复制到匹配的阴影DOM插槽中)。然后,他们需要监听属性更改等信息。组件用户还需要做更多的工作,他们必须将正确的元素插入正确的插槽中,其中一些是不平凡的(电子邮件内容可能必须链接)。

    在第二个示例中,组件作者不需要实现对从带有槽的HTML实例化的支持。但是组件用户必须从JS实例化。 所有渲染由组件作者在.renderFromObject方法中完成。如果需要,一些其他方法提供了 Hook 来更新 View 。

    可以通过让组件同时提供插槽和JS帮助程序来主张中间立场。但是我不明白HTML编写者不打算使用该组件的意义,但还有很多工作要做。

    因此,将所有带有影子DOM的东西都设为可行或应该放在我提供的插槽,因为不这样做不符合标准,并且我的代码将破坏某些期望它们的用户代理(忽略那些根本不知道的旧UA)自定义元素)?
  • 最佳答案

    @supersharp已将其钉牢。

    我对Web组件看到的一件事是,人们倾向于使他们的组件做得太多,而不是分解成较小的组件。

    让我们考虑一些本地元素:
    <form>没有影子DOM,它唯一要做的就是从其子表单元素中读取值,以便能够进行HTTP GET,POST等操作。
    <video> 100%shadowDOM,它使用应用程序提供的子代的唯一作用是定义要播放的视频。用户不能为<video>标记的影子子项调整任何CSS。也不应允许他们这样做。 <video>标记唯一允许的是隐藏或显示那些影子 child 的能力。 <audio>标记执行相同的操作。
    <h1><h6>没有阴影。这一切都是设置默认字体大小并显示子级。
    <img>标记使用阴影子项来显示图像和Alt-Text。

    就像@supersharp所说的,shadowDOM的使用是基于元素的。我还要进一步说,shadowDOM应该是一个深思熟虑的选择。我要补充一点,您需要记住,这些应该是组件而不是应用程序。

    是的,您可以将整个应用程序封装到一个组件中,但是浏览器并未尝试使用Native组件来做到这一点。您可以使组件变得更加特化,从而使它们变得更可重用。

    避免在Web组件中添加非原始JS的任何内容,换句话说,不要在您的组件中添加任何框架代码,除非您永远不想与不使用该框架的人共享它们。我写的组件是100%Vanilla JS,没有CSS框架。它们可以在Angular,React和vue中使用,而无需更改代码。

    但是选择对每个编写的组件使用shadowDOM。并且,如果您必须在 native 不支持Web组件的浏览器中工作,则可能根本不想使用shadowDOM。

    最后一件事。如果编写的组件不使用shadowDOM但具有CSS,则必须小心放置CSS,因为您的组件可能会放置在其他人的shadowDOM中。如果将CSS放在<head>标记中,则它将在另一个shadowDOM中失败。我使用以下代码来防止该问题:

    function setCss(el, styleEl) {
      let comp = (styleEl instanceof DocumentFragment ? styleEl.querySelector('style') : styleEl).getAttribute('component');
      if (!comp) {
        throw new Error('Your `<style>` tag must set the attribute `component` to the component name. (Like: `<style component="my-element">`)');
      }
    
      let doc = document.head; // If shadow DOM isn't supported place the CSS in `<head>`
      // istanbul ignore else
      if (el.getRootNode) {
        doc = el.getRootNode();
        // istanbul ignore else
        if (doc === document) {
          doc = document.head;
        }
      }
    
      // istanbul ignore else
      if (!doc.querySelector(`style[component="${comp}"]`)) {
        doc.appendChild(styleEl.cloneNode(true));
      }
    }
    
    export default setCss;
    

    关于javascript - 阴影DOM和浅色DOM应该包含哪些部分?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54300456/

    10-16 10:51