1、设置视图封装
默认情况下,组件专用样式的实现方式是:编写应用于组件的CSS或less文件,使其针对一些特殊属性,让Angular将这些属性添加到组件模板的所有顶级元素中。如果使用浏览器的F12开发者工具检查DOM,将看到代码中的外部样式文件的内容已被重写,例如:
...
<style>
div[_ngcontent-c0] {
backgroud-color: red;
}
</style>
...
对选择器加以修改,使其能够匹配所有带有名为_ngcontent-c0的属性的div元素,但可能会在浏览器中看到不同名称,是因为该属性名称是由Angular动态生成的。
为了保证style元素中的样式仅影响组件管理的HTML元素,对模板中的元素进行修改,这样它们就具有相同的动态生成的属性,如下所示:
...
<div _ngcontent-c0="" class="form-group">
<input _ngcontent-c0="" class="form-control ng-invalid ng-untouched" ng-reflect-name="name" name="name"/>
</div>
...
这被称为组件的视图封装(view encapsulation)行为,Angular正在做的是模拟被称为影子DOM(Shadow DOM)的特性:让DOM的各个部分彼此隔离,各自具有自己的范围,因此可以把JS、样式和模板应用到HTML文档的某一部分。Angular之所以模拟这种行为,是因为它仅由少量浏览器实现支持影子DOM特性,但有另外两个封装选项,可以使用@Component装饰器中的encapsulation属性来设置这些选项。
什么是影子DOM?
Web components 的一个重要属性是封装——可以将标记结构、样式和行为隐藏起来,并与页面上的其他代码相隔离,保证不同的部分不会混在一起,可使代码更加干净、整洁。其中,Shadow DOM 接口是关键所在,它可以将一个隐藏的、独立的 DOM 附加到一个元素上。(MDN上有详细描述:Shadow DOM)
ViewEncapsulation枚举中的值
名称 | 描述 |
---|---|
Emulated | 通过改写内容和样式来添加属性,从而模拟影子DOM。如果@Component装饰器中未指定encapsulation属性的值,那么这是默认行为 |
Native | 使用浏览器的影子DOM特性。这只有在浏览器实现影子DOM或者使用polyfill库时才有效 |
None | 将未修改的CSS样式添加到HTML文档的head节中,并让浏览器设法使用正常的CSS优先级规则来应用样式 |
应该谨慎使用Native和None。浏览器对影子DOM特性的支持非常有限,只有在使用polyfill库(为其他浏览器提供兼容性)时,才能使用Native选项。
选项None将组件定义的所有样式添加到HTML文档的head节中,并让浏览器设法应用它们。虽然这可以在所有浏览器中运行,但其结果是不可预测的,并且不同组件定义的样式之间没有隔离。为完整起见,使用默认的Emulated,它可以在Angular2支持的所有浏览器中运行而不需要polyfill库。
2、使用影子DOM CSS选择器
使用影子DOM意味着存在一些边界,普通的CSS选择器不能跨越。下面列出的一些特殊的CSS选择器,当使用依赖影子DOM的样式(即使通过仿真实现)时,就会用到这些特殊的CSS选择器
名称 | 描述 |
---|---|
:host | 用于匹配组件的宿主元素 |
:host-context(classSelector) | 用于匹配属于特定CSS类成员的宿主元素的祖先 |
/deep/ 或 >> | 父组件使用此选择器来定义影响其子组件模板中元素的样式,只有当@Coponent装饰器的encapsulation属性被设置为emulated时,才应使用这个选择器 |
- 选择宿主元素
组件的宿主元素出现在其模板之外,这意味着其样式中选择器仅适用于宿主元素所包含的内部元素,而不适用于宿主元素本身。这个问题可通过使用与宿主元素相匹配的:host选择器来解决。
div {
backgroud-color: red;
}
:host:hover {
font-size: 20px;
}
- 选择宿主元素的祖先
:host-context选择器用于根据宿主元素的祖先(位于模板之外)的CSS类成员资格对组件模板中的元素进行样式化。这是相比:host更受限的选择器,不能用于指定除CSS类选择器以外的任何东西,而且不支持匹配标签类型、属性或任何其他选择器。
只有在宿主元素的某个祖先元素是CSS类angular-app的成员时,选择器才将组件模板中input元素的backgroud-color属性设置为blue。
div {
backgroud-color: red;
}
:host:hover {
font-size: 20px;
}
:host-context(.angular-app) input {
backgroud-color: blue;
}
- 将样式推入子组件的模板中
由组件定义的样式不会被自动应用到子组件模板的元素中。
默认情况下,某些CSS样式属性(如font-style)会被继承,这意味着在父组件中设置这样的属性会影响子组件模板中的元素,这是因为浏览器会自动应用样式。
而其他属性(如边框)默认情况下不会被继承,在父组件中设置这些属性对子组件模板没有影响,除非使用/deep/或>>选择器。样式选择器使用/deep/将样式推入子组件的模板中,这意味着所有div元素都被赋予一个边框。
...
@Component({
selector: "app",
templateUrl: "template.html",
style: "/deep/ div { border:2px black solid; font-style: italic }"
})
...
}