styled-components 是我們思考如何增強 CSS 以用於樣式化 React 組件系統的結果。 通過專注於單一用例,我們設法為開發人員以及最終用戶的輸出優化了體驗。
除了改善開發人員的體驗外,styled-components 還提供
您可以在撰寫您熟悉和喜愛的 CSS 的同時獲得所有這些好處,只需將其綁定到個別組件即可。
安裝 styled-components 只需要一個指令,您就可以開始使用了
# with npm
npm install styled-components
# with yarn
yarn add styled-components
如果您使用像 yarn 這樣支援 "resolutions" package.json 欄位的套件管理器,我們也強烈建議您在其中加入一個對應主要版本範圍的項目。這有助於避免因專案中安裝了多個版本的 styled-components 而產生的所有問題。
在 package.json
中
{ "resolutions": { "styled-components": "^5" } }
強烈建議(但非強制)也使用 Babel 插件。它提供了許多好處,例如更清晰的類別名稱、伺服器端渲染相容性、更小的捆綁包等等。
如果您沒有使用模組打包器或套件管理器,我們也在 unpkg CDN 上託管了一個全域("UMD")建置版本。只需將以下 <script>
標籤新增到 HTML 檔案的底部
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
新增 styled-components
後,您將可以存取全域 window.styled
變數。
const Component = window.styled.div` color: red; `
這種使用方法需要頁面上也包含 react CDN 捆綁包 和 react-is
CDN 捆綁包(在 styled-components 腳本之前)。
styled-components
利用標記模板字面值來設定組件的樣式。
它消除了組件和樣式之間的映射。這意味著當您定義樣式時,實際上是在建立一個普通的 React 組件,並且您的樣式附加到該組件上。
此範例建立了兩個簡單的組件,一個包裝器和一個標題,並附加了一些樣式
Hello World!
這是一個線上編輯器,您可以修改程式碼來體驗使用 styled-components 的感覺!
CSS 規則會自動添加供應商前綴,styled-components 會為您處理這一切!
Styled components 使用 stylis.js 套件來為 css 規則添加前綴。
有關 stylis.js 中支援的前綴的其他資訊,請造訪他們的 儲存庫。
您可以將函式(「插值」)傳遞給 styled component 的模板字面值,以根據其 props 調整樣式。
此按鈕組件具有一個可以更改其顏色的 primary 狀態。當將 $primary
prop 設定為 true 時,我們將交換其背景和文字顏色。
您可能經常想要使用一個組件,但針對單一情況略微更改它。現在,您可以傳入一個插值函式並根據某些 props 更改它們,但覆蓋樣式一次需要付出很大的努力。
要輕鬆建立一個繼承另一個組件樣式的新組件,只需將其包裝在 styled()
建構函式中即可。在這裡,我們使用上一節中的按鈕並建立一個特殊的按鈕,使用一些與顏色相關的樣式來擴展它
我們可以看到新的 TomatoButton
仍然與 Button
相似,而我們只添加了兩個新規則。
在某些情況下,您可能想要更改 styled component 渲染的標籤或組件。這在構建導覽列時很常見,例如,其中混合了錨點連結和按鈕,但它們的樣式應該相同。
針對這種情況,我們有一個逃生艙口。您可以使用 "as" 多態 prop
來動態交換接收您撰寫樣式的元素
這也適用於自定義組件!
styled
方法完美適用於您自己的或任何第三方組件,只要它們將傳遞的 className
prop 附加到 DOM 元素即可。
如果您正在使用 react-native
,請記住使用 style
而不是 className
。
您也可以將標籤名稱傳遞到 styled()
工廠呼叫中,例如:styled("div")
。事實上,styled.tagname
輔助函式只是執行相同操作的別名。
如果 styled 的目標是一個簡單的元素(例如 styled.div
),styled-components 會將任何已知的 HTML 屬性傳遞給 DOM。如果它是一個自定義 React 組件(例如 styled(MyComponent)
),styled-components 會傳遞所有 props。
此範例顯示如何將 Input 組件的所有 props 傳遞到已掛載的 DOM 節點,就像 React 元素一樣。
請注意 $inputColor
prop 沒有傳遞給 DOM,但 type
和 defaultValue
有傳遞?styled
函式足夠聰明,可以自動為您過濾掉非標準屬性。
如果您熟悉將 CSS 導入組件(例如像 CSSModules),您應該習慣這樣做
import React from 'react'; import styles from './styles.css'; export default class Counter extends React.Component { state = { count: 0 }; increment = () => this.setState({ count: this.state.count + 1 }); decrement = () => this.setState({ count: this.state.count - 1 }); render() { return ( <div className={styles.counter}> <p className={styles.paragraph}>{this.state.count}</p> <button className={styles.button} onClick={this.increment}> + </button> <button className={styles.button} onClick={this.decrement}> - </button> </div> ); } }
因為樣式化組件是元素和樣式規則的組合,我們會像這樣編寫 Counter
import React from 'react'; import styled from 'styled-components'; const StyledCounter = styled.div` /* ... */ `; const Paragraph = styled.p` /* ... */ `; const Button = styled.button` /* ... */ `; export default class Counter extends React.Component { state = { count: 0 }; increment = () => this.setState({ count: this.state.count + 1 }); decrement = () => this.setState({ count: this.state.count - 1 }); render() { return ( <StyledCounter> <Paragraph>{this.state.count}</Paragraph> <Button onClick={this.increment}>+</Button> <Button onClick={this.decrement}>-</Button> </StyledCounter> ); } }
請注意,我們在 StyledCounter
前面添加了「Styled」前綴,以便 React 組件 Counter
和樣式化組件 StyledCounter
不會名稱衝突,但在 React 開發者工具和網頁檢查器中仍然易於識別。
將樣式化組件定義在 render 方法之外非常重要,否則它會在每次渲染過程中重新創建。在 render 方法中定義樣式化組件會阻礙緩存並大幅降低渲染速度,應避免這種做法。
以推薦的方式編寫您的樣式化組件
const StyledWrapper = styled.div` /* ... */ `; const Wrapper = ({ message }) => { return <StyledWrapper>{message}</StyledWrapper>; };
不要這樣寫
const Wrapper = ({ message }) => { // WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!! const StyledWrapper = styled.div` /* ... */ `; return <StyledWrapper>{message}</StyledWrapper>; };
推薦閱讀:Talia Marcassa 在 樣式化組件:使用或不使用? 中撰寫了一篇關於實際使用的精彩評論,其中包含許多可靠的實務見解和與替代方案的比較。
我們使用的預處理器 stylis 支援類似 scss 的語法,可自動嵌套樣式。
通過此預處理,樣式化組件支援一些進階選擇器模式
&
單個「&」符號指的是組件的所有實例;它用於應用廣泛的覆蓋
Hello world!How ya doing?The sun is shining...Pretty nice day today.Don't you think?Splendid.
&&
雙「&」符號指的是組件的一個實例;如果您正在執行條件樣式覆蓋,並且不希望樣式應用於特定組件的所有實例,這將非常有用
&&
單獨的雙「&」符號具有一種稱為「優先級提升」的特殊行為;如果您正在處理混合樣式化組件和原生 CSS 的環境,其中可能存在樣式衝突,這將非常有用
I'm blue, da ba dee da ba daa
如果您在沒有「&」符號的情況下放入選擇器,它們將引用組件的子項。
為了避免不必要的包裝器僅將某些屬性傳遞給渲染的組件或元素,您可以使用 .attrs
建構函式。它允許您將額外的屬性附加到組件。
例如,您可以將靜態屬性附加到元素,或將第三方屬性(如 activeClassName
)傳遞給 React Router 的 Link 組件。此外,您還可以將更多動態屬性附加到組件。.attrs
物件也接受函數,這些函數接收組件接收的屬性。返回值也將合併到結果屬性中。
這裡我們渲染一個 Input
組件,並將一些動態和靜態屬性附加到它
如您所見,我們可以在插值中訪問我們新創建的屬性,並且 type
屬性會向下傳遞給元素。
請注意,在包裝樣式化組件時,.attrs
會從最內部的樣式化組件應用到最外層的樣式化組件。
這允許每個包裝器覆蓋嵌套的 .attrs
使用,類似於樣式表中稍後定義的 CSS 屬性如何覆蓋先前的聲明。
Input
的 .attrs
會先套用,然後是 PasswordInput
的 .attrs
這就是為什麼 PasswordInput
的類型是 'password'
,但仍然使用 Input
的 $size
屬性。
使用 @keyframes
的 CSS 動畫不會限定在單個組件的範圍內,但您仍然不希望它們是全局的,以避免名稱衝突。這就是為什麼我們導出一個 keyframes
輔助函數,它將生成一個您可以在整個應用程序中使用的唯一實例
< 💅🏾 >
react-native
不支援 Keyframes。請改用 ReactNative.Animated
API。
Keyframes 在使用時會延遲注入,這就是它們可以進行程式碼分割的方式,因此您必須使用 css
輔助函數 來處理共用的樣式片段
const rotate = keyframes`` // ❌ This will throw an error! const styles = ` animation: ${rotate} 2s linear infinite; ` // ✅ This will work as intended const styles = css` animation: ${rotate} 2s linear infinite; `
這在 v3 及更低版本中可以運作,在這些版本中我們沒有對 keyframes 進行程式碼分割。如果您從 v3 升級,請確保所有共用的樣式片段都使用 css
輔助函數!
樣式化組件可以與 React Native 以相同的方式和相同的導入方法一起使用。使用 Expo 的 Snack 嘗試此範例。
import React from 'react' import styled from 'styled-components/native' const StyledView = styled.View` background-color: papayawhip; ` const StyledText = styled.Text` color: #BF4F74; ` class MyReactNativeComponent extends React.Component { render() { return ( <StyledView> <StyledText>Hello World!</StyledText> </StyledView> ) } }
由於 css-to-react-native
,我們也支援更複雜的樣式(例如 transform
),這通常是一個陣列,以及速記(例如 margin
)!
請注意,flex
屬性與 CSS 速記類似,而不是 React Native 中的舊版 flex
屬性。設定 flex: 1
會將 flexShrink
設定為 1
,此外還會將 flexGrow
設定為 1
,並將 flexBasis
設定為 0
。
想像一下您如何在 React Native 中編寫屬性,猜猜您如何將其轉換為 CSS,您可能是對的
const RotatedBox = styled.View` transform: rotate(90deg); text-shadow-offset: 10px 5px; font-variant: small-caps; margin: 5px 7px 2px; `
與網頁版本的一些差異是,您不能使用 keyframes
和 createGlobalStyle
輔助函數,因為 React Native 不支援 keyframes 或全局樣式。如果您使用媒體查詢或嵌套 CSS,我們也會發出警告。
在 v2 中,我們支援百分比。為了實現這一點,我們需要對所有速記強制執行單位。如果您要遷移到 v2,可以使用程式碼修改工具。
如果您只想導入 styled-components
而不是 styled-components/native
,您可以添加一個包含 "react-native"
的 resolverMainFields
設定。這在 Metro 中預設支援(目前在 Haul 中有效),但在某些時候似乎已被移除。