Angular13 Angular 组件样式

2024-02-25 开发教程 Angular13 匿名 5

组件样式

Angular 应用使用标准的 CSS 来设置样式。这意味着你可以把关于 CSS 的那些知识和技能直接用于 Angular 程序中,例如:样式表、选择器、规则以及媒体查询等。

另外,Angular 还能把组件样式捆绑在组件上,以实现比标准样式表更加模块化的设计。

本章将会讲解如何加载和使用这些组件样式。

可以运行现场演练/ 下载范例,在 Stackblitz 中试用并下载本页的代码。

使用组件样式

对你编写的每个 Angular 组件来说,除了定义 HTML 模板之外,还要定义用于模板的 CSS 样式、 指定任意的选择器、规则和媒体查询。

实现方式之一,是在组件的元数据中设置 ​styles ​属性。 ​styles ​属性可以接受一个包含 CSS 代码的字符串数组。 通常你只给它一个字符串就行了,如同下例:

@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 {
/* . . . */
}

范围化的样式

在 ​@Component​ 的元数据中指定的样式只会对该组件的模板生效。

它们既不会被模板中嵌入的组件继承,也不会被通过内容投影(如 ng-content)嵌进来的组件继承。

在这个例子中,​h1​ 的样式只对 ​HeroAppComponent ​生效,既不会作用于内嵌的 ​HeroMainComponent​,也不会作用于应用中其它任何地方的 ​<h1>​ 标签。

这种范围限制就是所谓的样式模块化特性

  • 可以使用对每个组件最有意义的 CSS 类名和选择器。
  • 类名和选择器是局限于该组件的,它不会和应用中其它地方的类名和选择器冲突。
  • 组件的样式不会因为别的地方修改了样式而被意外改变。
  • 可以让每个组件的 CSS 代码和它的 TypeScript、HTML 代码放在一起,这将促成清爽整洁的项目结构。
  • 以后还可以修改或移除组件的 CSS 代码,而不用遍历整个应用来看它有没有在别处用到。

特殊的选择器

组件样式中有一些从影子(Shadow) DOM 样式范围领域(记录在W3CCSS Scoping Module Level 1中) 引入的特殊选择器:

:host

每个组件都会关联一个与其组件选择器相匹配的元素。这个元素称为宿主元素,模板会渲染到其中。​:host​ 伪类选择器可用于创建针对宿主元素自身的样式,而不是针对宿主内部的那些元素。

@Component({
selector: 'app-main',
template: `
<h1>It Works!</h1>
<div>
Start editing to see some magic happen :)
</div>
`
})
export class HostSelectorExampleComponent {
}

下面的样式将以组件的宿主元素为目标。应用于此选择器的任何规则都将影响宿主元素及其所有后代(在这种情况下,将所有包含的文本斜体)。(译注:后代的样式源自 CSS 的样式继承特性)

:host {
font-style: italic;
}

:host​ 选择是是把宿主元素作为目标的唯一方式。除此之外,你将没办法指定它, 因为宿主不是组件自身模板的一部分,而是父组件模板的一部分。

要把宿主样式作为条件,就要像函数一样把其它选择器放在 ​:host​ 后面的括号中。

在这个例子中,当 CSS 类 ​active ​应用在宿主元素上时,宿主元素的内容也变成了粗体。

: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 ​时,才会把该组件内部的所有文本置为斜体。

:host-context(.active) {
font-style: italic;
}

注意,只有宿主元素及其各级子节点会受到影响,不包括加上 ​active ​类的这个节点的祖先。

已弃用 /deep/、>>> 和 ::ng-deep

组件样式通常只会作用于组件自身的 HTML 上。

把伪类 ​::ng-deep​ 应用到任何一条 CSS 规则上就会完全禁止对那条规则的视图包装。任何带有 ​::ng-deep​ 的样式都会变成全局样式。为了把指定的样式限定在当前组件及其下级组件中,请确保在 ​::ng-deep​ 之前带上 ​:host​ 选择器。如果 ​::ng-deep​ 组合器在 ​:host​ 伪类之外使用,该样式就会污染其它组件。

这个例子以所有的 ​<h3>​ 元素为目标,从宿主元素到当前元素再到 DOM 中的所有子元素:

: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。

@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)
@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 {
/* . . . */
}
src/app/hero-app.component.css
h1 {
font-weight: normal;
}

注意:这些样式只对当前组件生效。 它们既不会作用于模板中嵌入的任何组件,也不会作用于投影进来的组件(如 ​ng-content​ )。

你可以指定多个样式文件,甚至可以组合使用 ​style ​和 ​styleUrls ​方式。

当你使用 Angular CLI 的 ​ng generate component​ 命令但不带 ​--inline-style​ 标志时,CLI 会为你创建一个空白的样式表文件,并且在所生成组件的 ​styleUrls ​中引用该文件。

ng generate component hero-app

模板内联样式

可以直接在组件的 HTML 模板中写 ​<style>​ 标签来内嵌 CSS 样式。

@Component({
selector: 'app-hero-controls',
template: `
<style>
button {
background-color: white;
border: 1px solid #777;
}
</style>
<h3>Controls</h3>
<button (click)="activate()">Activate</button>
`
})

模板中的 link 标签

你也可以在组件的 HTML 模板中写 ​<link>​ 标签。

@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 文件的。

/* 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 预处理器。

添加到 ​@Component.styles​ 数组中的字符串必须写成 CSS,因为 CLI 没法对这些内联的样式使用任何 CSS 预处理器。