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
,玩一玩方便理解。