NPM version
Downloads
Repo Dependents
Github repo

此快速参考备忘单提供了使用 CSS in JS 工具的各种方法

入门

安装

Styled Components 是增强 CSS 在 React 组件系统样式的 CSS in JS 的最佳实践。

安装依赖和 TypeScript 类型依赖

1
npm install --save styled-components

快速开始

1
import styled from 'styled-components';

创建一个 Title 组件

1
2
3
4
5
// 该组件将呈现具有样式的 <h1> 标签
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
`;

创建一个 Wrapper 组件

1
2
3
4
5
// 该组件将呈现具有某些样式的 <section> 标记
const Wrapper = styled.section`
padding: 4em;
background: papayawhip;
`;

像使用其他 React 组件一样使用 Title/Wrapper - 除了它们的样式!

1
2
3
4
5
6
7
8
9
function Demo() {
return (
<Wrapper>
<Title>
Hello World!
</Title>
</Wrapper>
);
}

根据 Props 适配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import styled from 'styled-components';

const Button = styled.button`
/* 根据主要 props 调整颜色 */
background: ${
props =>
props.primary ? "blue" : "white"
};
color: ${
props =>
props.primary ? "white" : "blue"
};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid blue;
border-radius: 3px;
`;

使用 primary props 控制按钮样式

{5}
1
2
3
4
5
6
7
8
function Demo() {
return (
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
}

扩展样式

{7}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const Button = styled.button`
color: palevioletred;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// 基于 Button 的新组件,但具有一些覆盖样式
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
const Demo = () => (
<div>
<Button>普通按钮</Button>
<TomatoButton>番茄色按钮</TomatoButton>
</div>
);

扩展样式改变标签 (as)

{17,20}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const Button = styled.button`
color: palevioletred;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
display: block;
`;

const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;

const Demo = () => (
<div>
<Button>普通按钮</Button>
<Button as="a" href="#">
按钮样式的链接
</Button>
<TomatoButton as="a" href="#">
番茄按钮样式的链接
</TomatoButton>
</div>
);

自定义组件(as)

{20}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const Button = styled.button`
color: palevioletred;
font-size: 1em;
border: 2px solid palevioletred;
display: block;
`;

const ReversedButton = props => (
<Button
{...props}
children={
props.children.split('').reverse()
}
/>
);

render(
<div>
<Button>普通按钮</Button>
<Button as={ReversedButton}>
具有普通按钮样式的自定义按钮
</Button>
</div>
);

样式化任何组件

1
2
3
4
5
6
7
8
9
10
const Link = ({ className, children }) => (
<a className={className}>
{children}
</a>
);
const StyledLink = styled(Link)`
color: palevioletred;
font-weight: bold;
`;
<StyledLink className="hello" />

在 render 之外定义 Styled 组件

{3}
1
2
3
4
5
6
7
8
9
const Box = styled.div`/* ... */`;
const Wrapper = ({ message }) => {
// ⚠️ 不能在这里定义 styled 组件
return (
<Box>
{message}
</Box>
);
};

注意:组件 Box 不能放到 Wrapper 函数组件里面

传入值

{3,4,17}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const Input = styled.input`
color: ${
props =>
props.inputColor || "palevioletred"
};
background: papayawhip;
`;
const Demo = () => (
<div>
<Input
defaultValue="@probablyup"
type="text"
/>
<Input
defaultValue="@geelen"
type="text"
inputColor="rebeccapurple"
/>
</div>
);

样式对象

{2,5}
1
2
3
4
5
6
const PropsBox = styled.div(props => ({
background: props.background,
height: '50px',
width: '50px',
fontSize: '12px'
}));

在组件中使用

{5}
1
2
3
4
5
6
7
8
9
const Example = () => {
return (
<div>
<PropsBox
background="blue"
/>
</div>
);
}

注意:样式对象里面的样式并不是 CSS 中的写法。

CSSModules => styled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import React, { useState } from 'react';
import styles from './styles.css';

function ExampleCounter() {
const [count, setCount] = useState(0)
return (
<div className={styles.counter}>
<p className={styles.paragraph}>
{count}
</p>
<button
className={styles.button}
onClick={() => setCount(count +1)}
>
+
</button>
<button
className={styles.button}
onClick={() => setCount(count -1)}
>
-
</button>
</div>
);
}

👇👇 与下面 styled 写法等效 👇👇

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import styled from 'styled-components';

const StyledCounter = styled.div`
/* ... */
`;
const Paragraph = styled.p`
/* ... */
`;
const Button = styled.button`
/* ... */
`;
function ExampleCounter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count +1);
}
const decrement = () => {
setCount(count -1);
}
return (
<StyledCounter>
<Paragraph>{count}</Paragraph>
<Button onClick={increment}>
+
</Button>
<Button onClick={decrement}>
-
</Button>
</StyledCounter>
);
}

伪元素、伪选择器和嵌套

{3,6,9,12,15}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const Thing = styled.div.attrs((/* props */) => ({ tabIndex: 0 }))`
color: blue;
&:hover { /* <Thing> 悬停时 */
color: red;
}
& ~ & { /* <Thing> 作为 <Thing> 的兄弟,但可能不直接在它旁边 */
background: tomato;
}
& + & { /* <Thing> 旁边的 <Thing> */
background: lime;
}
&.something { /* <Thing> 标记有一个额外的 CSS 类 “.something” */
background: orange;
}
.something-else & { /* <Thing> 在另一个标记为 “.something-else” 的元素中 */
border: 1px solid;
}
`;

render(
<React.Fragment>
<Thing>Hello world!</Thing>
<Thing>你怎么样?</Thing>
<Thing className="something">
艳阳高照...
</Thing>
<div>今天真是美好的一天。</div>
<Thing>你不觉得吗?</Thing>
<div className="something-else">
<Thing>灿烂</Thing>
</div>
</React.Fragment>
);

改变 styled 组件样式

{13,21}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { css } from 'styled-components'
import styled from 'styled-components'

const Input = styled.input.attrs({
type: "checkbox"
})``;
const LabelText = styled.span`
${(props) => {
switch (props.$mode) {
case "dark":
return css`
color: white;
${Input}:checked + && {
color: blue;
}
`;
default:
return css`
color: black;
${Input}:checked + && {
color: red;
}
`;
}
}}
`;

function Example() {
return (
<React.Fragment>
<Label>
<Input defaultChecked />
<LabelText>Foo</LabelText>
</Label>
<Label>
<Input />
<LabelText $mode="dark">
Foo
</LabelText>
</Label>
</React.Fragment>
);
}

全局样式 createGlobalStyle

{3,11}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import {
styled,
createGlobalStyle
} from 'styled-components'

const Thing = styled.div`
&& {
color: blue;
}
`;
const GlobalStyle = createGlobalStyle`
div${Thing} {
color: red;
}
`;

const Example = () => (
<React.Fragment>
<GlobalStyle />
<Thing>
我是蓝色的
</Thing>
</React.Fragment>
);

className 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const Thing = styled.div`
color: blue;
/* <Thing> 中标记为“.something”的元素 */
.something {
border: 1px solid;
}
`;

function Example() {
return (
<Thing>
<label
htmlFor="foo-button"
className="something"
>
神秘按钮
</label>
<button id="foo-button">
我该怎么办?
</button>
</Thing>
)
}

共享样式片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const rotate = keyframes`
from {top:0px;}
to {top:200px;}
`;

// ❌ 这将引发错误!
const styles = `
animation: ${rotate} 2s linear infinite;
`;

// ✅ 这将按预期工作
const styles = css`
animation: ${rotate} 2s linear infinite;
`;

Class 组件样式定义

{5}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class NewHeader extends React.Component {
render() {
return (
<div
className={this.props.className}
/>
);
}
}
const StyledA = styled(NewHeader)``
const Box = styled.div`
${StyledA} {
/* 变更 NewHeader 样式 */
}
`;

附加额外的 Props

{3,5,13,14,23}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Input = styled.input.attrs(props=>({
// 我们可以定义静态道具
type: "text",
// 或者我们可以定义动态的
size: props.size || "1em",
}))`
color: palevioletred;
font-size: 1em;
border: 2px solid palevioletred;
border-radius: 3px;

/* 这里我们使用动态计算的 props */
margin: ${props => props.size};
padding: ${props => props.size};
`;

使用 Input 组件

1
2
3
4
5
6
7
8
9
10
11
12
function Example() {
return (
<div>
<Input placeholder="小文本输入" />
<br />
<Input
placeholder="更大的文本输入"
size="2em"
/>
</div>
)
}

覆盖 .attrs

{11}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 的attrs会先被应用,然后这个 attrs obj
const PasswordInput = styled(Input).attrs({
type: "password",
})`
/* 同样,border 将覆盖 Input 的边框 */
border: 2px solid aqua;
`;

使用 InputPasswordInput 组件

{5,11}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
render(
<div>
<Input
placeholder="更大的文本输入"
size="2em"
/>
<br />
{/*⚠️ 仍然可以使用Input中的 size attr*/}
<PasswordInput
placeholder="更大的密码输入"
size="2em"
/>
</div>
);

动画

创建关键帧

1
2
3
4
5
6
7
8
9
const rotate = keyframes`
from {
transform: rotate(0deg);
}

to {
transform: rotate(360deg);
}
`;

我们创建一个 Rotate 组件

1
2
3
4
5
6
7
// 它将在两秒内旋转我们传递的所有内容
const Rotate = styled.div`
display: inline-block;
animation: ${rotate} 2s linear infinite;
padding: 2rem 1rem;
font-size: 1.2rem;
`;

使用 Rotate 组件

1
2
3
4
5
function Example() {
return (
<Rotate>&lt; 💅🏾 &gt;</Rotate>
)
}

isStyledComponent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react'
import styled, { isStyledComponent } from 'styled-components'
import MaybeStyledComponent from './my'

let TargetedComponent = isStyledComponent(MaybeStyledComponent)
? MaybeStyledComponent
: styled(MaybeStyledComponent)``;

const ParentComponent = styled.div`
color: cornflowerblue;

${TargetedComponent} {
color: tomato;
}
`;

ThemeConsumer

1
2
3
4
5
6
7
8
9
10
11
12
13
import {
ThemeConsumer
} from 'styled-components'

function Example() {
return (
<ThemeConsumer>
{theme => (
<div>主题色是 {theme.color}</div>
)}
</ThemeConsumer>
);
}

TypeScript

安装

Web 应用上安装 styled

1
npm install -D @types/styled-components

React Native 应用上安装 styled

1
2
3
npm install -D \
@types/styled-components \
@types/styled-components-react-native

如果对 TypeScript 不熟悉,参考 TypeScript 备忘清单

自定义 Props

1
2
3
4
5
6
7
8
9
10
11
12
13
import styled from 'styled-components';

interface TitleProps {
readonly isActive: boolean;
}

const Title = styled.h1<TitleProps>`
color: ${(props) => (
props.isActive
? props.theme.colors.main
: props.theme.colors.secondary
)};
`;

简单的 Props 类型定义

1
2
3
4
5
6
7
8
9
10
11
12
import styled from 'styled-components';
import Header from './Header';

const Header = styled.header`
font-size: 12px;
`;

const NewHeader = styled(Header)<{
customColor: string;
}>`
color: ${(props) => props.customColor};
`;

禁止转移到子组件($)

{5}
1
2
3
4
5
6
7
8
9
10
11
12
import styled from 'styled-components';
import Header from './Header';

interface ReHeader {
$customColor: string;
}

const ReHeader = styled(Header)<ReHeader>`
color: ${
props => props.$customColor
};
`;

禁止 customColor 属性转移到 Header 组件,在其前面加上美元($)符号

函数组件类型继承

{8,13}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { FC, PropsWithRef, DetailedHTMLProps, ImgHTMLAttributes } from 'react';
import styled from 'styled-components';

const Img = styled.img`
height: 32px;
width: 32px;
`;
export interface ImageProps extends DetailedHTMLProps<
ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement
> {
text?: string;
};
export const Image: FC<PropsWithRef<ImageProps>> = (props) => (
<Img src="" alt="" {...props} />
);

React Native

基础实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from 'react'
import styled from 'styled-components/native'

const StyledView = styled.View`
background-color: papayawhip;
`;
const StyledText = styled.Text`
color: palevioletred;
`;

class MyReactNativeComponent extends React.Component {
render() {
return (
<StyledView>
<StyledText>Hello World!</StyledText>
</StyledView>
);
}
}

React Native 中写 CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import styled from 'styled-components/native'

const RotatedBox = styled.View`
transform: rotate(90deg);
text-shadow-offset: 10px 5px;
font-variant: small-caps;
margin: 5px 7px 2px;
`;

function Example() {
return (
<RotatedBox />
)
}

与 web 版本的一些区别是,您不能使用关键帧(keyframes)和 createGlobalStyle 助手,因为 React Native 不支持关键帧或全局样式。如果您使用媒体查询或嵌套 CSS,我们也会警告您。

高级用法

主题化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import styled, { ThemeProvider } from 'styled-components'

// 定义我们的按钮,但这次使用 props.theme
const Button = styled.button`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;

/* 使用 theme.main 为边框和文本着色 */
color: ${props => props.theme.main};
border: 2px solid ${props => props.theme.main};
`;

// 我们正在为未包装在 ThemeProvider 中的按钮传递默认主题
Button.defaultProps = {
theme: {
main: "palevioletred"
}
}

// 定义 props.theme 的外观
const theme = {
main: "mediumseagreen"
};

render(
<div>
<Button>Normal</Button>

<ThemeProvider theme={theme}>
<Button>Themed</Button>
</ThemeProvider>
</div>
);

功能主题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import styled, { ThemeProvider } from 'styled-components'

// 定义我们的按钮,但这次使用 props.theme
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;
`;
// 在主题上定义我们的`fg`和`bg`
const theme = {
fg: "palevioletred",
bg: "white"
};

// 这个主题交换了`fg`和`bg`
const invertTheme = ({ fg, bg }) => ({
fg: bg,
bg: fg
});

render(
<ThemeProvider theme={theme}>
<div>
<Button>默认主题</Button>
<ThemeProvider theme={invertTheme}>
<Button>反转主题</Button>
</ThemeProvider>
</div>
</ThemeProvider>
);

通过 withTheme 高阶组件

1
2
3
4
5
6
7
8
9
10
import { withTheme } from 'styled-components'

class MyComponent extends React.Component {
render() {
console.log('Current theme: ', this.props.theme)
// ...
}
}

export default withTheme(MyComponent)

useContext 钩子

1
2
3
4
5
6
7
8
9
import { useContext } from 'react'
import { ThemeContext } from 'styled-components'

const MyComponent = () => {
const themeContext = useContext(ThemeContext)

console.log('Current theme: ', themeContext)
// ...
}

useTheme 自定义钩子

1
2
3
4
5
6
7
8
import {useTheme} from 'styled-components'

const MyComponent = () => {
const theme = useTheme()

console.log('Current theme: ', theme)
// ...
}

主题 props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import {
ThemeProvider,
styled
} from 'styled-components';

// 定义我们的按钮
const Button = styled.button`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
/* 使用 theme.main 为边框和文本着色 */
color: ${props => props.theme.main};
border: 2px solid ${props => props.theme.main};
`;
// 定义主题的外观
const theme = {
main: "mediumseagreen"
};

使用自定义主题组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
render(
<div>
<Button theme={{ main: "royalblue" }}>
特设主题
</Button>
<ThemeProvider theme={theme}>
<div>
<Button>Themed</Button>
<Button
theme={{ main: "darkorange" }}
>
被覆盖
</Button>
</div>
</ThemeProvider>
</div>
);

Refs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import {
ThemeProvider,
styled
} from 'styled-components';

const Input = styled.input`
border: none;
border-radius: 3px;
`;

class Form extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}

render() {
return (
<Input
ref={this.inputRef}
placeholder="Hover to focus!"
onMouseEnter={() => {
this.inputRef.current.focus()
}}
/>
);
}
}

使用 Form 组件

1
2
3
4
5
function Example() {
return (
<Form />
)
}

特异性问题

在文件 MyComponent.js 中定义 MyComponent 组件。

1
2
3
const MyComponent = styled.div`
background-color: green;
`;

定义样式 my-component.css

1
2
3
.red-bg {
background-color: red;
}

使用 MyComponent 组件

1
<MyComponent className="red-bg" />

由于某种原因,这个组件仍然有绿色背景,即使你试图用 red-bg 类覆盖它!

解决方案

1
2
3
.red-bg.red-bg {
background-color: red;
}

ThemeProvider

1
2
3
4
5
6
7
8
9
10
11
import styled, { ThemeProvider } from 'styled-components'

const Box = styled.div`
color: ${props => props.theme.color};
`;

const Example = () => (
<ThemeProvider theme={{ color: 'mediumseagreen' }}>
<Box>I'm mediumseagreen!</Box>
</ThemeProvider>
);

shouldForwardProp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Comp = styled('div').withConfig({
shouldForwardProp: (prop, defaultValidatorFn) =>
!['hidden'].includes(prop) && defaultValidatorFn(prop),
}).attrs({ className: 'foo' })`
color: red;
&.foo {
text-decoration: underline;
}
`;

const Example = () => (
<Comp hidden draggable="true">
Drag Me!
</Comp>
);

评论
avatar
竹山一叶
技术分享 个人心得
Follow Me
公告
欢迎光临小站,这里是我日常工作和学习中收集和整理的总结,希望能对你有所帮助:)

本站的内容经过个人加工总结而来,也参考了网友们分享的资料,如有侵权,请第一时间联系我,我将及时进行修改或删除😊
最新文章
网站资讯
文章数目 :
437
已运行时间 :
本站总字数 :
431.6k
本站访客数 :
本站总访问量 :
最后更新时间 :
文章归档文章分类文章标签复制本文标题复制本文地址
随便逛逛