Angular 组件样式的封装
样式封装
在html中,无论一个style元素定义在什么地方,其内部的规则都是作用到整个html文档的。
从框架的设计和使用者角度来看,对组件样式都一种封装需求:就是希望针对组件定义的样式只在这个组件内部生效,不影响这个组件之外的元素。
Angular 提供了3种样式封装选项: ViewEncapsulation.Native, ViewEncapsulation.Emulated, ViewEncapsulation.None。 这里有篇介绍文章:https://blog.thoughtram.io/angular/2015/06/29/shadow-dom-strategies-in-angular2.html
可以在定义组件时配置样式封装的方式,例如:
@Component({
moduleId: module.id,
selector: '...',
templateUrl: '....component.html',
styles: [`...`],
encapsulation: ViewEncapsulation.None
})
默认的封装方式是 ViewEncapsulation.Emulated, 因为Native会使用shadow Dom, 而shadow Dom 现在并没有得到广泛的浏览器支持。
默认的样式封装带来的问题
默认的封装方式 Emulated ,Angular在运行时会同时对该组件的 html 模板和 css规则进行修改:
-
html模板中的元素都会添加一个属性 _ngcontent-c* , 星号表示一个数字,例如
<div _ngcontent-c39="" class="topo-container">
-
每条样式规则的每个selector都会加上和上面元素新增的属性对应的属性选择器,例如:
你定义的规则是这样的:
div.button-cotainer div.play-button button.fa-arrow-left { padding-left: 7px; padding-top: 1px; }
在浏览器中查看规则,你看到的是这样的:
div.button-cotainer[_ngcontent-c39] div.play-button[_ngcontent-c39] button.fa-arrow-left [_ngcontent-c39] { padding-left: 7px; padding-top: 1px; }
上面这种对CSS规则的修改,会带来一个问题:
-
由于在JS代码中通过web api(例如 appenChild())动态添加的元素是没有 _ngcontent-c* 这种属性的,导致组件的元数据中的CSS规则无法应用到这种动态添加的元素。
规避这个问题的方法:使用 /deep/ 语法,该语法告知angular不要在 /deep/之后的元素类型上添加属性修饰,例如:选择器 /deep/ p span 经过angular编译后 就是 p span, 当然这样就使得样式具有了全局性,所以最好是结合:host 使用, 例如 :host /deep/ p span。