Introduction
Angular中有三种样式封装方式,分别是:Emulated
, ShadowDom
, and None
。
- ViewEncapsulation.Emulated (Default value)
- ViewEncapsulation.ShadowDom
- ViewEncapsulation.None
可以用过Component中的encapsulation
属性来指定封装方式,如果不指定的话,默认是Emulated
。
1 | ({ |
下面我们分别讲解三种封装方式。
Emulated
这是默认的封装方式,如果你没有在@Component
中显式指定encapsulation
的话,那么使用的就是该方式。在该方式下,Angular会生成一个随机字符串做为组件的属性,然后将样式应用到这个属性上。
这种封装方式是模拟Shadow DOM
的方式,Angular会更改组件的CSS选择器,以便样式只应用于组件的视图。这意味着组件的样式不会影响应用程序的其他部分。
以下面的Product组件为例,product-home.component.scss
这个样式文件里面的样式只会影响ProductHomeComponent
这个组件对应的视图.
1 | ({ |
运行app后,我们可以在浏览器中inspect生成后的页面,可以看到app-product-home
元素有一个唯一的属性_nghost-ng-c3352351300
:
1 | <app-product-home _nghost-ng-c3352351300> |
再看一下实际的例子,假如我们有如下html template, ProductHomeComponent中引入了ProductDetailComponent
1 | <!-- product-home.component.html --> |
那么生成的HTML如下:
可以看到,Angular为生成的Dom节点都添加了额外的属性,这些属性大概分为两类:
_nghost-xxxx
- 用来标记组件的host element,比如上图中第一个红框内_ngcontent-xxxx
- 用来标记组件的子元素,比如上图中app-product-home
内的p
元素,它就是一个纯内容,所以标记是_ngcontent-xxxx
。- 有些元素既是host, 又是content,比如上图的第二个红框。因为
ProductDetailComponent
既是ProductHomeComponent
的子元素,而它本身又是一个组件。所以它同时拥有_nghost-xxxx
和_ngcontent-xxxx
两个属性。
如果我们给上面的p
标签添加一个背景色的话,那么观察生成后的css样式,你会发现,样式文件里面也添加了额外的属性,这样就确保了样式的唯一性。
观察下图中左侧p
标签的属性,和右侧css文件中product-container
的属性,可以看到他们是一一对应的。
Emulated
模式生成的样式最终会插入到html文件的head
标签中。(可以运行Angular app,然后鼠标右键-inspect查看对应的html文件代码)
ShadowDom
这种方式使用浏览器内置的Shadow DOM API来封装组件的样式,该模式下,组件被放到一个ShadowRoot之下,这个Shadow Root相当于组件的host element, 这意味着组件的样式不会影响应用程序的其他部分。
该模式生成的样式会直接放到shadow-root
中。
1 | ({ |
注意观察生成的html文件,app-product-home
元素会有一个shadow-root
:
1 | <app-product-home> |
None
这种模式不进行任何封装,相当于裸奔,其效果等于直接在html中引入样式文件。这种模式谨慎使用,因为会有样式污染。
该模式生成的样式会直接放到html文件的head
中。
1 | ({ |
The generated HTML will not have any unique attributes or shadow root:
1 | <app-product-home> |
注意:ViewEncapsulation.None
ViewEncapsulation.Emulated
中的样式同时会插入到ViewEncapsulation.ShadowDom
的组件中。
References
- https://v17.angular.io/guide/view-encapsulation - 这里面的例子要仔细研究一下,有很多细节。