Vue: scoped 樣式與 CSS Module 對比

發表於2018-11-14

譯者按:本文講解的主要是 Scoped 樣式和 CSS Module 的對比,對這兩個概念已經熟悉的同學,同樣也建議看一下,因為文中還提到了一些如 CSS Modules 的 “:export”概念,及 Scoped 樣式存在一些缺陷,如果你對這些細節也已經熟知,那麼請儘快離開這個頁面,以免浪費您時間。

在現代化的 Web 開發中,CSS 還遠未完美,這一點應該沒有什麼意外。現今的專案通常都相當複雜,而 css 樣式天生又是全域性性的,所以到最後總是極容易地就發生樣式衝突——要麼是樣式相互覆蓋,要麼就是隱式地級聯到了下面那些我們未考慮到的元素。

在減輕 CSS 存在的主要痛點方面,我們普遍採用的解決方案是引入 BEM (Block Element Modifier) 方法學。不過這隻能解決我們這個大問題的很小一部分。

我們非常幸運,社群已經開發出了一些解決方案,他們可以幫我們處理這些問題。說不定你已經聽說過了 CSS ModulesStyled ComponentsGlamorousJSS——這些只是眾多流行的工具中的少數幾個。如果你對這個話題感興趣,你可以檢視這篇帖文——作者 Indrek Lasn 對 CSS-in-JS 的思想做了非常詳盡的講解。

每個通過 vue-cli 建立的 Vue.js 應用都內建了兩個很好的解決方案:Scoped CSSCSS Modules (模組式 CSS)。兩種方案各有優缺點,所以下面我們就仔細看下哪種方案在你的案例中更適用。

Scoped 樣式

我們只需要在 <style> 標籤上新增一個 scoped 屬性即可啟用 scoped 樣式:

這樣就會使得我們的樣式只被應用到這個元件中的元素上。這是藉助 PostCSS 實現的,它會將上面的程式碼轉換成下面這樣:

就像你看到的這樣,整個過程不需要做什麼就可以達到很好的 scoped 樣式效果。

現在假設你需要調整一個檢視中的某個元件的寬度,那麼你可以像你平時那樣做的一樣:在這個元件上新增一個額外的 class 來設定其樣式。

經轉換後:

 

這次還是一樣,不需要做什麼你就獲得了對佈局的徹底控制。

不過請注意:這個特性存在一個缺陷,即如果你子元件的根元素上有一個類已經在這個父元件中定義過了,那麼這個父元件的樣式就會洩露到子元件中。如果想更好地理解這個問題,可以檢視這個 CodeSandbox 例子。

還有一些情況是我們需要對我們的子元件的深層結構設定樣式——雖然這種做法並不受推薦且應該避免。為了簡便起見,我們假設我們的父元件現在要對 BasePanel 的標題設定樣式,在 scoped 樣式中,這種情況可以使用 >>> 連線符(或者 /deep/ )實現。

經轉換後:

非常簡單,是吧?可是別忘記,我們卻因此失去了元件的封裝效果。這個元件內的所有的 .title 類的樣式都會被這些樣式所浸染——即便是孫節點。

模組式 CSS

模組式 CSS 的流行源於 React 社群,它獲得了社群的迅速的採用。Vue.js 更甚之,其強大、簡便的特性在加上通過 vue-cli 對其開箱即用的支援,將其發展到另一個高度。

現在讓我們來看下怎麼使用它:

這次我們使用的不是 scoped 屬性,而是 module。這等於告訴 vue-template-compiler 和 vue-cli 的 webpack 配置要對這一部分採用哪些相應的 loader,進而生成像下面這樣的 CSS:

它的特殊之處以及和 scoped 樣式不一樣的地方就在於所有建立的類可以通過這個元件的 $style 物件獲取。因此,要將這個類進行應用,我們需要像下面這樣進行 class 繫結:

這段程式碼將生成下面的 HTML 及相關的樣式:

 

它的第一點好處就是,當我們在 HMTL 中檢視這個元素時我們可以立刻知道它所屬的是哪個元件;第二點好處是,一切都變成顯式的了,我們擁有了徹底的控制權——不會再有什麼奇怪的現象了。和 scoped 樣式那種把普通的標籤也加上那些 data 屬性的做法不一樣,這些普通標籤在轉換後還是最初的樣子。

比較 scoped 樣式中的第二個例子,我們來看下我們可以怎麼對那個元件設定樣式:

其轉換後:

毫無意外,跟我們期望的結果一樣。此外,因為所有的 CSS 類可以通過 $style 物件獲取到,所以我們可以通過 props 將這些類傳遞到任何我們希望的深度中,這樣,在子元件中的任意位置使用這些類就會變得極其容易:

模組式 CSS 與 JS 有著很好的互操作性 (interoperability),這一點不只侷限於 CSS 類。我們還可以使用 :export 關鍵字將其他的東西匯出到 $style 物件上。

例如,想象一下你有一個圖表需要開發 —— 你可以在 CSS 中定義你的色彩變數的同時將其匯出,以供你的元件使用:

對於模組式 CSS的概念,我這裡還只是講到了它的皮毛,它實際要寬泛的多,建議你檢視下它完整的規範以瞭解更多。

總結

 

其實兩種方案都非常簡單、易用,在某種程度上解決的是同樣的問題。 那麼你該選擇哪種呢?

scoped 樣式的使用不需要額外的知識,給人舒適的感覺。它所存在的侷限,也正是它的使用簡單的原因。它可以用於支援小型到中型的應用。

在更大的應用或更復雜的場景中,這個時候,對於 CSS 的運用,我們就會希望它更加顯式,擁有更多的控制權。雖然在模板中大量使用 $style 看起來並不那麼“性感”,但卻更加安全和靈活,為此我們只需付出微小的代價。還有一個好處就是我們可以用 JS 獲取到我們定義的一些變數(如色彩值、樣式斷點),這樣我們就無需手動保持其在多個檔案中同步。

相關文章