项目结构 #
基本概念 #
良好的项目结构可以提高代码的可维护性和可读性。本节介绍 Emotion 项目的推荐组织方式。
目录结构 #
基础结构 #
text
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.styles.ts
│ │ ├── Button.types.ts
│ │ └── index.ts
│ ├── Input/
│ ├── Card/
│ └── index.ts
├── styles/
│ ├── theme.ts
│ ├── global.ts
│ ├── mixins.ts
│ └── breakpoints.ts
├── layouts/
│ ├── MainLayout.tsx
│ └── MainLayout.styles.ts
├── pages/
│ ├── Home.tsx
│ └── About.tsx
└── App.tsx
组件内聚结构 #
将样式与组件放在一起:
text
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.styles.ts
│ │ ├── Button.test.tsx
│ │ └── index.ts
│ └── Card/
│ ├── Card.tsx
│ ├── Card.styles.ts
│ ├── CardHeader.tsx
│ ├── CardBody.tsx
│ ├── CardFooter.tsx
│ └── index.ts
样式分离结构 #
将所有样式集中管理:
text
src/
├── components/
│ ├── Button.tsx
│ ├── Input.tsx
│ └── Card.tsx
├── styles/
│ ├── components/
│ │ ├── button.styles.ts
│ │ ├── input.styles.ts
│ │ └── card.styles.ts
│ ├── theme.ts
│ └── global.ts
文件命名规范 #
组件文件 #
| 文件类型 | 命名 | 说明 |
|---|---|---|
| 组件 | ComponentName.tsx |
PascalCase |
| 样式 | ComponentName.styles.ts |
样式定义 |
| 类型 | ComponentName.types.ts |
TypeScript 类型 |
| 测试 | ComponentName.test.tsx |
测试文件 |
| 索引 | index.ts |
导出文件 |
样式文件示例 #
Button.styles.ts:
tsx
import styled from '@emotion/styled'
import { css } from '@emotion/react'
export const buttonBase = css`
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
transition: all 0.2s ease;
`
export const Button = styled.button`
${buttonBase}
background-color: ${props => props.theme.colors.primary};
color: white;
&:hover {
opacity: 0.9;
}
`
export const OutlineButton = styled.button`
${buttonBase}
background: transparent;
border: 2px solid ${props => props.theme.colors.primary};
color: ${props => props.theme.colors.primary};
`
Button.tsx:
tsx
import { Button, OutlineButton } from './Button.styles'
export { Button, OutlineButton }
组件组织 #
单文件组件 #
适合简单组件:
tsx
import styled from '@emotion/styled'
const Container = styled.div`
padding: 20px;
background: #f5f5f5;
`
const Title = styled.h2`
font-size: 24px;
color: #333;
`
export function SimpleCard({ title, children }) {
return (
<Container>
<Title>{title}</Title>
{children}
</Container>
)
}
多文件组件 #
适合复杂组件:
Card.styles.ts:
tsx
import styled from '@emotion/styled'
export const Container = styled.div`
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
overflow: hidden;
`
export const Header = styled.div`
padding: 16px;
border-bottom: 1px solid #eee;
`
export const Body = styled.div`
padding: 16px;
`
export const Footer = styled.div`
padding: 16px;
background: #f9f9f9;
`
Card.tsx:
tsx
import { Container, Header, Body, Footer } from './Card.styles'
export function Card({ header, children, footer }) {
return (
<Container>
{header && <Header>{header}</Header>}
<Body>{children}</Body>
{footer && <Footer>{footer}</Footer>}
</Container>
)
}
Card.Header = Header
Card.Body = Body
Card.Footer = Footer
样式组织 #
主题文件 #
styles/theme.ts:
tsx
export const theme = {
colors: {
primary: '#007bff',
secondary: '#6c757d',
success: '#28a745',
danger: '#dc3545',
warning: '#ffc107',
info: '#17a2b8',
},
fontSizes: {
xs: '12px',
sm: '14px',
md: '16px',
lg: '18px',
xl: '20px',
},
spacing: {
xs: '4px',
sm: '8px',
md: '16px',
lg: '24px',
xl: '32px',
},
borderRadius: {
sm: '4px',
md: '8px',
lg: '12px',
},
}
export type Theme = typeof theme
Mixins 文件 #
styles/mixins.ts:
tsx
import { css } from '@emotion/react'
export const mixins = {
flexCenter: css`
display: flex;
align-items: center;
justify-content: center;
`,
flexBetween: css`
display: flex;
align-items: center;
justify-content: space-between;
`,
truncate: css`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`,
cardShadow: css`
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
`,
smoothTransition: css`
transition: all 0.2s ease;
`,
visuallyHidden: css`
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
`,
}
断点文件 #
styles/breakpoints.ts:
tsx
import { css } from '@emotion/react'
export const breakpoints = {
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
}
export const media = {
sm: (styles) => css`
@media (min-width: ${breakpoints.sm}) {
${styles}
}
`,
md: (styles) => css`
@media (min-width: ${breakpoints.md}) {
${styles}
}
`,
lg: (styles) => css`
@media (min-width: ${breakpoints.lg}) {
${styles}
}
`,
xl: (styles) => css`
@media (min-width: ${breakpoints.xl}) {
${styles}
}
`,
}
组件库结构 #
UI 组件库 #
text
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.styles.ts
│ │ ├── Button.types.ts
│ │ ├── Button.stories.tsx
│ │ └── index.ts
│ ├── Input/
│ ├── Select/
│ ├── Checkbox/
│ └── index.ts
├── styles/
│ ├── theme.ts
│ ├── global.ts
│ └── index.ts
└── index.ts
导出结构 #
components/index.ts:
tsx
export { Button } from './Button'
export { Input } from './Input'
export { Select } from './Select'
export { Checkbox } from './Checkbox'
index.ts:
tsx
export * from './components'
export * from './styles'
最佳实践 #
1. 一致的命名 #
tsx
const PrimaryButton = styled.button`...`
const SecondaryButton = styled.button`...`
const DangerButton = styled.button`...`
2. 样式复用 #
tsx
const baseInputStyles = css`
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
`
const Input = styled.input`
${baseInputStyles}
`
const Textarea = styled.textarea`
${baseInputStyles}
min-height: 100px;
`
3. 组件分组 #
tsx
const Card = styled.div`...`
const CardHeader = styled.div`...`
const CardBody = styled.div`...`
const CardFooter = styled.div`...`
Card.Header = CardHeader
Card.Body = CardBody
Card.Footer = CardFooter
export { Card }
4. 类型导出 #
tsx
export type { ButtonProps } from './Button.types'
export { Button } from './Button'
下一步 #
掌握了项目结构后,继续学习 Emotion与TypeScript,了解如何在 TypeScript 项目中使用 Emotion。
最后更新:2026-03-28