前置
在 react 中解決組件樣式衝突的方案中,若是您喜歡將 css 與 js 分離,可能更習慣於 CSS-Modules;若是習慣了 Vue.js 那樣的單文件組件,可能習慣於使用 styled-components 來解決這個問題。使用 CSS-Modules 從老項目遷移過來可能更容易。css
安裝
npm i styled-components
基本用法
import React from 'react' import { render } from 'react-dom' import styled from 'styled-components' const items = [ { title: 'title1', type: 'primary', desc: 'Lorem ipsum dolor sit amet consectetur ' }, { title: 'title2', type: 'other', desc: 'Lorem ipsum dolor sit amet consectetur ', }, ] function App() { return ( <div> {items.map(renderItem)} </div> ) } function renderItem(item) { return ( <Wrap> <h2>{item.title}</h2> <p>{item.desc}</p> </Wrap> ) } const Wrap = styled.div` margin: 10px auto 10px; padding: 10px; width: 90%; border-radius: 5px; background: #eee ` render(<App />, document.getElementById('app'))
實際渲染結果:react
咱們須要在 JavaScript 模板字符串內部書寫 css 樣式,爲了獲得 css 語法高亮,能夠使用 vscode 擴展。npm
嵌套
const Wrap = styled.div` margin: 10px auto; padding: 10px; width: 90%; border-radius: 5px; background: #eee; + h2 { + color: red + } `
props
function renderItem(item) { return ( + <Wrap type={item.type} > <h2>{item.title}</h2> <p>{item.desc}</p> </Wrap> ) } const Wrap = styled.div` margin: 10px auto; padding: 10px; width: 90%; border-radius: 5px; + background: ${props => props.type === 'primary' ? '#202234' : '#eee'}; `
在模板括號中可以使用任意 JavaScript 表達式,這裏使用箭頭函數,經過 props 接收參數。app
繼承
使用繼承實現上文功能:dom
import React from 'react' import { render } from 'react-dom' import styled from 'styled-components' const items = [...] function App() { return (...) } function renderItem(item) { + const Container = item.type === 'primary' ? primaryContainer : OrdinaryContainer return ( + <Container> <h2>{item.title}</h2> <p>{item.desc}</p> </Container> ) } const Wrap = styled.div` margin: 10px auto 10px; padding: 10px; width: 90%; border-radius: 5px; - background: ${props => props.type === 'primary' ? '#202234' : '#eee'}; ` + const OrdinaryContainer = styled(Wrap)` + background: #eee; + ` + const primaryContainer = styled(Wrap)` + background: #202234; + ` render(<App />, document.getElementById('app'))
咱們獲得一樣的效果:ide
繼承的語法函數
// 如上所示,您應該這樣寫來實現繼承 const OrdinaryContainer = styled(Wrap)`` // 如今已經不支持extend關鍵字 const OrdinaryContainer = Wrap.extend``
下面給它們共同繼承的 Wrap 添加一個border:spa
const Wrap = styled.div` margin: 10px auto 10px; padding: 10px; width: 90%; border-radius: 5px; + border: 2px solid red; `
將影響全部繼承過 Wrap 的樣式變量:3d
attrs
封裝一個文本輸入框組件 src/components/Input.jsxcode
import styled from "styled-components"; const Input = styled.input.attrs({ type: 'text', padding: props => props.size || "0.5em", margin: props => props.size || "0.5em" })` border: 2px solid #eee; color: #555; border-radius: 4px; margin: ${props=>props.margin}; padding: ${props=>props.padding}; ` export default Input
使用
// .. import Input from './components/Input' function App() { return ( <div> <Input></Input> <Input size="2em"></Input> </div> ) } render(<App />, document.getElementById('app'))
createGlobalStyle
在 V4 版本已經將 injectGlobal
移除,使用 createGlobalStyle
代替它設置全局樣式,
import React from 'react' import { render } from 'react-dom' import styled, {createGlobalStyle} from 'styled-components' // ... function App() { return ( <div> <GlobalStyle></GlobalStyle> {items.map(renderItem)} </div> ) } function renderItem(item) { const Container = item.type === 'primary' ? primaryContainer : OrdinaryContainer return ( <Container> <h2>{item.title}</h2> <p>{item.desc}</p> </Container> ) } const GlobalStyle = createGlobalStyle` *{ margin: 0; padding: 0; } body { min-height: 100%; background: #ffb3cc; } ` // ... render(<App />, document.getElementById('app'))
ThemeProvider
經過上下文 API 將主題注入組件樹中位於其下方任何位置的全部樣式組件中。
import React from 'react' import { render } from 'react-dom' import styled, {createGlobalStyle, ThemeProvider} from 'styled-components' import Input from './components/Input' const items = [...] function App() { return ( <div> <GlobalStyle></GlobalStyle> {items.map(renderItem)} <Input></Input> <Input size="2em"></Input> </div> ) } function renderItem(item) { const Container = item.type === 'primary' ? primaryContainer : OrdinaryContainer return ( <Container> <h2>{item.title}</h2> <p>{item.desc}</p> </Container> ) } // ... const primaryContainer = styled(Wrap)` background: ${props=>props.theme.primary}; ` const theme = { primary: '#202234' } render(<ThemeProvider theme={theme}><App /></ThemeProvider>, document.getElementById('app'))
咱們須要先導入 ThemeProvider
,而後用標籤將 <App />
包裹,須要注意的是必須爲 ThemeProvider
標籤提供一個 theme
屬性,接下來在全部子組件中均可以經過 props.theme.xxx
獲取 theme
下的屬性。咱們將 Input 組件的背景色一樣改成 theme
下的 primary
:
import styled from "styled-components"; const Input = styled.input.attrs({ // ... })` // ... background: ${props=>props.theme.primary}; ` export default Input
keyframes
使用 styled-components 時,沒法直接在模板字符串中建立 keyframes
,須要先導入 styled-components 下的 keframes
對象來建立它。下面看一個簡單的實例:
import React from 'react' import styled, { keyframes } from 'styled-components' import { render } from 'react-dom' const items = [...] function App() { return ( <div> {items.map(renderItem)} </div> ) } function renderItem(item) { const Container = item.type === 'primary' ? primaryContainer : OrdinaryContainer return ( <Container> <h2>{item.title}</h2> <p>{item.desc}</p> </Container> ) } // ... const fadeIn = keyframes` 0% { opacity: 0; } 100% { opacity: 1; } ` const Wrap = styled.div` // ... animation: 1.5s ${fadeIn} ease-out; ` // .. render(<App />, document.getElementById('app'))
從新刷新頁面,就能看見下面的效果: