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