| name | frontend-coding-standards |
| description | 前端开发准则,适用于 Vue、React、Next.js、Taro 等框架。Use when developing frontend components to ensure: (1) Separation of component logic and styles, (2) Modular file organization with single-responsibility components, (3) File size control (split when exceeding 1000 lines), (4) Proper extraction and migration of sub-modules with their dependencies, (5) Regular creation of reusable component modules for better decoupling and software robustness. |
前端开发准则
适用于 Vue、React、Next.js、Taro 等前端框架的组件开发规范。通过模块化组织代码,提升代码的可维护性、复用性和健壮性。
核心原则
小文件、高内聚、低耦合。
组件应该小而专注,每个文件只负责一个明确的职责,避免将所有逻辑堆积在单个文件中。
四大准则
准则一:职责分离
组件函数和样式必须分开开发,禁止在一个文件中堆积多个模块的实现。
推荐的文件组织方式
ComponentName/
├── index.tsx # 组件入口(导出主组件)
├── Component.tsx # 组件逻辑实现
├── Component.types.ts # 类型定义(Props、State、Interface)
├── Component.utils.ts # 工具函数(数据处理、格式化等)
├── Component.hooks.ts # 自定义 Hooks(复杂逻辑抽取)
└── Component.module.css / Component.styles.ts # 样式定义
禁止的做法
import React, { useState, useEffect } from 'react';
interface User { ... }
interface Order { ... }
interface Product { ... }
const formatDate = (date: string) => { ... };
const calculateTotal = (items: Item[]) => { ... };
const UserCard = ({ user }) => { ... };
const OrderList = ({ orders }) => { ... };
const ProductGrid = ({ products }) => { ... };
const styles = { ... };
export default function Dashboard() {
return (
<div>
<UserCard user={user} />
<OrderList orders={orders} />
<ProductGrid products={products} />
{/* 数百行 JSX */}
</div>
);
}
推荐的做法
export { UserCard } from './UserCard';
import { UserCardProps } from './UserCard.types';
import { formatUserName } from './UserCard.utils';
import styles from './UserCard.module.css';
export function UserCard({ user }: UserCardProps) {
return <div className={styles.card}>{formatUserName(user)}</div>;
}
export interface UserCardProps {
user: User;
}
export function formatUserName(user: User): string {
return `${user.firstName} ${user.lastName}`;
}
.card { padding: 16px; border: 1px solid #eee; }
import { UserCard } from '@/components/UserCard';
import { OrderList } from '@/components/OrderList';
import { ProductGrid } from '@/components/ProductGrid';
export default function Dashboard() {
return (
<div>
<UserCard user={user} />
<OrderList orders={orders} />
<ProductGrid products={products} />
</div>
);
}
准则二:文件行数控制
当一个文件超过 1000 行时,必须考虑将组件模块拆分到单独的文件中。
拆分检查清单
| 文件行数 | 处理方式 |
|---|
| < 300 行 | 可以接受,保持现状 |
| 300-500 行 | 观察是否有可抽取的重复逻辑 |
| 500-800 行 | 建议拆分,寻找内聚的子模块 |
| 800-1000 行 | 必须拆分,避免继续膨胀 |
| > 1000 行 | 强制拆分,按功能模块分离 |
拆分策略
拆分前:
├── pages/
│ └── Dashboard.tsx (1500 行) ❌
拆分后:
├── pages/
│ └── Dashboard/
│ ├── index.tsx # 入口文件(50 行)
│ ├── Dashboard.tsx # 主组件逻辑(200 行)
│ ├── Dashboard.types.ts # 类型定义(100 行)
│ ├── Dashboard.utils.ts # 工具函数(150 行)
│ ├── Dashboard.hooks.ts # 自定义 Hooks(200 行)
│ ├── Dashboard.styles.ts # 样式定义(150 行)
│ └── components/ # 子组件目录
│ ├── UserSection/
│ │ ├── index.tsx
│ │ ├── UserSection.tsx
│ │ ├── UserSection.types.ts
│ │ └── UserSection.styles.ts
│ ├── StatsSection/
│ └── ActivitySection/
准则三:依赖一并迁移
迁移子模块时,该子组件使用到的函数和样式必须一并迁移到新文件中。
迁移检查清单
在拆分组件时,确保以下依赖完整迁移:
| 依赖类型 | 检查项 | 迁移目标 |
|---|
| 类型定义 | Props、Interface、Type | Component.types.ts |
| 工具函数 | 数据处理、格式化、校验 | Component.utils.ts |
| 自定义 Hooks | useEffect、useState 等封装 | Component.hooks.ts |
| 样式定义 | CSS、Styled-components、Tailwind | Component.styles.ts 或 .module.css |
| 常量定义 | 枚举、配置项、魔法数字 | Component.const.ts |
| 子子组件 | 仅该组件使用的子组件 | components/ 子目录 |
迁移示例
import { UserCard } from './components/UserCard';
function formatUserName(user: User) {
return `${user.firstName} ${user.lastName}`;
}
export default function Dashboard() {
}
import { UserCard } from './components/UserCard';
export default function Dashboard() {
return <UserCard user={user} />;
}
import { formatUserName } from './UserCard.utils';
import styles from './UserCard.module.css';
export function UserCard({ user }: UserCardProps) {
return <div className={styles.card}>{formatUserName(user)}</div>;
}
export function formatUserName(user: User): string {
return `${user.firstName} ${user.lastName}`;
}
准则四:积极创建组件
前端开发必须经常创建新的组件模块,不要犹豫。
何时应该创建新组件
| 场景 | 示例 |
|---|
| 代码复用 | 同样的 JSX 结构在多处使用 |
| 逻辑复杂 | 某一部分有独立的状态管理和生命周期 |
| 文件膨胀 | 当前文件行数超过阈值 |
| 功能独立 | 可以独立开发、测试、部署的单元 |
| 团队协作 | 不同成员负责不同模块 |
创建新组件的流程
发现可抽取的模块
↓
分析依赖关系(函数、样式、类型、子组件)
↓
创建组件目录和文件结构
↓
迁移代码和依赖到新组件
↓
在父组件中引用新组件
↓
验证功能完整性
组件粒度参考
function ButtonText({ text }) {
return <span>{text}</span>;
}
function ButtonIcon({ icon }) {
return <i className={icon} />;
}
function Button({ text, icon, onClick }) {
return (
<button onClick={onClick}>
{icon && <i className={icon} />}
<span>{text}</span>
</button>
);
}
function DataTable({ data, columns }) {
return (
<table>
<TableHeader columns={columns} />
<TableBody data={data} columns={columns} />
<TableFooter data={data} />
</table>
);
}
收益
遵循以上准则,你将获得:
| 收益 | 说明 |
|---|
| 降低文件行数 | 单文件复杂度下降,易于阅读和维护 |
| 增强复用性 | 独立的组件可以在多处复用,减少重复代码 |
| 模块解耦 | 组件间依赖清晰,修改一个模块不会影响其他模块 |
| 提高健壮性 | 故障隔离,局部问题不会扩散到整个系统 |
| 便于协作 | 不同开发者可以并行开发不同模块 |
| 易于测试 | 独立的组件更容易编写单元测试 |
检查清单
在开发或重构前端代码时,使用以下清单进行自我检查:
代码审查前
拆分组件时
代码提交前