Angular 應(yīng)用使用標準的 CSS 來設(shè)置樣式。這意味著你可以把關(guān)于 CSS 的那些知識和技能直接用于 Angular 程序中,例如:樣式表、選擇器、規(guī)則以及媒體查詢等。
另外,Angular 還能把組件樣式捆綁在組件上,以實現(xiàn)比標準樣式表更加模塊化的設(shè)計。
對你編寫的每個 Angular 組件來說,除了定義 HTML 模板之外,還要定義用于模板的 CSS 樣式、 指定任意的選擇器、規(guī)則和媒體查詢。
實現(xiàn)方式之一,是在組件的元數(shù)據(jù)中設(shè)置 styles
屬性。 styles
屬性可以接受一個包含 CSS 代碼的字符串?dāng)?shù)組。 通常你只給它一個字符串就行了,如同下例:
Path:"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 {
/* . . . */
}
它們既不會被模板中嵌入的組件繼承,也不會被通過內(nèi)容投影(如 ng-content
)嵌進來的組件繼承。
在這個例子中,h1
的樣式只對 HeroAppComponent
生效,既不會作用于內(nèi)嵌的 HeroMainComponent
,也不會作用于應(yīng)用中其它任何地方的 <h1>
標簽。
這種范圍限制就是所謂的樣式模塊化特性
注:
- 在@Component
的元數(shù)據(jù)中指定的樣式只會對該組件的模板生效。
組件樣式中有一些從影子(Shadow) DOM 樣式范圍領(lǐng)域引入的特殊選擇器:
使用 :host
偽類選擇器,用來選擇組件宿主元素中的元素(相對于組件模板內(nèi)部的元素)。
Path:"src/app/hero-details.component.css" 。
:host {
display: block;
border: 1px solid black;
}
:host
選擇是是把宿主元素作為目標的唯一方式。除此之外,你將沒辦法指定它, 因為宿主不是組件自身模板的一部分,而是父組件模板的一部分。
要把宿主樣式作為條件,就要像函數(shù)一樣把其它選擇器放在 :host
后面的括號中。
下一個例子再次把宿主元素作為目標,但是只有當(dāng)它同時帶有 active CSS 類的時候才會生效。
Path:"src/app/hero-details.component.css" 。
:host(.active) {
border-width: 3px;
}
有時候,基于某些來自組件視圖外部的條件應(yīng)用樣式是很有用的。 例如,在文檔的 <body>
元素上可能有一個用于表示樣式主題 (theme) 的 CSS 類,你應(yīng)當(dāng)基于它來決定組件的樣式。
這時可以使用 :host-context()
偽類選擇器。它也以類似 :host()
形式使用。它在當(dāng)前組件宿主元素的祖先節(jié)點中查找 CSS 類, 直到文檔的根節(jié)點為止。在與其它選擇器組合使用時,它非常有用。
在下面的例子中,只有當(dāng)某個祖先元素有 CSS 類 theme-light
時,才會把 background-color
樣式應(yīng)用到組件內(nèi)部的所有 <h2>
元素中。
Path:"src/app/hero-details.component.css" 。
:host-context(.theme-light) h2 {
background-color: #eef;
}
組件樣式通常只會作用于組件自身的 HTML 上。
把偽類 ::ng-deep
應(yīng)用到任何一條 CSS 規(guī)則上就會完全禁止對那條規(guī)則的視圖包裝。任何帶有 ::ng-deep
的樣式都會變成全局樣式。為了把指定的樣式限定在當(dāng)前組件及其下級組件中,請確保在 ::ng-deep
之前帶上 :host
選擇器。如果 ::ng-deep
組合器在 :host
偽類之外使用,該樣式就會污染其它組件。
這個例子以所有的 <h3>
元素為目標,從宿主元素到當(dāng)前元素再到 DOM 中的所有子元素:
Path:"src/app/hero-details.component.css" 。
:host /deep/ h3 {
font-style: italic;
}
/deep/
組合器還有兩個別名:>>>
和 ::ng-deep
。
注:
-/deep/
和>>>
選擇器只能被用在仿真 (emulated) 模式下。 這種方式是默認值,也是用得最多的方式。
- CSS 標準中用于 "刺穿 Shadow DOM" 的組合器已經(jīng)被廢棄,并將這個特性從主流瀏覽器和工具中移除。 因此,Angular 也將會移除對它們的支持(包括
/deep/
、&&&
和::ng-deep
)。 目前,建議先統(tǒng)一使用::ng-deep
,以便兼容將來的工具。
有幾種方式把樣式加入組件:
styles
或 styleUrls
元數(shù)據(jù)上述作用域規(guī)則對所有這些加載模式都適用。
你可以給 @Component
裝飾器添加一個 styles
數(shù)組型屬性。
這個數(shù)組中的每一個字符串(通常也只有一個)定義一份 CSS。
Path:"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 {
/* . . . */
}
注:
- 這些樣式只對當(dāng)前組件生效。 它們既不會作用于模板中嵌入的任何組件,也不會作用于投影進來的組件(如ng-content
)。
當(dāng)使用 --inline-styles
標識創(chuàng)建組件時,Angular CLI 的 ng generate component
命令就會定義一個空的 styles
數(shù)組。
ng generate component hero-app --inline-style
你可以通過把外部 CSS 文件添加到 @Component
的 styleUrls
屬性中來加載外部樣式。
@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 {
/* . . . */
}
h1 {
font-weight: normal;
}
注:
- 這些樣式只對當(dāng)前組件生效。 它們既不會作用于模板中嵌入的任何組件,也不會作用于投影進來的組件(如ng-content
)。
- 你可以指定多個樣式文件,甚至可以組合使用
style
和styleUrls
方式。
當(dāng)你使用 Angular CLI 的 ng generate component
命令但不帶 --inline-style
標志時,CLI 會為你創(chuàng)建一個空白的樣式表文件,并且在所生成組件的 styleUrls
中引用該文件。
ng generate component hero-app
你也可以直接在組件的 HTML 模板中寫 <style>
標簽來內(nèi)嵌 CSS 樣式。
Path:"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 (click)="activate()">Activate</button>
`
})
你也可以在組件的 HTML 模板中寫 <link>
標簽。
Path:"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>`
})
注:
- 當(dāng)使用 CLI 進行構(gòu)建時,要確保這個鏈接到的樣式表文件被復(fù)制到了服務(wù)器上。
- 只要引用過,CLI 就會計入這個樣式表,無論這個 link 標簽的 href 指向的 URL 是相對于應(yīng)用根目錄的還是相對于組件文件的。
你還可以利用標準的 CSS @import 規(guī)則來把其它 CSS 文件導(dǎo)入到 CSS 文件中。
在這種情況下,URL 是相對于你正在導(dǎo)入的 CSS 文件的。
Path:"src/app/hero-details.component.css (excerpt)" 。
/* The AOT compiler needs the `./` to show that this is local */
@import './hero-details-box.css';
當(dāng)使用 CLI 進行構(gòu)建時,你必須配置 "angular.json" 文件,使其包含所有外部資源(包括外部的樣式表文件)。
在它的 styles 區(qū)注冊這些全局樣式文件,默認情況下,它會有一個預(yù)先配置的全局 "styles.css" 文件。
如果使用 CLI 進行構(gòu)建,那么你可以用 sass
、less
或 stylus
來編寫樣式,并使用相應(yīng)的擴展名(.scss
、.less
、.styl
)把它們指定到 @Component.styleUrls
元數(shù)據(jù)中。例子如下:
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
...
CLI 的構(gòu)建過程會運行相關(guān)的預(yù)處理器。
當(dāng)使用 ng generate component
命令生成組件文件時,CLI 會默認生成一個空白的 CSS 樣式文件(.css
)。 你可以配置 CLI,讓它默認使用你喜歡的 CSS 預(yù)處理器。
注:
- 添加到@Component.styles
數(shù)組中的字符串必須寫成 CSS,因為 CLI 沒法對這些內(nèi)聯(lián)的樣式使用任何 CSS 預(yù)處理器。
像上面討論過的一樣,組件的 CSS 樣式被封裝進了自己的視圖中,而不會影響到應(yīng)用程序的其它部分。
通過在組件的元數(shù)據(jù)上設(shè)置視圖封裝模式,你可以分別控制每個組件的封裝模式。 可選的封裝模式一共有如下幾種:
ShadowDom
模式使用瀏覽器原生的 Shadow DOM 實現(xiàn)(參見 MDN 上的 Shadow DOM)來為組件的宿主元素附加一個 Shadow DOM。組件的視圖被附加到這個 Shadow DOM 中,組件的樣式也被包含在這個 Shadow DOM 中。(譯注:不進不出,沒有樣式能進來,組件樣式出不去。)Native
視圖包裝模式使用瀏覽器原生 Shadow DOM 的一個廢棄實現(xiàn) —— 參見變化詳情。Emulated
模式(默認值)通過預(yù)處理(并改名)CSS 代碼來模擬 Shadow DOM 的行為,以達到把 CSS 樣式局限在組件視圖中的目的。 更多信息,見附錄 1。(譯注:只進不出,全局樣式能進來,組件樣式出不去)None
意味著 Angular 不使用視圖封裝。 Angular 會把 CSS 添加到全局樣式中。而不會應(yīng)用上前面討論過的那些作用域規(guī)則、隔離和保護等。 從本質(zhì)上來說,這跟把組件的樣式直接放進 HTML 是一樣的。(譯注:能進能出。)
通過組件元數(shù)據(jù)中的 encapsulation
屬性來設(shè)置組件封裝模式:
Path:"src/app/quest-summary.component.ts" 。
// warning: few browsers support shadow DOM encapsulation at this time
encapsulation: ViewEncapsulation.Native
ShadowDom 模式只適用于提供了原生 Shadow DOM 支持的瀏覽器(參見 Can I use 上的 Shadow DOM v1 部分)。 它仍然受到很多限制,這就是為什么仿真 (Emulated) 模式是默認選項,并建議將其用于大多數(shù)情況。
當(dāng)使用默認的仿真模式時,Angular 會對組件的所有樣式進行預(yù)處理,讓它們模仿出標準的 Shadow CSS 作用域規(guī)則。
在啟用了仿真模式的 Angular 應(yīng)用的 DOM 樹中,每個 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 的 <head>
中一樣:
[_nghost-pmm-5] {
display: block;
border: 1px solid black;
}
h3[_ngcontent-pmm-6] {
background-color: white;
border: 1px solid #777;
}
這些就是那些樣式被處理后的結(jié)果,每個選擇器都被增加了 _nghost
或 _ngcontent
屬性選擇器。 這些額外的選擇器實現(xiàn)了本文所描述的這些作用域規(guī)則。
更多建議: