CSS `Shadow Parts` (`::part()`) `Global Styling` 与组件内部样式暴露

admin
Tue, 22 Jul 2025 11:21:32 +0000
CSS, css, parts, shadow

大家好，欢迎来到今天的“CSS Shadow Parts：组件界的自由女神”特别讲座！ 今天我们要聊的是CSS Shadow Parts，这玩意儿啊，就像组件界的自由女神，让你的组件在保持独立自主的同时，还能有限度地接受外部世界的“关照”。听起来有点绕？别担心，咱们一步一步来。 什么是Shadow DOM？我们先打个底 在深入Shadow Parts之前，我们先来回顾一下Shadow DOM。简单来说，Shadow DOM就是给你的HTML元素穿上一层“隐身衣”，让它里面的内容和外部世界隔离开来。这就像你在家里建了一个秘密花园，花园里的花花草草，邻居看不见，也影响不到你的整体装修风格。 为什么要这么做？ 样式隔离： 避免全局CSS样式污染你的组件内部样式，让组件更加健壮。 结构隐藏： 隐藏组件内部复杂的HTML结构，只暴露必要的接口给外部使用。 组件复用： 因为隔离性，你的组件可以放心地在任何地方复用，不用担心样式冲突。 Shadow DOM 的代码示例： <my-element> #shadow-root p { color: blue; } …

继续阅读“CSS `Shadow Parts` (`::part()`) `Global Styling` 与组件内部样式暴露”

The post CSS `Shadow Parts` (`::part()`) `Global Styling` 与组件内部样式暴露 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

大家好，欢迎来到今天的“CSS Shadow Parts：组件界的自由女神”特别讲座！

今天我们要聊的是CSS Shadow Parts，这玩意儿啊，就像组件界的自由女神，让你的组件在保持独立自主的同时，还能有限度地接受外部世界的“关照”。听起来有点绕？别担心，咱们一步一步来。

什么是Shadow DOM？我们先打个底

在深入Shadow Parts之前，我们先来回顾一下Shadow DOM。简单来说，Shadow DOM就是给你的HTML元素穿上一层“隐身衣”，让它里面的内容和外部世界隔离开来。这就像你在家里建了一个秘密花园，花园里的花花草草，邻居看不见，也影响不到你的整体装修风格。

为什么要这么做？

样式隔离： 避免全局CSS样式污染你的组件内部样式，让组件更加健壮。

结构隐藏： 隐藏组件内部复杂的HTML结构，只暴露必要的接口给外部使用。

组件复用： 因为隔离性，你的组件可以放心地在任何地方复用，不用担心样式冲突。

Shadow DOM 的代码示例：

<my-element>
  #shadow-root

      p {
        color: blue;
      }

这是 Shadow DOM 里面的文字，是蓝色的。
</my-element>


  class MyElement extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
      this.shadowRoot.innerHTML = `

          p {
            color: blue;
          }

这是 Shadow DOM 里面的文字，是蓝色的。
      `;
    }
  }
  customElements.define('my-element', MyElement);



  p {
    color: red;
  }

这是 Shadow DOM 外面的文字，是红色的。

在这个例子中，my-element组件创建了一个Shadow DOM。Shadow DOM里面的 标签是蓝色的，而Shadow DOM外面的 标签是红色的。这就展示了Shadow DOM的样式隔离效果。

Shadow Parts：打破隔离墙的一扇窗

Shadow DOM虽然好，但有时候我们还是希望能够对组件内部的某些部分进行样式定制。毕竟，完全的隔离也会带来一些不便。这时候，Shadow Parts就闪亮登场了！

Shadow Parts允许组件的作者指定组件内部的某些元素为“可样式化部分”，然后外部就可以通过::part()选择器来修改这些部分的样式。这就像在你的秘密花园里开了一扇窗，允许邻居欣赏（并稍微修剪一下）花园里的特定花朵。

Shadow Parts 的代码示例：

<custom-button>Click me</custom-button>


  class CustomButton extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
      this.shadowRoot.innerHTML = `

          button {
            background-color: lightblue;
            border: none;
            padding: 10px 20px;
            cursor: pointer;
          }


          <slot></slot>

      `;
    }
  }
  customElements.define('custom-button', CustomButton);



  custom-button::part(button) {
    background-color: orange;
    color: white;
    border-radius: 5px;
  }


在这个例子中，我们在组件内部的 元素上添加了part="button"属性。这意味着这个按钮成为了一个“可样式化部分”。然后，在外部CSS中，我们使用custom-button::part(button)选择器来修改这个按钮的背景颜色、文字颜色和边框圆角。

::part()选择器的语法

::part()选择器的语法非常简单：

element::part(part-name) {
  /* 样式规则 */
}

element：你要选择的组件的标签名，例如custom-button。

::part(part-name)：用于选择组件内部指定part属性的元素。

part-name：你在组件内部定义的part属性的值，例如button。

Global Styling：全局样式的影响

虽然Shadow DOM可以隔离样式，但有一些全局样式还是会影响到Shadow DOM内部的元素。

继承属性： 像color、font、line-height等继承属性会穿透Shadow DOM，影响到组件内部的元素。

all属性： all: initial或all: unset可以重置元素的全部样式，包括继承属性。

Global Styling 与 ::part() 的交互

当全局样式和::part()样式同时作用于同一个元素时，它们的优先级是怎样的呢？

1. ::part()样式优先级高于普通全局样式。

2. !important可以提升全局样式的优先级，使其高于::part()样式。

代码示例：

<custom-button>Click me</custom-button>


  class CustomButton extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: 'open' });
      this.shadowRoot.innerHTML = `

          button {
            background-color: lightblue;
            border: none;
            padding: 10px 20px;
            cursor: pointer;
            color: black; /* Shadow DOM 内部样式 */
          }


          <slot></slot>

      `;
    }
  }
  customElements.define('custom-button', CustomButton);



  /* 全局样式 */
  custom-button::part(button) {
    background-color: orange; /* ::part() 样式 */
  }

  custom-button::part(button) {
    color: white !important; /* ::part() 样式，但使用 !important */
  }

  button {
    color: red; /* 全局样式，会被Shadow DOM内部样式覆盖 */
  }
  custom-button {
    color: green; /* 全局样式，会继承到Shadow DOM 内部，除非内部有明确定义 */
  }


在这个例子中：

按钮的背景颜色会是橙色，因为::part()样式覆盖了Shadow DOM内部的样式。

按钮的文字颜色会是白色，因为::part()样式使用了!important，优先级最高。

Shadow DOM外部的button选择器设置的文字颜色红色无效，因为Shadow DOM内部的样式隔离了外部样式。

custom-button设置的文字颜色绿色会继承到Shadow DOM内部，除非内部有明确定义。

组件内部样式暴露：权衡的艺术

使用Shadow Parts暴露组件内部样式，是一门权衡的艺术。你需要考虑以下几个方面：

可定制性： 你希望外部能够定制组件的哪些部分？

维护性： 暴露的样式越多，组件的内部结构就越容易受到外部的影响，维护成本也会增加。

一致性： 暴露的样式太少，可能会限制组件的灵活性，影响用户体验。

最佳实践建议：

1. 只暴露必要的样式： 尽量只暴露那些需要定制的、与组件外观相关的部分。

2. 使用语义化的part名称： 尽量使用语义化的part名称，例如header、content、footer，而不是part1、part2、part3。

3. 提供默认样式： 为每个part提供合理的默认样式，即使外部没有定制，组件也能正常显示。

4. 文档化你的part： 清楚地文档化你的组件暴露了哪些part，以及每个part的作用和预期用途。

::part() 的适用场景

主题定制： 允许用户自定义组件的颜色、字体、边框等。

布局调整： 允许用户调整组件内部元素的布局，例如调整header和footer的位置。

状态样式： 允许用户自定义组件在不同状态下的样式，例如hover、active、disabled。

::part() 的局限性

只适用于 Shadow DOM： ::part() 只能用于选择 Shadow DOM 内部的元素，不能选择 Light DOM 中的元素。

只能选择直接子元素： ::part() 只能选择 Shadow DOM 的直接子元素，不能选择更深层次的元素。

兼容性： 虽然现代浏览器对 ::part() 的支持已经比较好，但仍然需要考虑一些旧浏览器的兼容性问题。

Shadow Parts vs CSS Variables：选择哪个？

CSS Variables（自定义属性）是另一种常用的组件样式定制方法。那么，Shadow Parts和CSS Variables有什么区别，应该选择哪个呢？

特性 | Shadow Parts | CSS Variables
选择器 | ::part(part-name) | var(--variable-name)
作用范围 | 只能选择 Shadow DOM 内部指定 part 属性的元素。 | 可以作用于任何元素，包括 Shadow DOM 内部和外部的元素。
定制粒度 | 可以定制整个元素，包括它的所有样式属性。 | 只能定制单个CSS属性的值。
灵活性 | 较低，只能定制预先定义的 part。 | 较高，可以动态地修改任何CSS属性的值。
适用场景 | 需要定制组件的整体外观和布局，例如主题定制。 | 需要定制组件的某些特定属性，例如颜色、字体大小。
维护性 | 较高，因为外部只能修改预先定义的 part，不会影响组件的内部结构。 | 较低，因为外部可以修改任何CSS属性的值，可能会破坏组件的内部结构。
优先级 | 优先级高于普通全局样式，但低于 !important。 | 优先级由CSS层叠规则决定。

简单来说：

如果你想让外部能够定制组件的整体外观和布局，那么Shadow Parts是更好的选择。

如果你只想让外部能够定制组件的某些特定属性，那么CSS Variables是更好的选择。

结论：灵活的组件构建之路

Shadow Parts是构建可定制、可维护的Web Components的强大工具。它可以让你在保持组件独立性的同时，允许外部对组件的某些部分进行样式定制。当然，在使用Shadow Parts的时候，需要权衡可定制性、维护性和一致性，选择最适合你的场景的方案。

记住，技术只是工具，关键在于如何使用它。希望今天的讲座能给你带来一些启发，让你在组件构建的道路上走得更远。

好啦，今天的讲座就到这里，谢谢大家！ 如果大家还有什么问题，欢迎随时提问。