ViewEncapsulation 现饭现炒
在Angular组件封装中,组件样式难免会遇到被第三方样式覆盖的问题,不可能一直用
!important来提升样式优先级,如果不加:host样式可能会向父节点穿透而覆盖其他组件样式,所以尽量避免使用!important,本文介绍Angular组件封装的几种方式。
先了解下ShadowDom
关于ShadowDom简而言之,能使我们将Scoped Styles应用于封装元素,而不会影响其他元素。
ViewEncapsulation Types
| Mode | Value | Description | 
|---|---|---|
| Emulated | 0 | 通过向宿主元素添加包含替代 ID 的属性并预处理通过 styles 或 styleUrls 提供样式规则,来模拟 Native 所有选择器。这是默认选项。 | 
| None | 2 | 不要提供任何模板或样式封装。 | 
| ShadowDom | 3 | 使用 Shadow DOM 封装样式。 | 
现在我们一个一个地查看所有ViewEncapsulation模式。
先介绍下例子的组件结构
app.html
1  | <hello name="{{ name }}"></hello>  | 
example.html
1  | <p class="example">  | 
example.css
1  | .example {  | 
child.html
1  | <p class="example">  | 
在appComponent组件中app-example与app-child组件同级,且只有example组件设置了样式。
1.ViewEncapsulation.None
首先在app-example组件下设置ViewEncapsulation.None
1  | ({  | 

可以看到三个example样式不仅在子组件生效,在同级组件也生效,似乎样式穿透到了上级,与全局样式的效果相同。所以用的时候需要注意使用场景。除非在example样式上封一层样式,像下面这样:
1  | .content {  | 
而且使用了ViewEncapsulation.None的组件,样式权重比全局样式权重和第三方样式高,这一点也要注意。
2.ViewEncapsulation.Emulated
1  | ({  | 
再切换到ViewEncapsulation.Emulated,也可以直接去掉encapsulation属性,因为它就是默认的。切换之后看到例子,它只对设置了encapsulation的组件本身生效,不会向子元素穿透。

同None属性一样,样式权重大于全局样式,但是第三方组件样式权重可能会大于Emulated。
用:host ::ng-deep伪类选择器 选择宿主元素以及子元素样式生效,必要时可以使用None属性。
:host ::ng-deep会创建一个属性_ngcontent-xxs-c360(每次渲染会有不同),来标识宿主class
1  | .example[_ngcontent-xxs-c360] {  | 
2.ViewEncapsulation.ShadowDom
1  | ({  | 
很明显ViewEncapsulation.ShadowDom会让组件最终渲染成 Shadom Dom,样式会与父组件隔离,子组件会生效。

在
Shadom Dom中用:host ::ng-deep伪类选择器,不会生效,也没必要。编译之后的css被“封装”在了Shadom Dom里,与Emulated模式不同,不会生成 host 伪类
附上stackblitz,玩一玩方便理解。