前言
為了應對越來越復雜的web應用,組件化應運而生,React、Vue等組件化框架使我們的程序更簡單更加可維護。然而,即使有了組件化前端開發者還是需要使用CSS編寫樣式,CSS很簡單,但CSS是相當有限的,沒有變量、循環和函數等邏輯讓CSS用起來十分混亂。LESS和SASS等語言的特性使樣式編寫有所好轉,但還是阻止不了CSS的混亂狀態。
最近,為了解決CSS混亂問題,人們開始考慮使用JS上編寫CSS, styled components 就是其中一種解決方案。styled components是一個React第三方庫,作用是可以將樣式寫成組件的形式,實現在JS上編寫CSS。
基本用法
安裝
npm install -S styled-components
使用styled-components不需要再使用className屬性來控制樣式,而是將樣式寫成更具語義化的組件的形式,如下例:
import React from 'react'; import styled from 'styled-components'; import { render } from 'react-dom'; const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; `; class App extends React.Component { render() { return ( <Title>Hello world</Title> ) } } render( <App />, document.getElementById('app') );
結果:
上例中的styled.h1是一個標簽模板函數,緊跟其後的是一個模板字符串參數,“標簽模板”和“模板字符串”都是es6的語法,具體可以參考阮一峰老師ECMASCRIPT6入門中的 模板字符串 和 標簽模板
styled.h1函數返回一個React Component,styled components會為這個React Component添加一個class,該class的值為一個隨機字符串。傳給styled.h1的模板字符串參數的值實際上是CSS語法,這些CSS會附加到該React Component的class中,從而為React Component添加樣式:
更簡單地控制樣式 —— props參數
在React中是通過控制className和style來控制樣式的,如下例,Title組件需要判斷是否有primary屬性來判斷是否渲染”title-primary”類的樣式:
class Title extends React.Component { render() { const className = `title${this.props.primary ? ' title-primary' : ''}`; return ( <div className={className}>{this.props.children}</div> ); } }
<Title primary>Hello world</Title>
styled components使用props來控制樣式,將控制樣式代碼放在樣式組件中,使React組件更加簡潔:
const Title = styled.h1` font-size: 1.5em; text-align: center; color: ${props => props.primary ? 'SteelBlue' : 'palevioletred'}; `;
<Title primary>Hello world</Title>
上面兩種寫法非常相似,實際上還是要用CSS的語法編寫樣式。但是第一種寫法需要className或style作為“中間人”使JS找到對應的樣式,而styled components的寫法中樣式組件暴露props讓外層JS來控制樣式,不再需要className或style這樣的“中間人”, 移除了樣式和組件間的映射關系 。
支持全部CSS特性
實際上styled components使用的還是CSS,因此完美 支持CSS的所有特性 。偽類,媒體查詢,keyframes都可以在styled components中實現:
const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; &:hover { background-color: #ddd; } @media (max-width: 650px) { font-size: 1em; } `;
上例中,鼠標放在“Hello world”的時候會變成灰色,窗口寬度少於650px的時候文字大小會變成1em。
CSS作用域
CSS有一個痛點——CSS的作用域是全局的。當兩個CSS選擇器有沖突時,會根據選擇器的權值確定使用哪一個選擇選擇器。當項目較大時,編寫的css選擇器很難判斷會不會把另外一個選擇器沖掉。
解決CSS作用域的其中一個方法就是使用 後代選擇器 ,這種用法和命名空間相似,即在每個選擇器前添加一個父元素的選擇器,從而減少選擇器沖突的概率:
#myParent .title { /*...*/ } #myParent .title-primary { /*...*/ }
LESS和SASS的持選擇器嵌套的語法,該語法最終會編譯成後代選擇器的形式,和上述方法實際上是一樣的:
#myParent { .title { /*...*/ } .title-primary { /*...*/ } }
使用嵌套語法能較好的解決CSS全局作用域的問題,但是,由於編譯出來的CSS選擇器過於具體,一個組件組件難以復用另一個組件的當時,你的組件樣式變得孤立起來。
上文有提到,使用styled components會給生成的React組件添加一個值為隨機字符串的className。使用同一個styled components生成的多個React組件的className是不同的,這種隨機className的機制使得 組件之間的className值不會沖突 ,從而解決了CSS全局作用域的問題。
styled components也支持css的嵌套語法:
const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; `; const Wrapper = styled.div` font-size: 1.2em; .content1 { color: red; } .content2 { color: green; } `;
<Wrapper> <div>You'd better don't do that like so:</div> <div className="content1">I am red</div> <div className="content2">I am green</div> </Wrapper>
不推薦在styled components中使用嵌套,因為樣式組件的子組件的className不會被編譯成隨機值,這些子組件的類還是會受CSS的全局作用域影響。
分離邏輯組件和展示組件
使用styled components,可將組件分為邏輯組件和展示組件 ,邏輯組件只關註邏輯相關的部分,展示組件只關註樣式 。通過解耦成兩種組件,可以使代碼變得更加清晰可維護。
當邏輯有變化,如後臺拉取的數據的格式有所變化時,只需關註並修改邏輯組件上的代碼,展示組件的代碼不用動。而當UI需要變化時,只需改變展示組件上的代碼,並保證展示組件暴露的props接口不變即可。邏輯組件和展示組件各司其職,修改代碼時錯誤發生率也會有所減少。
CSS Module vs Styled Components
CSS Module是另一種解決全局作用域的方案,還沒了解CSS Module的朋友可以參考 CSS Modules 入門及 React 中實踐 和 CSS Modules 用法教程 這兩篇博文。
CSS Module只需修改構建代碼和使用模塊依賴引入className的方式即可使用,且支持less和sass的語法,樣式代碼不用修改即可讓使用CSS語法的舊項目零成本接入。而styled components是一種全新的樣式組件化的編程方式,不支持less和sass語法。
CSS Module還是JS和CSS分離的寫法,而styled components實際上是在JS上寫CSS。習慣JS、CSS和HTML都分離的人或許不習慣styled components的寫法。
最後
styled components一種全新的控制樣式的編程方式,它能解決CSS全局作用域的問題,而且移除了樣式和組件間的映射關系。傳統的web開發推崇HTML、CSS、Javascript都分離,styled components則有點“離經叛道”的味道——在JS上編寫CSS,什麽東西都在JS裏寫感覺是近幾年前端開發的趨勢,React就是一個活生生的例子。究竟這是不是一個好趨勢,現在還不好判斷,但是實踐和時間總會給我們一個答案。
Tags:
文章來源: