關於Angular樣式封裝

引導

這是一個很簡單的話題,可是你很難在搜索到一篇比較完整的介紹它的文章,或者說單純的告訴你 ViewEncapsulation 的用法而已,這在實際項目中遠遠不夠的。css

1、封裝模式

分別爲:html

  • Native 原先瀏覽器Shadow DOM行爲。typescript

  • Emulated 仿真模式,經過Angular來模擬相似Shadow DOM的行爲。bootstrap

  • None 無任何封裝行爲。瀏覽器

以上三種模式惟一的區別在於Shadow DOM,固然其做用是讓組件的樣式只進不出,換言之即組件內的樣式不會影響到外部組件。有關於Shadow DOM更多的細節不在這裏討論。antd

三者的表現形式app

假定使用如下代碼:工具

@Component({
  template: `<h1>test</h1>`,
  styles: [`h1 { color: #f50; }`],
  encapsulation: ViewEncapsulation.Native
})

在不一樣模式下產生的HTML&CSS風格都不盡相同,瞭解這些不同尤其重要。它們分別爲:spa

Native:code

#shadow-root (open)
    <style>h1 { color: #f50; }</style>
    <h1>test</h1>

Emulated:

<style>h1[_ngcontent-c0] { color: #f50; }</style>
<h1 _ngcontent-c0>test</h1>

None:

<style>h1 { color: #f50; }</style>
<h1>test</h1>

Native & None 在內容是同樣的,但其後者會影響至其餘外部組件的 h1 元素。

2、組件樣式

組件樣式的封裝模式取決於咱們對 encapsulation 的配置,例如上面的示例。固然你能夠了在 main.ts 時爲全部組件統一設定一種行的模式,例如:

platformBrowserDynamic().bootstrapModule(AppModule, {
  defaultEncapsulation: ViewEncapsulation.None
})

雖然三種模式都有不一樣的風格,但對於一個組件而言,若是沒有一很合理的使用風格在實際項目中會讓咱們很頭疼,特別是當項目中使用第三方組件庫(例如:ngx-bootstrap、ng-zorro-antd、material2 等)時,有時很容易受組件庫的影響抑或須要讓組件庫與業務組件樣式作一些微調時,瞭解一些細節很是重要。

例如一個用於渲染頁面標頭名曰:app-header 組件,其中 <nz-breadcrumb> 麪包屑 默認狀況下它會對最後一項進行加粗,但假設這不是咱們但願的,而應該是一個不加粗和其餘項同樣的文本:

<nz-breadcrumb>
  <nz-breadcrumb-item>Home</nz-breadcrumb-item>
  <nz-breadcrumb-item>Detail</nz-breadcrumb-item>
</nz-breadcrumb>

最終生成的HTML是這樣子:

<nz-breadcrumb _ngcontent-c1="" class="ant-breadcrumb">
    <nz-breadcrumb-item _ngcontent-c1="">
    <span class="ant-breadcrumb-link">
      Home
    </span>
    <span class="ant-breadcrumb-separator">/</span></nz-breadcrumb-item>
    <nz-breadcrumb-item _ngcontent-c1="">
    <span class="ant-breadcrumb-link">
      Detail
    </span>
    <span class="ant-breadcrumb-separator">/</span></nz-breadcrumb-item>
  </nz-breadcrumb>

假若你不假思索的在 app-header 組件的 styles 屬性中加上:

.ant-breadcrumb-link {
    font-weight: normal;
}

正如你指望的那樣,可能不必定會有你想要的結果,亦或的結果可能會存在隱患。前面我說過三種模式惟一的區別在於Shadow DOM,所以說白了是兩種不一樣的結果。

若組件設定爲 None 模式,而會生效,但只要 app-header 組件出現過一次在將來全部即便再也不使用 app-header 組件的狀況下全部的麪包屑的最後一項都是是不加粗的,這即是我說的隱患。

反之,對於 Shadow 行爲,它會爲 nz-breadcrumb 建立一個額外的屬性 _ngcontent-c1標識(不論是 NativeEmulated 本質是同樣的)所設定的樣式僅限於 app-header 組件當中。而 Angular 中即採用 :host 來表示組件自身,因此前面的CSS樣式應該變成這樣:

:host .ant-breadcrumb-link {
    font-weight: normal;
}

最後生成的樣式會變成這樣:

[_nghost-c1] .ant-breadcrumb-link[_ngcontent-c1] {
  font-weight: normal;
}

我認爲咱們沒有必要去理解生成的標識符是怎麼樣,只須要知道 :host 表示組件自身。

然而咱們會發現,對於第三方組件 nz-breadcrumb 組件而言,.ant-breadcrumb-link 是其組件內部某個HTML元素的 class 而已,且它有本身的一套組件封裝規則。但咱們生成的CSS中包括了一個奇怪的字符 [_ngcontent-c1],最終致使 app-header 組件樣式沒法改變第三方組件 nz-breadcrumb 組件內容的樣式。

這是很合理的,個人領地不可侵犯,Angular 組件自己便是 Web Component 標準的具體實現。

難道咱們沒有辦法侵犯第三方組件了嗎?好在 Angular 提供了一種對將來工具更好兼容性的命令 ::ng-deep 來強制樣式容許侵入子組件。

:host ::ng-deep .ant-breadcrumb-link {
  font-weight: normal;
}

生成的CSS會是這樣:

[_nghost-c1] .ant-breadcrumb-link {
  font-weight: normal;
}

最終這個不加粗的效果只會在 app-header 組件內部有效。

總結

熟悉 :host::ng-deep 組合用法對組件樣式的構建很關鍵,Angular 組件有本身的業務邏輯、樣式、HTML模板它們是構建一個 Web Component 的基礎技術核心。

但願此篇能幫助你們更好理解組件樣式。

Happy coding!

相關文章
相關標籤/搜索