shadow dom简介

简介

在过去,不论是以 link 标签引入 CSS,或者在 style 标签下直接书写,所有的样式都会在全局生效,样式作用域该页面上的每个 HTML 元素。JS 也是如此,通过script 引入或者书写的代码都会共享同一个全局作用域。这就导致在这种整个运行环境都共享同一个状态空间的情况下,一个页面能放的东西有一个明确的上限,复杂度也会随着单个页面业务的增加而大幅上涨,只有通过模块化的方式才能降低页面的复杂度,使得一个页面所能承载更多的业务。因为浏览器的原始特性不支持模块化的构建方式,所以后来衍生出各种打包工具人为的将html页面划分成多个独立的模块,实现前端的模块化。

shadow dom 是为前端模块化所设计的一个工具,使得能够不通过打包工具就可以在原生 dom 层面实现 dom 元素之间的隔离。

shadow dom 的基本使用

shadow dom 需要通过 js 开启,如下代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="temp"></div>
  <script>
    const div = document.getElementById('temp')
    div.attachShadow({mode: 'open'});
    div.shadowRoot.innerHTML = `
      <style>
        p {
          color: red;
        }
      </style>
      <p>Shadow DOM</p>
    `
  </script>
</body>
</html>

我们首先写入了一个 div 标签,在 script 里面,我们首先通过 document.getElementById 方法获取到了 div 的 dom 对象。然后调用了 attachShadow,使得该 div 下的 dom 为 shadow dom,再向其中添加了 html 代码。

现在,div 内部的 dom 元素叫做 shadow dom。那么其他的 dom 叫做 light dom。这里我们将 shadow dom 和 light dom 分别翻译为影子dom 和 普通dom。

另外,可以将影子节点依附到的 html 元素有:

  • article
  • aside
  • blockquote
  • body
  • div
  • footer
  • h1
  • h2
  • h3
  • h4
  • h5
  • h6
  • header
  • main
  • nav
  • p
  • section
  • span

即这些 dom 元素可以调用 attachShadow。

shadow dom 与 webcomponent

shadow dom 应该与 webcomponent 一起使用。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .innerDiv {
      color: red;
    }
  </style>
</head>
<body>
  <fancy-tabs>
    <div>First</div>
    <div>Second</div>
    <div>Third</div>
  </fancy-tabs>
  <script>
    customElements.define('fancy-tabs', class extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({mode: 'open'});
        this.shadowRoot.innerHTML = `
          <style>
            :host {
              display: block;
              border: solid 1px #000;
            }
            ::slotted(*) {
              font-size: 30px;
            }
          </style>
          <div class="innerDiv">234</div>
          <slot></slot>
        `;
      }
    });
  </script>
</body>
</html>

这里我们定义了一个 <fancy-tabs> 组件,组件内部写了一些样式,还有一个 class 为 innerDiv 的 div 元素和slot。渲染结果如下:

在这里插入图片描述

一个值得注意的地方是,我们在外部 style 标签中定义了 innerDiv class 样式,文字的颜色为红色。但是在 <fancy-tabs> 内部对应的 div 元素并没有受到外部 class 的影响。如果不开启 attachShadow 则该 div 下的文字会变成红色。这说明 shadow dom 隔绝了外界的 css 样式。

mode 为 open 和 closed

attachShadow 函数可以传递一个配置对象,其中 mode 为必选项,还有两个 delegatesFocus 和 slotAssignment 为可选项。这里只说mode。

mode 限制的主要是外部 js 对 shadow dom 的访问,当 mode 为 open 时,外部 js 可以访问内部的 shadow dom 元素。mode 为 closed 时,外部 js 不可以访问 shadow dom,此时原来的 this.shadowRoot === null 这时上面的 <fancy-tabs> 组件需要通过如下方式书写才能正常运行:

customElements.define('fancy-tabs', class extends HTMLElement {
    constructor() {
        super();
        this._shadowRoot = this.attachShadow({mode: 'closed'})
        this._shadowRoot.innerHTML = `
            <style>
            :host {
            display: block;
            border: solid 1px #000;
            }
            ::slotted(*) {
            font-size: 30px;
            }
            </style>
            <div class="innerDiv">234</div>
            <slot></slot>
        `
    }
});

这里通过 attachShadow 的返回对象来实现对 innerHTML 的控制。

不管 open 还是 closed,都不会影响 component 内部的 js 访问外部的变量。

组件内部的 css 选择器

组件内部有一些特殊的 css 选择器可以使用:

  1. :host(selector) {} 可以作用到 shadow dom 的宿主身上,父级的 host 优先级更高。
  2. ::slotted(selector) {} 作用到插槽里面,web component 里的 slot 与 vue、react 里的 slot 几乎一样。
参考资料

文章封面

Shadow DOM concepts

Shadow DOM v1 - Self-Contained Web Components

影子 DOM(Shadow DOM)

Virtual DOM vs. Shadow DOM: What Every Developer Should Know

https://blog.openreplay.com/shadow-dom–the-ultimate-guide/

Shadow DOM: A Quick and Simple Introduction

Chapter 5. Working with the Shadow DOM

Shadow DOM Styling of Components

Understanding Shadow DOM in Web Components

Using Shadow DOM In Your Browser Tests

Web Components API: Shadow DOM and Light DOM

What We Do in the Shadow DOM

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值