草庐IT

javascript - shadow DOM 和 light DOM 应该放哪些部分?

coder 2025-02-11 原文

我正在学习网络组件。在设计自定义元素时,我必须决定将什么隐藏在影子 DOM 中。其余部分将暴露在轻型 DOM 中。

据我所知,这些 API 允许两种具有不同权衡的极端用例:

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

我目前倾向于将所有内容隐藏在影子 DOM 中,原因如下:

  • 我打算从 JS 实例化所有内容。我不会手动编写 HTML 页面。同时编写 HTML API 和 JS API 的代码工作量更大。
  • 隐藏所有内容的认知工作较少。我不需要在轻型 DOM 中可见哪些信息之间找到合适的平衡点。
  • 它更接近我熟悉的大多数 JS 框架。

我错过了什么吗?


编辑

谢谢,有人回答说这取决于部分回答我问题的用例。但对于我所处的情况,我仍然缺少答案:我宁愿不支持我的某些组件的插槽。

我将为频谱的每个极端添加一个示例:

  • Light-DOM-heavy 组件:组件用户必须将元素插入插槽

    <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:他们必须计算附件数量以切换回退;基本上,任何不是浏览器将元素从 light DOM 复制到匹配的 shadow DOM 槽中的输入转换。然后他们需要监听属性变化等等。组件用户还有更多工作,他们必须将正确的元素插入正确的位置,其中一些非常重要(电子邮件内容可能需要链接)。

    在第二个示例中,组件作者不需要实现对从带有槽的 HTML 进行实例化的支持。但是组件用户必须从 JS 实例化。 All 渲染由组件作者在.renderFromObject 方法中完成。一些额外的方法提供了钩子(Hook)来在需要时更新 View 。

    可以通过让组件提供插槽和 JS 助手来填充这些插槽来提倡中间立场。但如果该组件不被 HTML 作者使用,我看不出有什么意义,还有更多工作要做。

    因此,将所有带有影子 DOM 的东西都放在可行的位置还是应该 我提供插槽,因为不这样做不符合标准,并且我的代码会在一些期待它们的用户代理上中断(忽略旧的完全不了解自定义元素的 UA)?

  • 最佳答案

    @supersharp 搞定了。

    我在 Web 组件中看到的一件事是,人们倾向于让他们的组件做太多事情,而不是分解成更小的组件。

    让我们考虑一些原生元素:

    <form>没有影子 DOM,它唯一做的就是从它的子表单元素中读取值,以便能够执行 HTTP GET、POST 等。

    <video> 100% shadowDOM,它使用应用程序提供的子项的唯一目的是定义要播放的视频。用户无法为 <video> 的影子子级调整 任何 CSS标签。他们也不应该被允许。唯一的<video>标签允许隐藏或显示那些影子子项的能力。 <audio>标签做同样的事情。

    <h1><h6>没有阴影。所有这一切都是设置默认字体大小并显示子项。

    <img>标记使用影子子项来显示图像和替代文本。

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

    是的,您可以将整个应用程序封装到一个组件中,但浏览器并没有尝试使用 Native 组件来做到这一点。您可以使组件越特化,它们的可重用性就越高。

    避免将 任何 添加到您的 Web 组件中,而不是 vanilla JS,换句话说,不要将任何框架代码添加到您的组件中,除非您不想与不使用它的人共享它们框架。我写的组件是 100% Vanilla JS,没有 CSS 框架。它们在 Angular、React 和 vue 中使用,没有更改代码。

    但是为每个编写的组件选择了使用 shadowDOM。而且,如果您必须在本身不支持 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 - shadow DOM 和 light DOM 应该放哪些部分?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54300456/

    有关javascript - shadow DOM 和 light DOM 应该放哪些部分?的更多相关文章

    1. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

      为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

    2. java - 我的模型类或其他类中应该有逻辑吗 - 2

      我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

    3. ruby-on-rails - 带有 Zeus 的 RSpec 3.1,我应该在 spec_helper 中要求 'rspec/rails' 吗? - 2

      使用rspec-rails3.0+,测试设置分为spec_helper和rails_helper我注意到生成的spec_helper不需要'rspec/rails'。这会导致zeus崩溃:spec_helper.rb:5:in`':undefinedmethod`configure'forRSpec:Module(NoMethodError)对thisissue最常见的回应是需要'rspec/rails'。但这是否会破坏仅使用spec_helper拆分rails规范和PORO规范的全部目的?或者这无关紧要,因为Zeus无论如何都会预加载Rails?我应该在我的spec_helper中做

    4. ruby-on-rails - 您希望看到哪些 Rails 插件? - 2

      您认为可以作为插件很好地存在于您的Rails应用程序中必须实现的哪些行为?您过去曾搜索过哪些插件功能但找不到?哪些现有的Rails插件可以改进或扩展,如何改进或扩展? 最佳答案 我希望在管理界面中看到一个引擎插件,它提供了应用程序中所有模型的仪表板摘要,以及可配置的事件图表。 关于ruby-on-rails-您希望看到哪些Rails插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questio

    5. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

      我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

    6. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

      我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

    7. ruby - 实现k最近邻需要哪些数据? - 2

      我目前有一个reddit克隆类型的网站。我正在尝试根据我的用户之前喜欢的帖子推荐帖子。看起来K最近邻或k均值是执行此操作的最佳方法。我似乎无法理解如何实际实现它。我看过一些数学公式(例如k表示维基百科页面),但它们对我来说并没有真正意义。有人可以推荐一些伪代码,或者可以查看的地方,以便我更好地了解如何执行此操作吗? 最佳答案 K最近邻(又名KNN)是一种分类算法。基本上,您采用包含N个项目的训练组并对它们进行分类。如何对它们进行分类完全取决于您的数据,以及您认为该数据的重要分类特征是什么。在您的示例中,这可能是帖子类别、谁发布了该项

    8. ruby-on-rails - 我现在(2010 年 1 月)应该使用哪个版本的 Ruby? - 2

      我有1.8.6附带的VanillaMacOSXLeopard。我是RoR的新手,所以会学习网上的教程。在使用更高版本的Ruby时,我是否可能会发现遵循它们的问题?我目前正在查看提到1.8.6和1.8.7的这个-http://www.railstutorial.org/book 最佳答案 RoR教程对两者都适用,但如果您正在学习Ruby,则应该学习1.9。Rails3将不支持1.8.6,所以我会选择1.8.7或1.9。我还推荐使用RVM在Ruby版本之间切换。 关于ruby-on-rail

    9. ruby-on-rails - Rails 中的类实例变量应该在互斥体中设置吗? - 2

      假设我的Rails项目中有一个设置实例变量的Ruby类。classSomethingdefself.objects@objects||=begin#somelogicthatbuildsanarray,whichisultimatelystoredin@objectsendendend是否可以多次设置@objects?是否有可能在一个请求期间,在上面的begin/end之间执行代码时,可以在第二个请求期间调用此方法?我想这实际上归结为Rails服务器实例如何fork的问题。我应该改用Mutex还是线程同步?例如:classSomethingdefself.objectsreturn@o

    10. ruby - 在 Mechanize 中使用 JavaScript 单击链接 - 2

      我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan

    随机推荐