因爲React推崇組件模式,所以會要求HTML、CSS和JavaScript混合在一塊兒,雖然這與過去的關注點分離正好相反,可是更有利於組件之間的隔離。React已將HTML用JSX封裝,而對CSS只進行了較弱的封裝,僅僅給出了基本的樣式設置。不過,好在第三方庫提供了CSS in JS的解決方案,讓開發者能更高效的書寫組件的樣式,促進CSS工程化的發展。css
在React中的元素都包含style屬性,用來定義內聯樣式。style的屬性值是一個對象而不是一段字符串,該對象的屬性就是CSS屬性,但屬性名要用小駝峯的方式命名,例如line-height改爲lineHeight,具體以下所示。html
class Btn extends React.Component { render() { let btnStyle = { height: 40, lineHeight: 1.5, WebkitBorderRadius: "10%" }; return <button style={btnStyle}>提交</button>; } }
在CSS中,有些屬性是須要單位的,例如代碼中的height,若是沒有寫明,那麼React會自動給它們的數值加上像素(px)單位。還有一點要注意,屬性名不會自動補全瀏覽器前綴,除了IE的ms前綴是純小寫以外,其它前綴的首字母都得是大寫,例如Moz、O和Webkit。前端
元素的className屬性可以設置CSS類,它的值是一段字符串,以下代碼所示,爲<button>元素添加了一個名爲btn的CSS類。web
class Btn extends React.Component { render() { return <button className="btn">提交</button>; } }
若是要動態處理(增、刪或改)元素的CSS類,那麼藉助第三方的classnames庫就能大大下降開發複雜度。下面有兩個示例,完成了相同的功能,都會根據組件的active狀態(布爾值)決定是否添加一個名爲active的CSS類。編程
class Btn1 extends React.Component { render() { let className = "btn"; if (this.state.active) className += " active"; return <button className={className}>提交</button>; } } class Btn2 extends React.Component { render() { let btnClass = classNames({ btn: true, active: this.state.active }); return <button className={btnClass}>提交</button>; } }
Btn1組件經過條件語句和字符串拼接實現了CSS類的添加,而Btn2組件的實現方式更爲優雅和清晰,只須要調用classNames()函數就行。該函數能接收一個由CSS類組成的對象,其中屬性名就是類名,屬性值是布爾類型的,當爲true時,就添加該屬性,不然忽略。瀏覽器
因爲前端的職能和項目規模正在擴大,所以工程化是大勢所趨,而CSS的先天缺陷卻在阻礙這一發展進程,目前碰到的主要問題以下所列。緩存
(1)全局做用域,任意一個CSS規則都對整個頁面有效,當多個CSS規則發生衝突(即樣式污染)時,會根據計算出的特殊性再決定採用哪一個CSS規則。雖然能夠經過OOCSS、BEM等規範避免多人協做時的代碼衝突,但畢竟是自選方案,須要依靠團隊的執行力度,沒法在語言或工具級別強制實施。app
(2)缺乏編程特性,沒有變量、循環或函數等編程語言所需的語法,使得樣式表有不少冗餘代碼。雖然社區發展的CSS預處理器(Sass、Less等)和CSS後處理器(PostCSS)有效緩解了這種情況,可是沒法共享CSS處理器和JavaScript中的變量,不能知足某些特定複雜的場景。編程語言
(3)沒有依賴管理,在引入一個組件時,應該只加載與之關聯的CSS,而不是像如今這樣將整個CSS文件中的樣式都導入進來。目前,市面上已經出現了好多用JavaScript管理CSS依賴的第三方庫,例如styled-components、css-loader等。 模塊化
1)React的應對
在將CSS應用到React中時,爲了能有效的規避上述問題,引入了CSS in JS的編程思想,即用JavaScript編寫CSS,相關的解決方案有40多種之多,可謂百家爭鳴,這其中又分爲拋棄CSS和沿用CSS兩種。
第一節所講解的style屬性(內聯樣式)就完全拋棄了CSS,徹底用JavaScript控制CSS,不只CSS屬性的命名方式不一樣,並且還沒法使用僞類、僞元素、媒體查詢等CSS特性。
而CSS Modules與前者不一樣,依然採用JavaScript和CSS分離的寫法,不只沿用現有的CSS生態和JavaScript模塊化的能力,還支持CSS預處理器的語法,而且可以零成本對接遺留項目。經過將選擇器編譯成惟一的CSS類名後,就能實現CSS模塊化。不過,因爲採用了BEM(Block Element Modifier)命名規範,所以獲得的CSS類名將會比較複雜,而且沒有充分利用JavaScript來控制樣式,例如沒有打通JavaScript和CSS中的變量。
其實這兩種解決方案只有在適合的場景中,才能最大限度的發揮它的做用,真正意義上的銀彈並不存在,本節接下來要介紹一個介於這二者之間的第三方庫:styled-components。
2)標籤模板
styled-components的實現基於ES6新增的標籤模板,移除了元素和樣式之間的映射,經過JavaScript徹底控制了CSS,不只支持全部的CSS特性(例如僞類、媒體查詢、動畫等),還能自動添加瀏覽器前綴,但不支持Less、Sass等CSS預處理器的語法。下面的示例建立了一個帶樣式的Content組件,之因此定義在render()方法以外,是爲了不緩存失效,提高渲染速度。
const Content = styled.button` color: red; appearance: none; `; class Btn extends React.Component { render() { return <Content>提交</Content>; } }
在將Btn組件掛載到DOM中後,會渲染出一個包含CSS類的<button>元素,下面是生成的HTML代碼。
<style> .iCQFTl { color: red; -webkit-appearance: none; } </style> <button class="iCQFTl">提交</button>
由styled-components爲該按鈕生成了一個名稱惟一的CSS類iCQFTl,而且被內嵌到了頁面中。注意,appearance是一個實驗中的CSS屬性,用於設置元素的默認樣式,在不一樣瀏覽器中會被styled-components添加不一樣的前綴,例如上面CSS規則中的-webkit。
3)樣式繼承
經過構造函數styled()可以繼承指定組件的樣式,例如能夠用下面的方式繼承上例的Content組件,並額外聲明背景顏色。
const BgContent = styled(Content)`
background: yellow;
`;
4)屬性傳遞
經過組件的props可以調整其自身的樣式,以下代碼所示,佔位符內是一個箭頭函數,其參數就是傳遞進來的props。
const AttrContent = styled.button` background: ${props => props.active ? "blue" : "white"} `; class Btn extends React.Component { render() { return <AttrContent active>提交</AttrContent>; } }
5)選擇器嵌套
styled-components使用了一個輕量級的CSS預處理器:stylis,用JavaScript實現了選擇器的嵌套,以下代碼所示,其中&符號表示父級選擇器。
const ProContent = styled.button` &:hover { color: yellow; } `;
限於篇幅緣由,本節只列出了styled-components的幾個基礎功能,其餘諸如主題、附加屬性、Refs、動畫等功能能夠參考官方文檔。
styled-components開闢了一種新的控制CSS的方式,不但保持了原生CSS的寫法,並且還忽略了CSS和HTML元素之間的關聯,讓React組件更簡單。