浅谈shadow dom

什么是shadow dom?

顾名思义, shadow-dom,直译的话就是 影子/隐蔽处的dom ?我觉得可以理解为潜藏在背后的 DOM 结构,也就是我们无法直接控制操做的 DOM 结构。
浅谈shadow dom_第1张图片

它相当于一个作用域的概念,使其不会被外部所影响。可以把它理解成一颗单独的dom树。这样就不会有css的命名冲突或者样式的意外泄漏。
在chrome里,我们可以启用开发者工具的“Show user agent shadow DOM”选项,就能看到这些隐藏的结构。

举例:
video标签: 虽然我们创建的是一个空标签,但是在这个空标签内部,存在一个 shadow-dom ,点开 shadow-dom 可以看到里面的内容。其实这内部的具体内容,就是 的具体实现。
浅谈shadow dom_第2张图片

shadow DOM视为“DOM中的DOM”。它是独立的DOM树,具有自己的元素和样式,与原始DOM完全隔离。可以应用在组件中。

在一些微前端框架,例如qiankun的源码时,发现qiankun在实现css样式的隔离时,使用了shadow DOM

有什么特点?

可以使用和操作常规 DOM 一样的方式来操作 Shadow DOM,例如添加子节点、设置属性,以及为节点添加自己的样式
Shadow DOM 内部的元素始终不会影响到它外部的元素(除了 :focus-within),这为封装提供了便利。

有什么用?

1、良好的封装性,对于通用的组件来说,我们作为开发者,其实并不需要关心内部很多元素的实现或原理,比如我们在页面中使用 video 标签,其实对于暂停,播放,拖动滚动条等逻辑并不需要做过多的关心,那么我们希望代码越简洁越好,就像现在一样,只需要一个video标签即可,而不是还要写一堆元素来实现本身的播放暂停等按钮
2、现行的组件都是开放式的,即最终生成的 HTML DOM 结构难以与组件外部的 DOM 进行有效结构区分,样式容易互相混淆。Shadow-dom 的 封装隐藏性为我们提供了解决这些问题的方法。

为什么需要 shadow-dom?

Shadow-dom 是游离在 DOM 树之外的节点树,但是他的创建基于普通 DOM 元素(非 document),并且创建后的 Shadow-dom 节点可以从界面上直观的看到。更重要的是,Shadow-dom 具有良好的密封性。这是浏览器提供的一种“封装”功能,提供了一种强大的技术去隐藏一些实现细节
怎么创建shadow dom?
要创建一个shadow DOM。首先我们需要一个Shadow root,Shadow root 是 shadow 树中最顶层的节点, 是在创建 shadow DOM 时被附加到常规DOM节点的内容。具有与之关联的shadow root的节点称为shadow host。(如下图所示)
浅谈shadow dom_第3张图片浅谈shadow dom_第4张图片

像上图,shadowHost就是div.shadow-dom。这个外层的css会影响到他。(要注意:css样式的边界影响,就是不要给shadowhost或者他的父级定义可以遗传的样式,可以用all: initial还原)。

使用 attachShadow 创建shadowDom:
let shadow = element.attachShadow({mode: ‘open’});
let shadow = element.attachShadow({mode: ‘closed’});
open / closed 表示可否通过页面内的 JavaScript 方法来获取 Shadow DOM

创建shadow DOM需要注意以下几点:
1.只有封闭标签才能作为shadowHost。
2.当我们把一个标签设置成shadow DOM的时候,里面的子元素将全部失效。
3.当mode为closed时,禁止你使用host元素的shadowRoot属性从root外部访问shadow root的元素。
如何修改shadow dom的样式?
1、在shadow块下面创建style标签,在里面添加样式
2、mode为true时,通过shadowRoot获取到指定元素修改样式
试试?
示例中有两个div的className都是cn,一个在普通dom中,一个在shadowDom中。可以看到两个style标签下的cn样式互不影响

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .cn {
      color: red;
    }
    .shadow-dom {
      /* 可继承 */
      background-color: yellow;
      text-indent: 2em;
      /* 将所有css属性恢复到初始状态 */
      all: initial;
    }
  </style>
</head>
<body>
  <div class="wrapper">
    <div class="cn">dom</div>
    <div class="shadow-dom">test</div>
  </div>
</body>
<script>
  const shadowHost = document.querySelector('.shadow-dom');
  const shadowRoot = shadowHost.attachShadow({mode: 'open'});


  const div = document.createElement('div');
  div.innerHTML = "shdow-dom";
  div.className = 'cn'
  div.setAttribute('part', 'native')


  const style = document.createElement('style');
  style.textContent = `
    .cn {
      color: blue;
    }
  `;
  
  shadowRoot.appendChild(style)
  shadowRoot.appendChild(div)
  // shadowHost.shadowRoot.querySelector('.cn').style.color = 'green'

  console.log('shadowRoot', shadowHost.shadowRoot)
  
</script>
</html>

shadow-dom 的未来

现行的组件都是开放式的,即最终生成的 HTML DOM 结构难以与组件外部的 DOM 进行有效结构区分,样式容易互相混淆。Shadow-dom 的 封装隐藏性为我们提供了解决这些问题的方法。在 Web 组件化的规范中也可以看到 Shadow-dom 的身影,使用具有良好密封性的 Shadow-dom 开发下一代 Web 组件将会是一种趋势。

你可能感兴趣的:(前端,shadow,dom)