把样式加载进组件中
我们有几种方式来把样式加入组件:
内联在模板的 HTML 中
设置styles或styleUrls元数据
通过 CSS 文件导入
上述作用域规则对所有这些加载模式都适用。
元数据中的样式
我们可以给@Component装饰器添加一个styles数组型属性。 这个数组中的每一个字符串(通常也只有一个)定义一份 CSS。
@Component({
selector: 'hero-app',
template: `
<h1>Tour of Heroes</h1>
<hero-app-main [hero]=hero></hero-app-main>`,
styles: ['h1 { font-weight: normal; }']
})
export class HeroAppComponent {
/* . . . */
}
模板内联样式
我们也可以把它们放到
@Component({
selector: 'hero-controls',
template: `
<style>
button {
background-color: white;
border: 1px solid #777;
}
</style>
<h3>Controls</h3>
<button (click)="activate()">Activate</button>
`
})
元数据中的样式表 URL
我们还可以通过给组件的@Component装饰器中添加一个styleUrls属性来从外部 CSS 文件中加载样式:
@Component({
selector: 'hero-details',
template: `
<h2>{{hero.name}}</h2>
<hero-team [hero]=hero></hero-team>
<ng-content></ng-content>
`,
styleUrls: ['app/hero-details.component.css']
})
export class HeroDetailsComponent {
/* . . . */
}
URL是相对于应用程序根目录的,它通常是应用的宿主页面index.html所在的地方。 这个样式文件的 URL 不是相对于组件文件的。这就是为什么范例中的 URL 用app/开头。 参见附录 2 来了解如何指定相对于组件文件的 URL。
像 Webpack 这类模块打包器的用户可能会使用styles属性来在构建时从外部文件中加载样式。它们可能这样写:
styles: [require(‘my.component.css’)]
注意,这时候我们是在设置styles属性,而不是styleUrls属性! 是模块打包器在加载 CSS 字符串,而不是 Angular。 Angular 看到的只是打包器加载它们之后的 CSS 字符串。 对 Angular 来说,这跟我们手写了styles数组没有任何区别。 要了解这种 CSS 加载方式的更多信息,请参阅相应模块打包器的文档。
模板中的 link 标签
我们也可以在组件的 HTML 模板中嵌入标签。
像styleUrls标签一样,这个 link 标签的href指向的 URL 也是相对于应用的根目录的,而不是组件文件。
@Component({
selector: 'hero-team',
template: `
<link rel="stylesheet" href="app/hero-team.component.css">
<h3>Team</h3>
<ul>
<li *ngFor="let member of hero.team">
{{member}}
</li>
</ul>`
})
CSS @imports
我们还可以利用标准的 CSS @import规则来把其它 CSS 文件导入到我们的 CSS 文件中。
在这种情况下,URL 是相对于我们执行导入操作的 CSS 文件的。
app/hero-details.component.css (excerpt)
@import 'hero-details-box.css';
控制视图的封装模式:原生 (Native)、仿真 (Emulated) 和无 (None)
像上面讨论过的一样,组件的 CSS 样式被封装进了自己的视图中,而不会影响到应用程序的其它部分。
通过在组件的元数据上设置视图封装模式,我们可以分别控制每个组件的封装模式。 可选的封装模式一共有三种:
Native模式使用浏览器原生的 Shadow DOM 实现来为组件的宿主元素附加一个 Shadow DOM。组件的样式被包裹在这个 Shadow DOM 中。(译注:不进不出,没有样式能进来,组件样式出不去。)
Emulated模式(默认值)通过预处理(并改名)CSS 代码来模拟 Shadow DOM 的行为,以达到把 CSS 样式局限在组件视图中的目的。 更多信息,见附录 1 。(译注:只进不出,全局样式能进来,组件样式出不去)
None意味着 Angular 不使用视图封装。 Angular 会把 CSS 添加到全局样式中。而不会应用上前面讨论过的那些作用域规则、隔离和保护等。 从本质上来说,这跟把组件的样式直接放进 HTML 是一样的。(译注:能进能出。)
通过组件元数据中的encapsulation属性来设置组件封装模式:
// warning: few browsers support shadow DOM encapsulation at this time
encapsulation: ViewEncapsulation.Native
原生(Native)模式只适用于有原生 Shadow DOM 支持的浏览器。 因此仍然受到很多限制,这就是为什么我们会把仿真 (Emulated) 模式作为默认选项,并建议将其用于大多数情况。
附录 1:查看仿真 (Emulated) 模式下生成的 CSS
当使用默认的仿真模式时,Angular 会对组件的所有样式进行预处理,让它们模仿出标准的 Shadow CSS 作用域规则。
当我们查看启用了仿真模式的 Angular 应用时,我们看到每个 DOM 元素都被加上了一些额外的
<hero-details _nghost-pmm-5>
<h2 _ngcontent-pmm-5>Mister Fantastic</h2>
<hero-team _ngcontent-pmm-5 _nghost-pmm-6>
<h3 _ngcontent-pmm-6>Team</h3>
</hero-team>
</hero-detail>
到了两种被生成的属性:
一个元素在原生封装方式下可能是 Shadow DOM 的宿主,在这里被自动添加上一个_nghost属性。 这是组件宿主元素的典型情况。
组件视图中的每一个元素,都有一个_ngcontent属性,它会标记出该元素是哪个宿主的模拟 Shadow DOM。
这些属性的具体值并不重要。它们是自动生成的,并且我们永远不会在程序代码中直接引用到它们。 但它们会作为生成的组件样式的目标,就像我们在 DOM 的区所看到的:
[_nghost-pmm-5] {
display: block;
border: 1px solid black;
}
h3[_ngcontent-pmm-6] {
background-color: white;
border: 1px solid #777;
}
这些就是我们写的那些样式被处理后的结果,于是每个选择器都被增加了_nghost或_ngcontent属性选择器。 在这些附加选择器的帮助下,我们实现了本指南中所描述的这些作用域规则。
小伙伴们会很愉快的使用仿真模式 —— 直到有一天 Shadow DOM 获得全面支持。
附录 2:使用相对 URL 加载样式
把组件的代码 (ts/js)、HTML 和 CSS 分别放到同一个目录下的三个不同文件,是一个常用的实践:
quest-summary.component.ts
quest-summary.component.html
quest-summary.component.css
我们会通过设置元数据的templateUrl和styleUrls属性把模板和 CSS 文件包含进来。 既然这些文件都与组件(代码)文件放在一起,那么通过名字,而不是到应用程序根目录的全路径来指定它,就会是一个漂亮的方式。
通过把组件元数据的moduleId属性设置为module.id,我们可以更改 Angular 计算完整 URL 的方式
app/quest-summary.component.ts
@Component({
moduleId: module.id,
selector: 'quest-summary',
templateUrl: 'quest-summary.component.html',
styleUrls: ['quest-summary.component.css']
})
export class QuestSummaryComponent { }