Angular 复习与进阶系列 – Component 组件 の Angular Component vs Shadow DOM (CSS Isolation)

发布时间 2023-04-06 21:23:20作者: 兴杰

CSS Global Effect

CSS style 是全局影响的.

假设我们有 2 个组件, AppComponent 和 TestComponent

app html

<div class="container">
  <h1>Outside Hello World</h1>
  <app-test></app-test>
</div>

app css

h1 {
  background-color: pink;
}

test html

<h1>Inside Hello World</h1>

test css

h1 {
  font-size: 48px;
  color: red;
}

两个组件 html 都有 h1 element, 同时 2 个组件的 css 都给予 h1 不同的 style (1 个给 background-color, 1 个给 color)

最终效果

两个 h1 element 都渲染了 2 个 styles (background-color & color) 这就是 CSS 默认的行为.

但总所周知, 全局是魔鬼, 所以 CSSer 有许许多多方法来解决这个问题, 比如 BEM. 而 Angular 也用了其中二种方案.

 

Angular CSS Isolation

如果你按照我上面的例子写, 你会发现出来的效果 style 并没有互相渲染, 这是因为 Angular 默认就开启了 CSS 隔离. (我为了演示特意关闭的)

Angular 有 2 种隔离方案. 它们都可以达到隔离效果, 但有一点点微小的区别.

Shadow DOM 隔离方案

第一种方案是 Shadow DOM, Shadow DOM 的主要功能本来就是隔离, 所以 Angular 会选择这种方案是显而易见的. 

我们在组件的 metadata 里声明 

encapsulation: ViewEncapsulation.ShadowDom

这样就开启了 Shadow DOM 隔离

效果

element 结构

Emulated Shadow DOM 隔离方案

encapsulation: ViewEncapsulation.Emulated

有 Shadow DOM 方案不就够了吗? 为什么还要搞多一个 Emulated (模拟) 方案呢? 而且这个模拟方案竟然还是 default (首选) 方案 !?

Shadow DOM 会隔离 "所有" 外部的 style. 这也意味着我们不可以写全局的 reset.css, base.css 等等. 虽然我们想隔离, 但是我们想隔离的是各个组件之间的 style,

而不是像 reset.css 这种通用的全局 style.

为此 Angular 就搞了一个 Emulated Shadow DOM, 它和 Shadow DOM 一样不允许组件的 style 跑出去, 但它允许外面的 style 跑进来.

试想, Angular 项目中全部都是组件. 所有的 style 都跑不出去, 那又会有什么 style 跑进来呢? 

有, 那就是组件之外的, styles.scss

这个 styles.scss 就可以让我们写全局 reset 和 base style.

Emulated 的具体实现手法类似 BEM, 通过给 class selector 长命名来起到相互不被影响, 它整个过程是在 compile 阶段完成的, 所以我们完全感受不到.

下面是最终生成出来的 HTML 和 style

element 和 CSS selector 都多了许多 attribute, _ngcontent-ng-c123456789

这样就实现了隔离效果.

关闭所有隔离方案

上面有提到, Angular 默认就替组件开启了 Emulated 隔离方案, 所以哪怕我们什么也没声明, 它就已经是隔离的了.

但如果我们想关关闭隔离方案也是可以的, 声明

encapsulation: ViewEncapsulation.None

这样就关闭了.