Appearance
styled-components
安装
bash
# with npm
npm install --save styled-components
# with yarn
yarn add styled-componentsIt's highly recommended (but not required) to also use the Babel plugin.
cdn:通过 window.styled操作
html
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>基础
传递参数
${props => props.primary ? "white" : "palevioletred"}
jsx
const Button = styled.button`
/* Adapt the colors 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>
)继承 - styled
styled.button 等效 styled('button')
jsx
// 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;
`
// A new component based on Button, but with some override styles
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`改变标签类型 - as
jsx
render(
<div>
<Button>Normal Button</Button>
<Button as="a" href="#">
Link with Button styles
</Button>
<TomatoButton as="a" href="#">
Link with Tomato Button styles
</TomatoButton>
</div>
)自定义组件
jsx
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;
display: block;
`
const ReversedButton = (props) => <Button {...props} children={props.children.split('').reverse()} />
render(
<div>
<Button>Normal Button</Button>
<Button as={ReversedButton}>Custom Button with Normal Button styles</Button>
</div>
)三方组件上样式
jsx
// This could be react-router-dom'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>
)公共 css
引入 css 方式
jsx
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>
)
}
}样式组件
尽量在渲染函数外定义样式组件。
jsx
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>
)
}
}伪元素,伪类,嵌套
&:hover - 当前元素 hover
& ~ & - 前面有当前元素的所有元素
& + & - 下一个兄弟元素
&.className - 追加 className
.className & - 父级为 className
jsx
const Thing = styled.div.attrs((/* props */) => ({ tabIndex: 0 }))`
color: blue;
&:hover {
color: red; // <Thing> when hovered
}
& ~ & {
background: tomato; // <Thing> as a sibling of <Thing>, but maybe not directly next to it
}
& + & {
background: lime; // <Thing> next to <Thing>
}
&.something {
background: orange; // <Thing> tagged with an additional CSS class ".something"
}
.something-else & {
border: 1px solid; // <Thing> inside another element labeled ".something-else"
}
`
render(
<React.Fragment>
<Thing>Hello world!</Thing>
<Thing>How ya doing?</Thing>
<Thing className="something">The sun is shining...</Thing>
<div>Pretty nice day today.</div>
<Thing>Don't you think?</Thing>
<div className="something-else">
<Thing>Splendid.</Thing>
</div>
</React.Fragment>
)没有 &代表 孩子节点
jsx
const Thing = styled.div`
color: blue;
.something {
border: 1px solid; // an element labeled ".something" inside <Thing>
display: block;
}
`
render(
<Thing>
<label htmlFor="foo-button" className="something">
Mystery button
</label>
<button id="foo-button">What do I do?</button>
</Thing>
)&& ==?提高权重==
jsx
const Thing = styled.div`
&& {
color: blue;
}
`
const GlobalStyle = createGlobalStyle`
div${Thing} {
color: red;
}
`
render(
<React.Fragment>
<GlobalStyle />
<Thing>I'm blue, da ba dee da ba daa</Thing>
</React.Fragment>
)额外附加 - attr
.attr - 设置默认值
jsx
const Input = styled.input.attrs((props) => ({
// we can define static props
type: 'text',
// or we can define dynamic ones
size: props.size || '1em'
}))`
color: palevioletred;
font-size: 1em;
border: 2px solid palevioletred;
border-radius: 3px;
/* here we use the dynamically computed prop */
margin: ${(props) => props.size};
padding: ${(props) => props.size};
`
render(
<div>
<Input placeholder="A small text input" />
<br />
<Input placeholder="A bigger text input" size="2em" />
</div>
).attr - 从内至外,外层样式会覆盖内层
jsx
const Input = styled.input.attrs((props) => ({
type: 'text',
size: props.size || '1em'
}))`
border: 2px solid palevioletred;
margin: ${(props) => props.size};
padding: ${(props) => props.size};
`
// Input's attrs will be applied first, and then this attrs obj
const PasswordInput = styled(Input).attrs({
type: 'password'
})`
// similarly, border will override Input's border
border: 2px solid aqua;
`
render(
<div>
<Input placeholder="A bigger text input" size="2em" />
<br />
{/* Notice we can still use the size attr from Input */}
<PasswordInput placeholder="A bigger password input" size="2em" />
</div>
)动画
@keyframes 为全局声明,需要避免命名冲突
jsx
// Create the keyframes
const rotate = 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: ${rotate} 2s linear infinite;
padding: 2rem 1rem;
font-size: 1.2rem;
`
render(<Rotate>< 💅🏾 ></Rotate>)懒惰注入
不能直接使用,需要以 css`` 方式定义。
css``:css 版模板引擎
jsx
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;
`进阶
主题
主题上下文
<ThemeProvider> ,为子级提供 context
defaultProps:提供默认值
jsx
// Define our button, but with the use of props.theme this time
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};
`
// We are passing a default theme for Buttons that arent wrapped in the ThemeProvider
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>
)函数主题
使用函数进行内容转换
jsx
// Define our button, but with the use of props.theme this time
const Button = styled.button`
color: ${(props) => props.theme.fg};
border: 2px solid ${(props) => props.theme.fg};
background: ${(props) => props.theme.bg};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
`
// Define our `fg` and `bg` on the theme
const theme = {
fg: 'palevioletred',
bg: 'white'
}
// This theme swaps `fg` and `bg`
const invertTheme = ({ fg, bg }) => ({
fg: bg,
bg: fg
})
render(
<ThemeProvider theme={theme}>
<div>
<Button>Default Theme</Button>
<ThemeProvider theme={invertTheme}>
<Button>Inverted Theme</Button>
</ThemeProvider>
</div>
</ThemeProvider>
)获取 styled components
高阶组件
withTheme:可以在 styled components 外 使用 theme
jsx
import { withTheme } from 'styled-components'
class MyComponent extends React.Component {
render() {
console.log('Current theme: ', this.props.theme)
// ...
}
}
export default withTheme(MyComponent)hook
Theme
jsx
import { useContext } from 'react'
import { ThemeContext } from 'styled-components'
const MyComponent = () => {
const themeContext = useContext(ThemeContext)
console.log('Current theme: ', themeContext)
// ...
}