前置
在 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'))
从新刷新页面,就能看见下面的效果: