首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

组件样式

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

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

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

你可以运行在线例子 / 下载范例,在 Stackblitz 中试用并下载本页的代码。

使用组件样式

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

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

src/app/hero-app.component.ts

代码语言:javascript
复制
content_copy@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 伪类选择器,用来选择组件宿主元素中的元素(相对于组件模板内部的元素)。

src/app/hero-details.component.css

代码语言:javascript
复制
content_copy:host {
  display: block;
  border: 1px solid black;
}

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

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

下一个例子再次把宿主元素作为目标,但是只有当它同时带有 active CSS 类的时候才会生效。

src/app/hero-details.component.css

代码语言:javascript
复制
content_copy:host(.active) {
  border-width: 3px;
}

:host-context 选择器

有时候,基于某些来自组件视图外部的条件应用样式是很有用的。 例如,在文档的 <body> 元素上可能有一个用于表示样式主题 (theme) 的 CSS 类,你应当基于它来决定组件的样式。

这时可以使用 :host-context() 伪类选择器。它也以类似 :host() 形式使用。它在当前组件宿主元素的祖先节点中查找 CSS 类, 直到文档的根节点为止。在与其它选择器组合使用时,它非常有用。

在下面的例子中,只有当某个祖先元素有 CSS 类 theme-light 时,才会把 background-color 样式应用到组件内部的所有 <h2> 元素中。

src/app/hero-details.component.css

代码语言:javascript
复制
content_copy:host-context(.theme-light) h2 {
  background-color: #eef;
}

已废弃 /deep/>>>::ng-deep

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

可以使用 /deep/ 选择器来强制一个样式对各级子组件的视图也生效,它不但作用于组件的子视图,也会作用于组件的内容

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

src/app/hero-details.component.css

代码语言:javascript
复制
content_copy:host /deep/ h3 {
  font-style: italic;
}

/deep/ 组合器还有两个别名:>>>::ng-deep

/deep/>>> 选择器只能被用在仿真 (emulated) 模式下。 这种方式是默认值,也是用得最多的方式。 更多信息,见控制视图封装模式一节。

CSS 标准中用于 "刺穿 Shadow DOM" 的组合器已经被废弃,并将这个特性从主流浏览器和工具中移除。 因此,我们也将在 Angular 中移除对它们的支持(包括 /deep/>>>::ng-deep)。 目前,建议先统一使用 ::ng-deep,以便兼容将来的工具。

把样式加载进组件中

有几种方式把样式加入组件:

  • 设置 stylesstyleUrls 元数据
  • 内联在模板的 HTML 中
  • 通过 CSS 文件导入

上述作用域规则对所有这些加载模式都适用。

元数据中的样式

你可以给 @Component 装饰器添加一个 styles 数组型属性。

这个数组中的每一个字符串(通常也只有一个)定义一份 CSS。

src/app/hero-app.component.ts (CSS inline)

代码语言:javascript
复制
content_copy@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 标识创建组件时,CLI 就会定义一个空的 styles数组

代码语言:javascript
复制
content_copyng generate component hero-app --inline-style

组件元数据中的样式文件

你可以通过把外部 CSS 文件添加到 @ComponentstyleUrls 属性中来加载外部样式。

src/app/hero-app.component.ts (CSS in file)

src/app/hero-app.component.css

代码语言:javascript
复制
content_copy@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)。

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

CLI 会默认为你创建一个空白的样式表文件,并且在所生成组件的 styleUrls 中引用该文件。

代码语言:javascript
复制
content_copyng generate component hero-app

模板内联样式

你也可以在组件的 HTML 模板中嵌入 <style> 标签。

src/app/hero-controls.component.ts

代码语言:javascript
复制
content_copy@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> 标签。

src/app/hero-team.component.ts

代码语言:javascript
复制
content_copy@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 官方文档

只要引用过,CLI 就会计入这个样式表,无论这个 link 标签的 href 指向的 URL 是相对于应用根目录的还是相对于组件文件的。

CSS @imports 语法

你还可以利用标准的 CSS @import 规则来把其它 CSS 文件导入到 CSS 文件中。

这种情况下,URL 是相对于你正在导入的 CSS 文件的。

src/app/hero-details.component.css (excerpt)

代码语言:javascript
复制
content_copy/* The AOT compiler needs the `./` to show that this is local */
@import './hero-details-box.css';

外部以及全局样式文件

当使用 CLI 进行构建时,你必须配置 angular.json 文件,使其包含所有外部资源(包括外部的样式表文件)。

在它的 styles 区注册这些全局样式文件,默认情况下,它会有一个预先配置的全局 styles.css 文件。

要了解更多,参见 CLI 官方文档

非 CSS 样式文件

如果使用 CLI 进行构建,那么你可以用 sasslessstylus 来编写样式,并使用相应的扩展名(.scss.less.styl)把它们指定到 @Component.styleUrls 元数据中。例子如下:

代码语言:javascript
复制
content_copy@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
...

CLI 的构建过程会运行相关的预处理器。

当使用 ng generate component 命令生成组件文件时,CLI 会默认生成一个空白的 CSS 样式文件(.css)。 你可以配置 CLI,让它默认使用你喜欢的 CSS 预处理器,参见 CLI 官方文档 中的解释。

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

控制视图的封装模式:原生 (Native)、仿真 (Emulated) 和无 (None)

像上面讨论过的一样,组件的 CSS 样式被封装进了自己的视图中,而不会影响到应用程序的其它部分。

通过在组件的元数据上设置视图封装模式,你可以分别控制每个组件的封装模式。 可选的封装模式一共有如下几种:

  • ShadowDom 模式使用浏览器原生的 Shadow DOM 实现(参见 MDN上的 Shadow DOM)来为组件的宿主元素附加一个 Shadow DOM。组件的视图被附加到这个 Shadow DOM 中,组件的样式也被包含在这个 Shadow DOM 中。(译注:不进不出,没有样式能进来,组件样式出不去。)
  • Native 视图包装模式使用浏览器原生 Shadow DOM 的一个废弃实现 —— 参见变化详情
  • Emulated 模式(默认值)通过预处理(并改名)CSS 代码来模拟 Shadow DOM 的行为,以达到把 CSS 样式局限在组件视图中的目的。 更多信息,见附录 1 。(译注:只进不出,全局样式能进来,组件样式出不去)
  • None 意味着 Angular 不使用视图封装。 Angular 会把 CSS 添加到全局样式中。而不会应用上前面讨论过的那些作用域规则、隔离和保护等。 从本质上来说,这跟把组件的样式直接放进 HTML 是一样的。(译注:能进能出。)

通过组件元数据中的 encapsulation 属性来设置组件封装模式:

src/app/quest-summary.component.ts

代码语言:javascript
复制
content_copy// warning: few browsers support shadow DOM encapsulation at this time
encapsulation: ViewEncapsulation.Native

ShadowDom 模式只适用于提供了原生 Shadow DOM 支持的浏览器(参见 Can I use 上的 Shadow DOM v1 部分)。 它仍然受到很多限制,这就是为什么仿真 (Emulated) 模式是默认选项,并建议将其用于大多数情况。

查看仿真 (Emulated) 模式下生成的 CSS

当使用默认的仿真模式时,Angular 会对组件的所有样式进行预处理,让它们模仿出标准的 Shadow CSS 作用域规则。

在启用了仿真模式的 Angular 应用的 DOM 树中,每个 DOM 元素都被加上了一些额外的属性。

代码语言:javascript
复制
content_copy<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 的 <head> 中一样:

代码语言:javascript
复制
content_copy[_nghost-pmm-5] {
  display: block;
  border: 1px solid black;
}

h3[_ngcontent-pmm-6] {
  background-color: white;
  border: 1px solid #777;
}

这些就是那些样式被处理后的结果,每个选择器都被增加了 _nghost_ngcontent 属性选择器。 这些额外的选择器实现了本文所描述的这些作用域规则。

扫码关注腾讯云开发者

领取腾讯云代金券

http://www.vxiaotou.com