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
2
3
4
5
6
7
<hello name="{{ name }}"></hello>
<p>
Start editing to see some magic happen :)
</p>

<app-example></app-example>
<app-child></app-child>

example.html

1
2
3
4
5
<p class="example">
example works!
</p>

<app-child></app-child>

example.css

1
2
3
4
5
.example  {
width: 100px;
height: 100px;
background: #ff1493;
}

child.html

1
2
3
<p class="example">
child works!
</p>

appComponent组件中app-exampleapp-child组件同级,且只有example组件设置了样式。

1.ViewEncapsulation.None

首先在app-example组件下设置ViewEncapsulation.None

1
2
3
@Component({
encapsulation: ViewEncapsulation.None
})

可以看到三个example样式不仅在子组件生效,在同级组件也生效,似乎样式穿透到了上级,与全局样式的效果相同。所以用的时候需要注意使用场景。除非在example样式上封一层样式,像下面这样:

1
2
3
4
5
6
7
.content {
.example {
width: 100px;
height: 100px;
background: #ff1493;
}
}

而且使用了ViewEncapsulation.None的组件,样式权重比全局样式权重和第三方样式高,这一点也要注意。

2.ViewEncapsulation.Emulated

1
2
3
@Component({
encapsulation: ViewEncapsulation.Emulated
})

再切换到ViewEncapsulation.Emulated,也可以直接去掉encapsulation属性,因为它就是默认的。切换之后看到例子,它只对设置了encapsulation的组件本身生效,不会向子元素穿透。

None属性一样,样式权重大于全局样式,但是第三方组件样式权重可能会大于Emulated
:host ::ng-deep伪类选择器 选择宿主元素以及子元素样式生效,必要时可以使用None属性。

:host ::ng-deep会创建一个属性_ngcontent-xxs-c360(每次渲染会有不同),来标识宿主class

1
2
3
4
5
.example[_ngcontent-xxs-c360] {
width: 100px;
height: 100px;
background: #ff1493;
}

2.ViewEncapsulation.ShadowDom

1
2
3
@Component({
encapsulation: ViewEncapsulation.ShadowDom
})

很明显ViewEncapsulation.ShadowDom会让组件最终渲染成 Shadom Dom,样式会与父组件隔离,子组件会生效。

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


附上stackblitz,玩一玩方便理解。