# 视图封装

在 Angular 中,组件的样式可以封装在组件的宿主元素中,这样它们就不会影响应用程序的其余部分。

Component的装饰器提供了 encapsulation 选项,可用来控制如何基于每个组件应用视图封装。

从以下模式中选择:

模式 Modes 详情 Details
ViewEncapsulation.ShadowDom Angular 使用浏览器内置的 Shadow DOM API 将组件的视图包含在 ShadowRoot(用作组件的宿主元素)中,并以隔离的方式应用所提供的样式。 ViewEncapsulation.ShadowDom 仅适用于内置支持 shadow DOM 的浏览器(请参阅 Can I use - Shadow DOM v1)。并非所有浏览器都支持它,这就是为什么 ViewEncapsulation.Emulated 是推荐和默认模式的原因。
ViewEncapsulation.Emulated Angular 会修改组件的 CSS 选择器,使它们只应用于组件的视图,不影响应用程序中的其他元素(模拟 Shadow DOM 行为)。有关更多详细信息,请参阅查看生成的 CSS。
ViewEncapsulation.None Angular 不应用任何形式的视图封装,这意味着为组件指定的任何样式实际上都是全局应用的,并且可以影响应用程序中存在的任何 HTML 元素。这种模式本质上与将样式包含在 HTML 本身中是一样的。

# 查看生成的 CSS

使用模拟视图封装时,Angular 会预处理所有组件的样式,以便它们仅应用于组件的视图。

在正运行的 Angular 应用程序的 DOM 中,使用模拟视图封装模式的组件所在的元素附加了一些额外的属性:

<hero-details _nghost-pmm-5>
  <h2 _ngcontent-pmm-5>Mister Fantastic</h2>
  <hero-team _ngcontent-pmm-5 _nghost-pmm-6>
    <h3 _ngcontent-pmm-6>Team</h3>
  </hero-team>
</hero-details>

有两种这样的属性:

属性 Attributes 详情 Details
_nghost 被添加到包裹组件视图的元素中,这将是本机 Shadow DOM 封装中的 ShadowRoots。组件的宿主元素通常就是这种情况。
_ngcontent 被添加到组件视图中的子元素上,这些属性用于将元素与其各自模拟的 ShadowRoots(具有匹配 _nghost 属性的宿主元素)相匹配。

这些属性的确切值是 Angular 的私有实现细节。它们是自动生成的,你不应在应用程序代码中引用它们。

它们以生成的组件样式为目标,这些样式会被注入到 DOM 的 &lt;head&gt;部分:


[_nghost-pmm-5] {
  display: block;
  border: 1px solid black;
}
h3[_ngcontent-pmm-6] {
  background-color: white;
  border: 1px solid #777;
}

这些样式经过后期处理,以便每个 CSS 选择器都使用适当的 _nghost_ngcontent属性进行扩充。这些修改后的选择器可以确保样式以隔离和有针对性的方式应用于组件的视图。

# 混合封装模式

如前所述,你可以在组件的装饰器中针对每个组件指定封装模式,这意味着在你的应用程序中,不同的组件可以使用不同的封装策略。

尽管可能,但不建议这样做。如果真的需要,你应该知道使用不同封装模式的组件的样式会如何彼此交互:

模式 Modes 详情 Details
ViewEncapsulation.Emulated 组件的样式会添加到文档的 &lt;head&gt; 中,使它们在整个应用程序中可用,但它们的选择器只会影响它们各自组件模板中的元素。
ViewEncapsulation.None 组件的样式会添加到文档的 &lt;head&gt; 中,使它们在整个应用程序中可用,因此是完全全局的,会影响文档中的任何匹配元素。
ViewEncapsulation.ShadowDom 组件样式仅添加到 shadow DOM 宿主中,确保它们仅影响各自组件视图中的元素。

ViewEncapsulation.EmulatedViewEncapsulation.None组件的样式也会添加到每个 ViewEncapsulation.ShadowDom组件的 shadow DOM 宿主中。

这意味着带有 ViewEncapsulation.None的组件的样式将影响 shadow DOM 中的匹配元素。

这种方法乍一看似乎有违直觉,但如果没有它,带有 ViewEncapsulation.None的组件将在使用 ViewEncapsulation.ShadowDom的组件内渲染不同的效果,因为其样式将不可用。

# 例子

本节展示了具有不同 ViewEncapsulation的组件的样式如何交互的示例。

请参阅 现场演练 以自己尝试这些组件。

# 无封装

第一个示例显示了一个具有 ViewEncapsulation.None的组件。此组件将其模板元素着色为红色。

src/app/no-encapsulation.component.ts

@Component({
  selector: 'app-no-encapsulation',
  template: `
    <h2>None</h2>
    <div class="none-message">No encapsulation</div>
  `,
  styles: ['h2, .none-message { color: red; }'],
  encapsulation: ViewEncapsulation.None,
})
export class NoEncapsulationComponent { }

Angular 将此组件的样式作为全局样式添加到文档的 &lt;head&gt;中。

如前所述,Angular 还会将这些样式添加到所有 shadow DOM 宿主。因此,样式在整个应用程序中都可用。

component with no encapsulation

# 模拟封装

第二个示例显示了一个具有 ViewEncapsulation.Emulated的组件。此组件将其模板元素着色为绿色。

src/app/emulated-encapsulation.component.ts

@Component({
  selector: 'app-emulated-encapsulation',
  template: `
    <h2>Emulated</h2>
    <div class="emulated-message">Emulated encapsulation</div>
    <app-no-encapsulation></app-no-encapsulation>
  `,
  styles: ['h2, .emulated-message { color: green; }'],
  encapsulation: ViewEncapsulation.Emulated,
})
export class EmulatedEncapsulationComponent { }

ViewEncapsulation.None类似,Angular 会将此组件的样式添加到文档的 &lt;head&gt;中,但它们是带有“作用域”的样式。

只有直接在该组件模板中的元素才会匹配其样式。由于来自 EmulatedEncapsulationComponent的样式是特化的,因此它们会覆盖来自 NoEncapsulationComponent的全局样式。

在此示例中,EmulatedEncapsulationComponent包含着 NoEncapsulationComponent,但 NoEncapsulationComponent仍然如预期般生效了,因为 EmulatedEncapsulationComponent的“范围化”样式与其模板中的元素并不匹配。

component with no encapsulation

# Shadow DOM 封装

第三个示例显示了一个具有 ViewEncapsulation.ShadowDom的组件。此组件会将其模板元素着色为蓝色。

src/app/shadow-dom-encapsulation.component.ts

@Component({
  selector: 'app-shadow-dom-encapsulation',
  template: `
    <h2>ShadowDom</h2>
    <div class="shadow-message">Shadow DOM encapsulation</div>
    <app-emulated-encapsulation></app-emulated-encapsulation>
    <app-no-encapsulation></app-no-encapsulation>
  `,
  styles: ['h2, .shadow-message { color: blue; }'],
  encapsulation: ViewEncapsulation.ShadowDom,
})
export class ShadowDomEncapsulationComponent { }

Angular 仅将此组件的样式添加到 shadow DOM 宿主,因此它们在 shadow DOM 之外是不可见的。

:: tip 注意 Angular 还将 NoEncapsulationComponentEmulatedEncapsulationComponent的全局样式添加到了 shadow DOM 宿主中,因此这些样式仍然可用于该组件的模板中的元素。 ::

在这个例子中,ShadowDomEncapsulationComponent包含一个 NoEncapsulationComponentEmulatedEncapsulationComponent

ShadowDomEncapsulationComponent组件添加的样式在该组件的整个 shadow DOM 中都可用,在 NoEncapsulationComponentEmulatedEncapsulationComponent中也是如此。

EmulatedEncapsulationComponent具有特化的“范围化”样式,因此该组件模板的样式不受影响。

由于 ShadowDomEncapsulationComponent中的样式是在全局样式之后添加到 Shadow Host 中的,因此 h2样式会覆盖 NoEncapsulationComponent中的样式。结果是 NoEncapsulationComponent中的 &lt;h2&gt;元素被着色为蓝色而不是红色,这可能不是组件作者的本意。

component with no encapsulation

Last Updated: 5/16/2023, 7:35:10 PM