摘要:通過使用或者從中導入文件使得模塊被編譯成為可能。模塊一次只運行一個文件,這樣可以避免全局上下文的污染。下面讓我們用重構這些這些代碼這是一個進化版,但是我們僅僅只達到了一部分目標。
原文:http://glenmaddern.com/articl...
譯者:@公子
如果你想知道 CSS 最近發展的轉折點,你應該選擇去觀看 Christopher Chedeau 在2014年11月的 NationJS 大會上做的名稱為 CSS in JS 的分享。不得不說這是一個技術分水嶺的時刻,一群不同的思維在自己的方向上就像粒子進入了高能漩渦中一樣飛速發展。其中,在 React 或者是 React 相關的項目中編寫 CSS 樣式,React Style, jxstyle 和 Radium 這三個算是最新的,最好的以及最具有可行性的方法。如果說“發明”是探索 最接近的可能 的一個實例(譯者注:最接近的可能是 Steven Johnson 于 2010 年提出來的一個概念),那么 Christopher 則是讓許許多多的可能變得更加接近(譯者注:上面三個工具中的兩個靈感都是來自他的分享)。
上圖列出的這些都是在許多大型 CSS 代碼庫中存在的問題。Christopher 指出,只要將你的樣式通過用 JS 去管理,這些問題都能很好的解決。不得不說這的確是有道理的,但是這種方法有它的復雜性并會帶來其他的相關問題。其實只要看看瀏覽器是如何處理 :hover 偽類狀態的,我們就會發現有些東西在 CSS 中其實很早就解決了。
CSS 模塊小組 覺得我們可以更加合理的解決問題:我們可以繼續保持 CSS 現在的樣子,并在 styles-in-JS 社區的基礎上建立更合理的改進。雖然我們已經找到了解決辦法同時又捍衛了 CSS 原始的美,但我們仍然欠那些把我們推向這個結果的那些人一聲感謝。謝謝你們,朋友們!
下面讓我來向大家解說一下什么是 CSS 模塊并且為什么它才是未來吧。
第一步:像局部一樣無需考慮全局沖突在 CSS 模塊中,每一個文件被編譯成獨立的文件。這樣我們就只需要使用通用簡單的類選擇器名字就好了。我們不需要擔心它會污染全局的變量。下面我就我們編寫一個擁有四個狀態的按鈕來說明這個功能。
https://jsfiddle.net/pqnot81c...
CSS 模塊之前我們是怎么做的我們也許會使用 Suit/BEM 命名規范去進行樣式命名,這樣我們的 HTML 和 CSS 代碼看起來就像如下所示:
/* components/submit-button.css */ .Button { /* all styles for Normal */ } .Button--disabled { /* overrides for Disabled */ } .Button--error { /* overrides for Error */ } .Button--in-progress { /* overrides for In Progress */
這樣寫看起來還挺棒的。使用 BEM 命令方式使我們有了 4 個樣式變量這樣我們不必使用嵌套選擇器。使用Button這種首字母大寫的方法可以很好的避免與之前的代碼或者是其他的依賴代碼進行沖突。另外我們使用了--語法這樣能很清楚的顯示出我們的依賴 Class。
總的來說,這樣做可以讓我們的代碼更易于維護,但是它需要我們在命名規范的學習上付出很多努力。不過這已經是目前 CSS 能給出的最好的解決辦法了。
CSS 模塊出來之后我們是怎么做的CSS 模塊意味著你從此再也不必為你的名字太大眾而擔心,只要使用你覺得有最有意義的名字就好了:
/* components/submit-button.css */ .normal { /* all styles for Normal */ } .disabled { /* all styles for Disabled */ } .error { /* all styles for Error */ } .inProgress { /* all styles for In Progress */
請注意我們這里并沒有在任何地方使用 button 這個詞。不過反過來想,為什么我們一定要使用它呢?這個文件已經被命名成了 submit-button.css 了呀!既然在其它的語言中你不需要為你的局部變量增加前綴,沒道理 CSS 需要加上這個蹩腳的功能。
通過使用 require 或者 import 從 JS 中導入文件使得 CSS 模塊被編譯成為可能。
/* components/submit-button.js */ import styles from "./submit-button.css"; buttonElem.outerHTML = ``
你不必擔心大眾名字會倒置命名沖突,編譯后實際上類名是會自動生成并保證是唯一的。CSS 模塊為你做好一切,最終編譯成一個 CSS 與 JS 交互的 ICSS 后綴文件(閱讀這里了解更多)。因此,你的程序最終看起來可能會是這個樣子的:
如果你的類名變的和上面的例子差不多的話,那么恭喜你你成功了!
命名約定現在回過頭來仔細看看我們的示例代碼:
/* components/submit-button.css */ .normal { /* all styles for Normal */ } .disabled { /* all styles for Disabled */ } .error { /* all styles for Error */ } .inProgress { /* all styles for In Progress */
請注意所有的類都是相互獨立的,這里并不存在一個“ 基類 ”然后其它的類集成并“ 覆蓋 ”它的屬性這種情況。在 CSS 模塊中 每一個類都必須包含這個元素需要的所有樣式 (稍后會有詳細說明)。這使得你在 JS 中使用樣式的時候有很大的不同:
/* Don"t do this */ `class=${[styles.normal, styles["in-progress"]].join(" ")}` /* Using a single name makes a big difference */ `class=${styles["in-progress"]}` /* camelCase makes it even better */ `class=${styles.inProgress}`
當然,如果你的工資是按照字符串長度來計算的話,你愛怎么做就怎么做吧!
React 示例CSS 模塊并不是 React 特有的功能,但是不得不說在 React 中使用 CSS 模塊會更爽。基于這個理由,我覺得我有必要展示下面這個如飄柔般絲滑的示例:
/* components/submit-button.jsx */ import { Component } from "react"; import styles from "./submit-button.css"; export default class SubmitButton extends Component { render() { let className, text = "Submit" if (this.props.store.submissionInProgress) { className = styles.inProgress text = "Processing..." } else if (this.props.store.errorOccurred) { className = styles.error } else if (!this.props.form.valid) { className = styles.disabled } else { className = styles.normal } return } }
你完全不需要擔心你的類命名會和全局的樣式表命名沖突,這樣能讓你的注意力更集中在組件上,而不是樣式。我敢保證,使用過一次之后,你會再也不想回到原來的模式中去。
然而這僅僅是一切的開始。CSS 模塊化是你的基本,但也是時候來思考一下如何把你的樣式們都集中到一塊了。
第二步:組件就是一切上文中我提到了每一個類必須包含按鈕不同狀態下的所有的樣式,與 BEM 命名方式上相比,代碼上可能區別如下:
/* BEM Style */ innerHTML = `
那么問題來了,你怎么在所有的狀態樣式中共享你的樣式呢?這個答案就是 CSS 模塊的強力武器 - 組件:
.common { /* all the common styles you want */ } .normal { composes: common; /* anything that only applies to Normal */ } .disabled { composes: common; /* anything that only applies to Disabled */ } .error { composes: common; /* anything that only applies to Error */ } .inProgress { composes: common; /* anything that only applies to In Progress */ }
composes這個關鍵詞將會使.normal類將.common內的所有樣式包含進來,這個有點像 Sass 的 @extends 語法。但是 Sass 依賴重寫你的 CSS 文件達到效果,而 CSS 模塊最后會通過 JS 編譯導出,不需要修改文件(譯者注:下面會有例子詳細說明)。
Sass按照 BEM 的命名規范,我用 Sass 的 @extends 寫的話可能會像如下的代碼:
.Button--common { /* font-sizes, padding, border-radius */ } .Button--normal { @extends .Button--common; /* blue color, light blue background */ } .Button--error { @extends .Button--common; /* red color, light red background */ }
編譯后的 CSS 文件如下:
.Button--common, .Button--normal, .Button--error { /* font-sizes, padding, border-radius */ } .Button--normal { /* blue color, light blue background */ } .Button--error { /* red color, light red background */ }
你可以只需要一個類來標記你的元素
composes 語法看起來很像 @extends 但是他們的工作方式是不同的。為了演示一下,讓我們來看一個例子:
.common { /* font-sizes, padding, border-radius */ } .normal { composes: common; /* blue color, light blue background */ } .error { composes: common; /* red color, light red background */ }
編譯后的文件可能是像如下一樣:
.components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ } .components_submit_button__normal__def6547 { /* blue color, light blue background */ } .components_submit_button__error__1638bcd { /* red color, light red background */ }
JS 代碼中通過 import styles from "./submit-button.css" 最終會返回:
styles: { common: "components_submit_button__common__abc5436", normal: "components_submit_button__common__abc5436 components_submit_button__normal__def6547", error: "components_submit_button__common__abc5436 components_submit_button__error__1638bcd" }
所以我們依然可以使用 style.normal 或者 style.error 在我們的代碼中,仍舊會有多個類樣式渲染在我們的 DOM 上。
Submit
這就是 composes 語法的厲害之處,你可以在不重寫你的 CSS 的情況下對你的元素混合使用不同類的樣式。
第三步:文件間共享Sass 或者 LESS 中,你可以在每個文件中使用 @import 在全局工作區間內共享樣式。這樣你就可以在一個文件中定義變量或者函數(mixins)并在你的其它組件文件中共享使用。這樣做是有好處的,但是很快你的變量命名就會與其它的變量名稱相沖突(雖然它在另外一個全部空間下),你不可避免的會重構你的 variables.scss 或者 settings.scss,最后你就會發現你已經看不懂到底哪個組件依賴哪個變量了。最后的最后你會發現你的配置文件變量名稱冗余到變得非常不實用。
針對上述問題仍然是有更好的解決辦法的(事實上 Ben Smithett 的文章 Sass 和 Wepack 的混合使用 給了 CSS 模塊話很大的啟發,我推薦大家去讀一讀這篇文章),但是不管怎么做你還是局限在了 Sass 的全局環境下。
CSS 模塊一次只運行一個文件,這樣可以避免全局上下文的污染。而且像 JS 使用 import 或者 require 來加載依賴一樣,CSS 模塊使用 compose 來從另一個文件中加載:
/* colors.css */ .primary { color: #720; } .secondary { color: #777; } /* other helper classes... */
/* submit-button.css */ .common { /* font-sizes, padding, border-radius */ } .normal { composes: common; composes: primary from "../shared/colors.css"; }
使用組件,我們能夠深入到每一個像colors.css一樣的基礎樣式表中,并隨意重命名它。又因為組件只是改變了最后導出的類名稱,而不是 CSS 文件本身,composes 語句在瀏覽器解析之前就會被刪除。
/* colors.css */ .shared_colors__primary__fca929 { color: #720; } .shared_colors__secondary__acf292 { color: #777; }
/* submit-button.css */ .components_submit_button__common__abc5436 { /* font-sizes, padding, border-radius */ } .components_submit_button__normal__def6547 {}
Submit
事實上,當它被瀏覽器解析之后,我們的局部是不存在一個"normal"樣式的。這是一件好事!這意味著我們可以增加一個局部名字有意義的對象(可能就叫"normal")而不用在 CSS 文件中新增代碼。我們使用的越多,對我們的網站會造成更少的視覺誤差以及在用戶瀏覽器上更少的不一致。
題外話:空的類樣式可以使用 csso 這樣的工具來檢查去除掉。
第四步:功能單一模塊組件是非常強大的,因為它確實的讓你描述了一個元素是什么,而不是它由那些樣式組成。這是一種不同的方式去描述概念示例(元素)到樣式實體(樣式規則)之間的映射關系。讓我們看一個簡單的 CSS 例子:
.some_element { font-size: 1.5rem; color: rgba(0,0,0,0); padding: 0.5rem; box-shadow: 0 0 4px -2px; }
這些元素,樣式都是特別簡單的。然而也存在著問題:顏色,字體大小,盒子陰影,內邊距-這里的一切都是量身定制的,這讓我們想要在其它地方復用這些樣式的時候變得有些困難。下面讓我們用 Sass 重構這些這些代碼:
$large-font-size: 1.5rem; $dark-text: rgba(0,0,0,0); $padding-normal: 0.5rem; @mixin subtle-shadow { box-shadow: 0 0 4px -2px; } .some_element { @include subtle-shadow; font-size: $large-font-size; color: $dark-text; padding: $padding-normal; }
這是一個進化版,但是我們僅僅只達到了一部分目標。事實上 $large-font-size 和 $padding-normal 只是在名字上表示了它的用途,并不能在任何地方都執行。像 box-shadow 這種定義沒辦法賦值給變量,我們不得不使用 mixin 或者 @extends 語法來配合。
CSS 模塊通過使用組件,我們可以使用我們可復用的部分定義我們的組件:
.element { composes: large from "./typography.css"; composes: dark-text from "./colors.css"; composes: padding-all-medium from "./layout.css"; composes: subtle-shadow from "./effect.css"; }
這種寫法勢必會有很多單一功能文件產生,然而通過使用文件系統來管理不同用途的樣式比起用命名空間來說要好的多。如果你想要從一個文件中導入多個類樣式的話,有一種簡單的寫法:
/* this short hand: */ .element { composes: padding-large margin-small from "./layout.css"; } /* is equivalent to: */ .element { composes: padding-large from "./layout.css"; composes: margin-small from "./layout.css"; }
這開辟了一種可能,使用極細粒的類樣式定義一些樣式別名去給每一個網站使用:
.article { composes: flex vertical centered from "./layout.css"; } .masthead { composes: serif bold 48pt centered from "./typography.css"; composes: paragraph-margin-below from "./layout.css"; } .body { composes: max720 paragraph-margin-below from "layout.css"; composes: sans light paragraph-line-height from "./typography.css"; }
我對這種技術非常感興趣,我覺得,它混合了像 Tachyons 類似的 CSS 原子技術,像 Semantic UI 那樣變量分離可讀的好處(譯者注:就是說 CSS 模塊的命名簡單易懂,組件復用方便)。
但是 CSS 模塊化之路僅僅是剛剛開始,未來我們希望大家能嘗試更多為它寫出譜寫出新的篇章。
翻滾吧!CSS 模塊!我們希望 CSS 模塊化能有助于你和你的團隊在你們現有的 CSS 和產品的基礎上維護代碼,讓它變得更舒適更高效。我們已經接近可能的把額外的語法減少到最少,并盡量確保語法和現有的變化不大。我們有 Webpack, JSPM 和 Browserify 的示例代碼,如果你們使用它們中的其中之一,你們可以參考一二。當然我們也在為 CSS 模塊話的使用尋找新的環境:服務端 NodeJS 的支持正在進行,Rails 的支持已經提上議程準備進行了。
但是為了讓它變得更簡單,我在 Plunker 上制作了一個預覽示例,你不用安裝任何東西就可以運行它:
這里是 CSS 模塊的 Github 倉庫地址,如果你有任何問題,請開 issue 提出。CSS 模塊團隊還很小,我們還沒有看到一些有用的例子,歡迎你們給我們投稿。
最后的最后,祝大家開心寫樣式,幸福每一天!文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85910.html
摘要:但是最終,我們會為模塊化帶來的好處而開心模塊將作用域限制于組件中,從而避免了全局作用域的問題。但這是因為模塊將樣式和組件相綁定,從而不會發生全局樣式的沖突。先從版本的模塊化開始。我認為模塊化背后的思想是正確的。 原文鏈接: https://www.sitepoint.com/und... 在瞬息萬變的前端開發世界中,很難找到一個真正有意義的概念,并且將其清晰明了的向廣大人民群眾普及。 ...
摘要:比如以為例,一個組件,包括一個文件一個文件圖片組件在中便可如下加載使用導出為對象的模塊化其實,還有另外一種思路,就是將內置中,成為的一部分。 CSS 模塊化 CSS(Cascading Style Sheets),從誕生之初就決定了它無法編程,甚至連解釋性語言都算不上,只能作為一種簡單的層疊樣式表,對 HTML 元素進行格式化。 但隨著前端的發展,前端項目已經變得越來越龐大和復雜,社區...
摘要:比如以為例,一個組件,包括一個文件一個文件圖片組件在中便可如下加載使用導出為對象的模塊化其實,還有另外一種思路,就是將內置中,成為的一部分。 CSS 模塊化 CSS(Cascading Style Sheets),從誕生之初就決定了它無法編程,甚至連解釋性語言都算不上,只能作為一種簡單的層疊樣式表,對 HTML 元素進行格式化。 但隨著前端的發展,前端項目已經變得越來越龐大和復雜,社區...
閱讀 2294·2021-09-22 15:27
閱讀 3166·2021-09-03 10:32
閱讀 3491·2021-09-01 11:38
閱讀 2493·2019-08-30 15:56
閱讀 2206·2019-08-30 13:01
閱讀 1531·2019-08-29 12:13
閱讀 1410·2019-08-26 13:33
閱讀 885·2019-08-26 13:30