# 组件样式
Angular 应用使用标准的 CSS 来设置样式。这意味着你可以把关于 CSS 的那些知识和技能直接用于 Angular 程序中,比如:样式表、选择器、规则以及媒体查询等。
另外,Angular 还能把组件样式捆绑在组件上,以实现比标准样式表更加模块化的设计。
本章将会讲解如何加载和使用这些组件样式。
可以运行现场演练/ 下载范例
,在 Stackblitz 中试用并下载本页的代码。
# 使用组件样式
对你编写的每个 Angular 组件来说,除了定义 HTML 模板之外,还要定义用于模板的 CSS 样式、 指定任意的选择器、规则和媒体查询。
实现方式之一,是在组件的元数据中设置 styles
属性。styles
属性可以接受一个包含 CSS 代码的字符串数组。通常你只给它一个字符串就行了,如同下例:
src/app/hero-app.component.ts
@Component({
selector: 'app-root',
template: `
<h1>Tour of Heroes</h1>
<app-hero-main [hero]="hero"></app-hero-main>
`,
styles: ['h1 { font-weight: normal; }']
})
export class HeroAppComponent {
/* . . . */
}
# 组件样式最佳实践
有关 Angular 如何将样式范围限定为特定组件的信息,参阅视图封装。
你应该将组件的样式视为该组件的私有实现细节。使用通用组件时,你不应该覆盖组件的样式,就像访问 TypeScript 类的私有成员一样。虽然 Angular 的默认样式封装可防止组件样式影响其他组件,但全局样式会影响页面上的所有组件。这包括 ::ng-deep
,它会将组件样式提升为全局样式。
# 创作支持自定义样式的组件
作为组件作者,你可以用四种不同的方式之一显式设计组件以接受自定义。
# 1. 使用 CSS 自定义属性(推荐)
你可以通过使用 CSS 自定义属性(也称为 CSS 变量)定义其样式来为组件定义受支持的自定义 API。使用你组件的任何人都可以通过为这些属性定义值、自定义组件在渲染页面上的最终外观来使用此 API。
虽然这需要为每个自定义点定义一个自定义属性,但它创建了一个清晰的 API 契约,可以在所有样式的封装模式下工作。
# 2.使用 @mixin
声明全局 CSS
虽然 Angular 的模拟样式封装可防止样式从组件中逃逸,但它并不能防止全局 CSS 影响整个页面。虽然组件使用者应避免直接覆盖组件的 CSS 内部,但你可以通过 Sass 等 CSS 预处理器提供受支持的自定义 API。
比如,组件可以提供一个或多个受支持的 mixin 来自定义组件外观的各个方面。虽然这种方法在其实现中使用了全局样式,但它允许组件作者通过对组件的私有 DOM 结构和 CSS 类的更改来使 mixins 保持最新。
# 3.使用 CSS ::part
自定义
如果你的组件使用了 Shadow DOM
,你可以应用 part
属性来指定组件模板中的元素。这允许组件的使用者使用 ::part伪元素
创作针对这些特定元素的任意样式。
虽然这允许你限制模板中消费者可以自定义的元素,但它并不能限制哪些 CSS 属性是可自定义的。
# 4.提供 TypeScript API
你可以定义一个 TypeScript API 来自定义样式,使用模板绑定来更新 CSS 类和样式。不建议这样做,因为这种样式 API 的额外 JavaScript 成本会产生比 CSS 高得多的性能成本。
# 特殊的选择器
组件样式中有一些从影子(Shadow) DOM 样式范围领域(记录在W3C的CSS Scoping Module Level 1中)引入的特殊选择器:
# :host
每个组件都会关联一个与其组件选择器相匹配的元素。这个元素称为宿主元素,模板会渲染到其中。:host
伪类选择器可用于创建针对宿主元素自身的样式,而不是针对宿主内部的那些元素。
src/app/host-selector-example.component.ts
@Component({
selector: 'app-main',
template: `
<h1>It Works!</h1>
<div>
Start editing to see some magic happen :)
</div>
`
})
export class HostSelectorExampleComponent {
}
下面的样式将以组件的宿主元素为目标。应用于此选择器的任何规则都将影响宿主元素及其所有后代(在这种情况下,将所有包含的文本斜体)。(译注:后代的样式源自 CSS 的样式继承特性)
src/app/hero-details.component.css
:host {
font-style: italic;
}
:host
选择是是把宿主元素作为目标的唯一方式。除此之外,你将没办法指定它,因为宿主不是组件自身模板的一部分,而是父组件模板的一部分。
要把宿主样式作为条件,就要像函数一样把其它选择器放在 :host
后面的括号中。
在这个例子中,当 CSS 类 active
应用在宿主元素上时,宿主元素的内容也变成了粗体。
src/app/hero-details.component.css
:host {
font-style: italic;
}
:host(.active) {
font-weight: bold;
}
:host
选择器也可以与其他选择器组合使用。在 :host
后面添加选择器以选择子元素,比如,使用 :host h2
定位组件视图内的 <h2>
。
不应该在 :host
选择器前面添加除 :host-context
之外的选择器来试图基于组件视图的外部上下文为本组件设置样式。因为此类选择器的作用域不会限于组件的视图,而是会选择外部上下文,但这不是内置的行为。请改用 :host-context
选择器。
# :host-context
有时候,需要以某些来自宿主的祖先元素为条件来决定是否要应用某些样式。比如,在文档的 <body>
元素上可能有一个用于表示样式主题 (theme) 的 CSS 类,你应当基于它来决定组件的样式。
这时可以使用 :host-context()
伪类选择器。它也以类似 :host()
形式使用。它在当前组件宿主元素的祖先节点中查找 CSS 类,直到文档的根节点为止。它只能与其它选择器组合使用。
在下面的例子中,只有当该组件的某个祖先元素有 CSS 类 active
时,才会把该组件内部的所有文本置为斜体。
src/app/hero-details.component.css
:host-context(.active) {
font-style: italic;
}
注意
只有宿主元素及其各级子节点会受到影响,不包括加上 active
类的这个节点的祖先。
# 已弃用 /deep/
、>>>
和 ::ng-deep
组件样式通常只会作用于组件自身的 HTML 上。
把伪类 ::ng-deep
应用到任何一条 CSS 规则上就会完全禁止对那条规则的视图封装。任何带有 ::ng-deep
的样式都会变成全局样式。为了把指定的样式限定在当前组件及其下级组件中,请确保在 ::ng-deep
之前带上 :host
选择器。如果 ::ng-deep
组合器在 :host
伪类之外使用,该样式就会污染其它组件。
这个例子以所有的 <h3>
元素为目标,从宿主元素到当前元素再到 DOM 中的所有子元素:
src/app/hero-details.component.css
:host ::ng-deep h3 {
font-style: italic;
}
/deep/
组合器还有两个别名:>>>
和 ::ng-deep
。
/deep/
和 >>>
选择器只能被用在**仿真 (emulated)**模式下。这种方式是默认值,也是用得最多的方式。更多信息,见控制视图封装模式一节。
CSS 标准中用于 "刺穿 Shadow DOM" 的组合器已经被弃用,并将这个特性从主流浏览器和工具中移除。因此,我们也将在 Angular 中移除对它们的支持(包括 /deep/
、>>>
和 ::ng-deep
)。目前,建议先统一使用 ::ng-deep
,以便兼容将来的工具。
# 把样式加载进组件中
有几种方式把样式加入组件:
设置
styles
或styleUrls
元数据内联在模板的 HTML 中
通过 CSS 文件导入
上述作用域规则对所有这些加载模式都适用。
# 元数据中的样式
给 @Component
装饰器添加一个 styles
数组型属性。
这个数组中的每一个字符串(通常也只有一个)定义一份 CSS。
src/app/hero-app.component.ts (CSS inline)
@Component({
selector: 'app-root',
template: `
<h1>Tour of Heroes</h1>
<app-hero-main [hero]="hero"></app-hero-main>
`,
styles: ['h1 { font-weight: normal; }']
})
export class HeroAppComponent {
/* . . . */
}
注意:这些样式只对当前组件生效。它们既不会作用于模板中嵌入的任何组件,也不会作用于投影进来的组件(如 ng-content
)。
当使用 --inline-styles
标识创建组件时,Angular CLI 的 ng generate component
命令就会定义一个空的 styles
数组。
ng generate component hero-app --inline-style
# 组件元数据中的样式文件
把外部 CSS 文件添加到 @Component
的 styleUrls
属性中以加载外部样式。
src/app/hero-app.component.ts (CSS in file)
src/app/hero-app.component.css
@Component({
selector: 'app-root',
template: `
<h1>Tour of Heroes</h1>
<app-hero-main [hero]="hero"></app-hero-main>
`,
styleUrls: ['./hero-app.component.css']
})
export class HeroAppComponent {
/* . . . */
}
注意:这些样式只对当前组件生效。它们既不会作用于模板中嵌入的任何组件,也不会作用于投影进来的组件(如 ng-content
)。
你可以指定多个样式文件,甚至可以组合使用 style
和 styleUrls
方式。
当你使用 Angular CLI 的 ng generate component
命令但不带 --inline-style
标志时,CLI 会为你创建一个空白的样式表文件,并且在所生成组件的 styleUrls
中引用该文件。
ng generate component hero-app
# 模板内联样式
可以直接在组件的 HTML 模板中写 <style>
标签来内嵌 CSS 样式。
src/app/hero-controls.component.ts
@Component({
selector: 'app-hero-controls',
template: `
<style>
button {
background-color: white;
border: 1px solid #777;
}
</style>
<h3>Controls</h3>
<button type="button" (click)="activate()">Activate</button>
`
})
# 模板中的 link 标签
你也可以在组件的 HTML 模板中写 <link>
标签。
src/app/hero-team.component.ts
@Component({
selector: 'app-hero-team',
template: `
<!-- We must use a relative URL so that the AOT compiler can find the stylesheet -->
<link rel="stylesheet" href="../assets/hero-team.component.css">
<h3>Team</h3>
<ul>
<li *ngFor="let member of hero.team">
{{member}}
</li>
</ul>`
})
当使用 CLI 进行构建时,要确保这个链接到的样式表文件被复制到了服务器上。参阅资产文件配置指南。
只要引用过,CLI 就会计入这个样式表,无论这个 link 标签的 href 指向的 URL 是相对于应用根目录的还是相对于组件文件的。
# CSS @imports 语法
可以利用标准的 CSS @import规则
来把其它 CSS 文件导入到 CSS 文件中。
在这种情况下,URL 是相对于你正在导入的 CSS 文件的。
src/app/hero-details.component.css (excerpt)
/* The AOT compiler needs the `./` to show that this is local */
@import './hero-details-box.css';
# 外部以及全局样式文件
当使用 CLI 进行构建时,你必须配置 angular.json
文件,使其包含所有外部资源(包括外部的样式表文件)。
在它的 styles
区注册这些全局样式文件,默认情况下,它会有一个预先配置的全局 styles.css
文件。
要了解更多,参阅 样式配置指南
。
# 非 CSS 样式文件
如果使用 CLI 进行构建,那么你可以用 sass
或 less
来编写样式,并使用相应的扩展名(.scss
、.less
)把它们指定到 @Component.styleUrls
元数据中。例子如下:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
…
CLI 的构建过程会运行相关的预处理器。
当使用 ng generate component
命令生成组件文件时,CLI 会默认生成一个空白的 CSS 样式文件(.css
)。你可以配置 CLI,让它默认使用你喜欢的 CSS 预处理器,参阅工作区配置指南中的解释。
← 组件交互 在父子指令及组件之间共享数据 →