Angular 组件样式

Angular 2+ (Now 5.0) 应用程序使用的是常规CSS样式,意思就是说CSS标准中的所有东西都可以直接在Angular应用程序中直接使用。

一、如何使用组件样式?

在编写Angular2组件时,不光只是定义HTML模板,同时也需要利用CSS对HTML模板进行美化。最简单的是在组件元数据里定义 styles 属性,其是一个含CSS代码的字符串数组,比如:

@Component({
  selector: 'hero-app',
  template: `
    <h1>Tour of Heroes</h1>
    <hero-app-main [hero]=hero></hero-app-main>`,
  styles: ['h1 { color: red; }'],
  directives: [HeroAppMainComponent]
})

组件样式不同于传统和全局style(即 <style> 元素)两种方式。

首先,h1{} 它仅限里当前 HeroAppComponent 组件有效,换言之,除组件外的其他任何 h1元素都不会受影响。

在组件模块化里,这种方式的CSS比传统CSS更加有优势:

  1. 支持CSS类名和选择器,且限当前组件上下文有意义。
  2. CSS类名和选择器不会与应用程序中其他类和选择器相冲突。
  3. 应用程序的其他地方无法修改组件样式。
  4. 组件的CSS代码、组件类、HTML代码可以放在同一文件里。
  5. 随时可以更改或删除组件的CSS代码,不必担心可能在别的地方已经被使用,只关心组件自身。

其实以上说来说去无非就是,组件样式的作用域限定非常窄,仅限组件自身使用而已。

二、特殊选择器

注意:本小节需要对影子DOM(Shadow DOM)有一定的了解,具体见【引用】章节。

组件样式有几个特殊的选择器是来自影子DOM(Shadow DOM)作用域的。

1、宿主元素(:host)

使用 :host 伪类选择器应用到组件的宿主元素里。换言之,:host 的使用只对目标元素有效。

:host {
  display: block;
  border: 1px solid black;
}

这是唯一一种对目标宿主元素设置样式的方式,组件内的其他选择器是无法应用它,因为它不是组件自己模板的一部分。并且是在父组件的模板中有效。当然除此之外,我们还可以针对个别的宿主元素设置样式,比如以下只针对含有 .active 有效:

:host(.active) {
  border-width: 3px;
}

2、宿主祖先元素(:host-context)

有时需要通过外部条件来改变宿主元素的样式,例如,可能需要根据 <body> 元素设置的CSS主题类,然后再其基础做对我们组件进行修改。用法和 :host() 类似,使用 :host-context() 伪类,括号里面可以指定根选择器。比如下面示例是针对带 theme-light 类下所有 h2 元素有效。

:host-context(.theme-light) h2 {
  background-color: #eef;
}

3、宿主元素样式的穿透(/deep/)

如果你了解影子DOM(Shadow DOM)的话,那么其中影子边界是个非常重要的概念。前面我们已经了解一点宿主元素与组件样式的作用域不同点。然而有时候我们需要对组件样式和其子组件样式同时设置样式时,这就需要 /deep/ 来穿透影子边界了。

比如下面示例是针对所有 <h3> 元素有效,包括组件与子组件下所有 <h3> 元素都有效:

:host /deep/ h3 {
  font-style: italic;
}

/deep/ 选择器还有另外一个别名 >>>

三、组件样式加载类型

有三种方式可以对组件添加样式:

  • HTML模板里
  • 通过元数据设置 stylestyleUrls 属性
  • 使用import加载CSS

1、元数据

上面示例都是通过元数据的 style 属性里定义样式。

@Component({
  selector: 'hero-app',
  template: `
    <h1>Tour of Heroes</h1>
    <hero-app-main [hero]=hero></hero-app-main>`,
  styles: ['h1 { font-weight: normal; }'],
  directives: [HeroAppMainComponent]
})

styles 属性是一个数组类型,每个项最终都会独立生成一个 <style></style> 来包裹其样式内容,通常我们只需要一个项即可。

2、模板内定义style标签

我们可以在模板里嵌入一个 <style> 标签,这和传统没有什么区别:

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

3、元数据设置外部样式

设置组件元数据的 styleUrls 属性,它是指定一个外部样式的URL地址。

@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'],
  directives: [HeroTeamComponent]
})

URL地址必须是相对于应用程序根目录开始

4、模板内定义link标签

我们可以在模板里嵌入一个 <link> 标签,与 styleUrls 效果相同。

@Component({
  selector: 'hero-team',
  template: `
    <link rel="stylesheet" href="app/hero-team.component.css">
    <h3>Team</h3>
    <ul>
      <li *ngFor="#member of hero.team">
        {{member}}
      </li>
    </ul>`
})

5、@imports方法

我们也可以标准CSS的 @import

@import 'hero-details-box.css';

与前面两者URL导入方式不同,其相对路径是组件所在的位置。

四、组件样式封装类型:原生、仿真、不封装

我们可以通过组件元数据来控制对样式的封装类型,Angular2有三种样式封装模式:

  • 原生

    视图封装是使用浏览器原生的影子DOM(Shadow DOM),这是Web组件新提出一种方法,目前浏览器的支持情况还不是很好。

  • 仿真

    视图封装是采用影子DOM(Shadow DOM)的写法,但Angular2最终会生成传统的CSS选择器来解决浏览器不支持影子DOM(Shadow DOM)的情况。

  • 不封装

    意思就是说Angular不会对视图进行任何封装。可以在组件元数据里 encapsulation 属性,重新定义我们的视图封装类型。比如设置为原生视图封装:

import { Component, ViewEncapsulation } from 'angular2/core';

@Component({
    encapsulation: ViewEncapsulation.Native
})

五、仿真视图封装生成的CSS

当使用Angular2默认仿真视图封装时,Angular2会预处理所有组件样式。在影子DOM里有个影子边界的概念,它是保证元素样式不受其它样式影响,这也是Web组件里最核心的功能之一。所以,在仿真视图封装时就需要对这些样式进行重命名

当我们透过浏览器的元素检查看到的都是这样的代码:

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

生成的代码有两种属性风格:

  • 一个影子DOM的宿主元素会生成一个 _nghost 属性。
  • 一个影子DOM的影子元素会生成一个 _ngcontent 属性。

这些属性的确切值并不重要,是由Angular2自动生成,并确保唯一性。最终生成把结果放在一个新创建 <style> 标签里。

[_nghost-pmm-5] {
  display: block;
  border: 1px solid black;
}

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

引用

  1. Shadow DOM
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容