Styled-Components
它是經由過程JavaScript轉變CSS編寫體式格局的處置懲罰計劃之一,從根本上處置懲罰通例CSS編寫的一些弊病。
經由過程JavaScript來為CSS賦能,我們能到達通例CSS所不好處置懲罰的邏輯龐雜、函數要領、復用、防止滋擾。
只管像SASS、LESS這類預處置懲罰言語增加了許多用用的特徵,然則他們照舊沒有對轉變CSS的雜沓有太大的協助。因而組織事情交給了像 BEM如許的要領,雖然比較有用,然則它完全是自選計劃,不能被強迫運用在言語或許東西層面。
他搭配React能夠將模塊化走向一個更高的高度,款式謄寫將直接依附在JSX上面,HTML、CSS、JS三者再次內聚。
基礎
裝置
npm install --save styled-components
除了npm裝置運用模塊化加載包以外,也支撐UMD
花樣直接加載劇本文件。
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
入門
styled-components
運用標籤模板來對組件舉行款式化。
它移除了組件和款式之間的映照。這意味着,當你定義你的款式時,你實際上製造了一個平常的React組件,你的款式也附在它上面。
這個例子建立了兩個簡樸的組件,一個容器和一個題目,並附加了一些款式。
// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;
// Use Title and Wrapper like any other React component – except they're styled!
render(
<Wrapper>
<Title>
Hello World, this is my first styled component!
</Title>
</Wrapper>
);
注重
CSS劃定規矩會自動增加瀏覽器廠商前綴,我們沒必要斟酌它。
透傳props
styled-components
會透傳一切的props屬性。
// Create an Input component that'll render an <input> tag with some styles
const Input = styled.input`
padding: 0.5em;
margin: 0.5em;
color: palevioletred;
background: papayawhip;
border: none;
border-radius: 3px;
`;
// Render a styled text input with a placeholder of "@mxstbr", and one with a value of "@geelen"
render(
<div>
<Input placeholder="@mxstbr" type="text" />
<Input value="@geelen" type="text" />
</div>
);
基於props做款式推斷
模板標籤的函數插值能拿到款式組件的props,能夠據此調解我們的款式劃定規矩。
const Button = styled.button`
/* Adapt the colours based on primary prop */
background: ${props => props.primary ? 'palevioletred' : 'white'};
color: ${props => props.primary ? 'white' : 'palevioletred'};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
款式化恣意組件
// This could be react-router's Link for example
const Link = ({ className, children }) => (
<a className={className}>
{children}
</a>
)
const StyledLink = styled(Link)`
color: palevioletred;
font-weight: bold;
`;
render(
<div>
<Link>Unstyled, boring Link</Link>
<br />
<StyledLink>Styled, exciting Link</StyledLink>
</div>
);
擴大款式
我們有時刻須要在我們的款式組件上做一點擴大,增加一些分外的款式:
須要注重的是.extend
在對款式組件有用,假如是其他的React組件,須要用styled
款式化一下。
// The Button from the last section without the interpolations
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// We're extending Button with some extra styles
const TomatoButton = Button.extend`
color: tomato;
border-color: tomato;
`;
render(
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);
在少少迥殊情況下,我們能夠須要變動款式組件的標籤範例。我們有一個迥殊的API,withComponent
能夠擴大款式和替代標籤:
const Button = styled.button`
display: inline-block;
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// We're replacing the <button> tag with an <a> tag, but reuse all the same styles
const Link = Button.withComponent('a')
// Use .withComponent together with .extend to both change the tag and use additional styles
const TomatoLink = Link.extend`
color: tomato;
border-color: tomato;
`;
render(
<div>
<Button>Normal Button</Button>
<Link>Normal Link</Link>
<TomatoLink>Tomato Link</TomatoLink>
</div>
);
增加attr
我們能夠運用attrs
API來為款式組件增加一些attr屬性,它們也能夠經由過程標籤模板插值函數拿到props傳值。
const Input = styled.input.attrs({
// we can define static props
type: 'password',
// or we can define dynamic ones
margin: props => props.size || '1em',
padding: props => props.size || '1em'
})`
color: palevioletred;
font-size: 1em;
border: 2px solid palevioletred;
border-radius: 3px;
/* here we use the dynamically computed props */
margin: ${props => props.margin};
padding: ${props => props.padding};
`;
render(
<div>
<Input placeholder="A small text input" size="1em" />
<br />
<Input placeholder="A bigger text input" size="2em" />
</div>
);
動畫
帶有@keyframes的CSS animations,平常來說會發生復用。styled-components
暴露了一個keyframes
的API,我們運用它發生一個能夠復用的變量。如許,我們在謄寫css款式的時刻運用JavaScript的功用,為CSS附能,而且防止了稱號爭執。
// keyframes returns a unique name based on a hash of the contents of the keyframes
const rotate360 = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
// Here we create a component that will rotate everything we pass in over two seconds
const Rotate = styled.div`
display: inline-block;
animation: ${rotate360} 2s linear infinite;
padding: 2rem 1rem;
font-size: 1.2rem;
`;
render(
<Rotate>< 💅 ></Rotate>
);
支撐 React Native
高等特徵
Theming
styled-components
暴露了一個<ThemeProvider>
容器組件,供應了設置默許主題款式的功用,他類似於react-rudux
的頂層組件Provider
,經由過程context
完成了從頂層究竟層一切款式組件的默許主題共用。
const Button = styled.button`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
/* Color the border and text with theme.main */
color: ${props => props.theme.main};
border: 2px solid ${props => props.theme.main};
`;
Button.defaultProps = {
theme: {
main: 'palevioletred'
}
}
// Define what props.theme will look like
const theme = {
main: 'mediumseagreen'
};
render(
<div>
<Button>Normal</Button>
<ThemeProvider theme={theme}>
<Button>Themed</Button>
</ThemeProvider>
</div>
);
Refs
平常我們在給一個非原生款式組件增加ref
屬性的時刻,其指向都是該組件實例的索引,我們經由過程用innerRef
能夠直接拿到內里的DOM
節點。
const AutoFocusInput = styled.input`
background: papayawhip;
border: none;
`;
class Form extends React.Component {
render() {
return (
<AutoFocusInput
placeholder="Hover here..."
innerRef={x => { this.input = x }}
onMouseEnter={() => this.input.focus()}
/>
);
}
}
Security
由於styled-components
許可我們運用恣意輸入作為CSS
屬性值,一旦意想到這一點,我們立時邃曉要對輸入做安全性校驗了,由於運用用戶外部的輸入款式能夠致使用戶的瀏覽器被CSS注入進擊。CSS注入進擊能夠不明顯,然則我們照樣得警惕一點,某些IE瀏覽器版本以至許可在URL聲明中實行恣意的JS。
這個例子通知我們外部的輸入以至能夠在CSS內挪用一個API收集要求。
// Oh no! The user has given us a bad URL!
const userInput = '/api/withdraw-funds';
const ArbitraryComponent = styled.div`
background: url(${userInput});
/* More styles here... */
`;
CSS.escape
這個將來API範例可凈化JS中的CSS的題目。然則瀏覽器兼容性如今還不是太好,所以我們發起在項目中運用polyfill by Mathias Bynens
。
CSS共存
假如我們盤算把styled-components
和現有的css
共存的話,我們須要注重兩個完成的細節題目:
styled-components
也會天生實在的款式表,並經由過程className
屬性鏈接天生的款式表內容。在JS運行時,他會天生一份實在的style節點插進去到document的head內。
注重的一個小地方:
// MyComponent.js
const MyComponent = styled.div`background-color: green;`;
// my-component.css
.red-bg {
background-color: red;
}
// For some reason this component still has a green background,
// even though you're trying to override it with the "red-bg" class!
<MyComponent className="red-bg" />
我們styled-components
天生的style款式表平常是在head頭部的最底下,一致CSS優先級條件下是會掩蓋默許前者css文件的款式的。這個插進去遞次運用webpack來調解是比較難過。所以,我們平常都如許經由過程調解css優先級來轉變顯現:
/* my-component.css */
.red-bg.red-bg {
background-color: red;
}
Media Templates
媒體查詢是開闢相應式web運用不可或缺的存在,這是一個簡樸的例子:
const Content = styled.div`
background: papayawhip;
height: 3em;
width: 3em;
@media (max-width: 700px) {
background: palevioletred;
}
`;
render(
<Content />
);
由於媒體查詢語句很長,而且經常在全部運用程序中重複運用,所以為此建立一些模板來複用是很有必要的。
運用JS的功用特徵,我們能夠輕鬆定義一份可設置的語句,包裝媒體查詢和款式。
const sizes = {
desktop: 992,
tablet: 768,
phone: 376
}
// Iterate through the sizes and create a media template
const media = Object.keys(sizes).reduce((acc, label) => {
acc[label] = (...args) => css`
@media (max-width: ${sizes[label] / 16}em) {
${css(...args)}
}
`
return acc
}, {})
const Content = styled.div`
height: 3em;
width: 3em;
background: papayawhip;
/* Now we have our methods on media and can use them instead of raw queries */
${media.desktop`background: dodgerblue;`}
${media.tablet`background: mediumseagreen;`}
${media.phone`background: palevioletred;`}
`;
render(
<Content />
);
這太cool了,不是嗎?
Tagged Template Literals
標籤模板是ES6的一個新特徵,這是我們styled-components
建立款式組件的體式格局和劃定規矩。
const aVar = 'good';
// These are equivalent:
fn`this is a ${aVar} day`;
fn([ 'this is a ', ' day' ], aVar);
這看起來有點貧苦,然則這意味着我們能夠在styled-components
天生款式組件中接收變量、函數、minxins,並將其變成純css。
這篇文章能夠相識更多:The magic behind 💅 styled-components
Server Side Rendering
styled-components
很好地支撐SSR。
一個例子:
import { renderToString } from 'react-dom/server'
import { ServerStyleSheet } from 'styled-components'
const sheet = new ServerStyleSheet()
const html = renderToString(sheet.collectStyles(<YourApp />))
const styleTags = sheet.getStyleTags() // or sheet.getStyleElement()
也能夠如許組件化包裹,只要在客戶端不這麼運用:
import { renderToString } from 'react-dom/server'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
const sheet = new ServerStyleSheet()
const html = renderToString(
<StyleSheetManager sheet={sheet.instance}>
<YourApp />
</StyleSheetManager>
)
const styleTags = sheet.getStyleTags() // or sheet.getStyleElement()
sheet.getStyleTags()
返回一個style標籤數組。詳細styled-components
關於SSR更深切的操縱,不在這裏繼續議論了,還能夠示知他兼容Next.js
關於SSR
的處置懲罰計劃。
Referring to other components
styled-components
供應了component selector
組件選擇器形式來替代我們以往對class名的依靠,處置懲罰得很清潔。這下我們沒必要為定名和選擇器爭執而苦惱了。
const Link = styled.a`
display: flex;
align-items: center;
padding: 5px 10px;
background: papayawhip;
color: palevioletred;
`;
const Icon = styled.svg`
transition: fill 0.25s;
width: 48px;
height: 48px;
${Link}:hover & {
fill: rebeccapurple;
}
`;
const Label = styled.span`
display: flex;
align-items: center;
line-height: 1.2;
&::before {
content: '◀';
margin: 0 10px;
}
`;
render(
<Link href="#">
<Icon viewBox="0 0 20 20">
<path d="M10 15h8c1 0 2-1 2-2V3c0-1-1-2-2-2H2C1 1 0 2 0 3v10c0 1 1 2 2 2h4v4l4-4zM5 7h2v2H5V7zm4 0h2v2H9V7zm4 0h2v2h-2V7z"/>
</Icon>
<Label>Hovering my parent changes my style!</Label>
</Link>
);
注重:
class A extends React.Component {
render() {
return <div />;
}
}
const B = styled.div`
${A} {
}
`;
這個例子是不能夠的,由於A繼續ReactComponent,不是被styled組織過的。我們的組件選擇器只支撐在Styled Components
建立的款式組件。
class A extends React.Component {
render() {
return <div className={this.props.className} />;
}
}
const StyledA = styled(A)``;
const B = styled.div`
${StyledA} {
}
`;
API文檔
基礎
- styled
- .attrs
- “字符模板
- ThemeProvider
助手
- css
- keyframes
- injectGlobal
- isStyledComponent
- withTheme
支撐CSS
在款式組件中,我們支撐一切CSS加嵌套。由於我們天生一個實在的stylesheet而不是內聯款式,所以CSS中的任何事情都在款式組件中事情!
(&)被我們所天生的、唯一的類名替代給款式組件,使其具有龐雜的邏輯變得輕易。
支撐flow和typescript
更多東西
Babel Plugin
Test Utilities
Jest Styled Components,基於jest,可對styled-components
做單元測試
Stylelint
運用stylelint 搜檢我們的styled-components
款式謄寫範例。
Styled Theming 語法高亮顯現
在模板文本中寫入CSS時喪失的一個東西是語法高亮顯現。我們正在勤奮在一切編輯器中完成準確的語法高亮顯現。支撐大部分編輯器包含Visual Studio Code、WebStorm。
總結
下面簡樸總結一下 styled-components 在開闢中的表現:
- 提出了 container 和 components 的觀點,移除了組件和款式之間的映照關聯,相符關注度星散的形式;
- 能夠在款式定義中直接引用到 js 變量,同享變量,異常輕易,應用js的特徵為css附能,帥斃了!
- 支撐組件之間繼續,輕易代碼復用,提拔可維護性;
- 兼容現有的 className 體式格局,晉級無痛;
- 這下寫CSS也興趣實足了。
- styled-components的最基礎頭腦就是經由過程移除款式和組件之間的映照來實行最好實踐
- 一個讓styled-components很輕易被接收的特徵:當他被疑心的時刻,你一樣能夠運用你熟習的要領去運用它!
固然,styled-components 另有一些優異的特徵,比方服務端襯着和 React Native 的支撐。
題外:styled-components的魔法
假如你從來沒看見過styled-components
,下面是一個簡樸的款式組件的例子:
const Button = styled.button`
background-color: papayawhip;
border-radius: 3px;
color: palevioletred;
`
如今能夠像運用一般React組件一樣襯着運用。
<Button>Hi Dad!</Button>
那末,這是怎樣事情的呢?這個過程當中究竟發生了什麼魔法?
標籤模板
實際上, style.button
` `是JavaScript的新語法特徵,屬於ES6的標籤模板功用。
本質上, styled.button
` 和
styled.button()`是一樣的。他們的差別只在通報參數時就變得可見了。
styled-components應用模板字符串的用途在於能夠給內部props賦值。
const Button = styled.button`
font-size: ${props => props.primary ? '2em' : '1em'};
`
// font-size: 2em;
<Button primary />