# 前端开发指南
## 1. 开发环境搭建
### 1.1 安装依赖
```bash
# 安装Node.js
# 推荐版本:18.x 或更高
# 进入frontend目录
cd frontend
# 安装依赖
npm install
```
### 1.2 环境变量配置
```bash
# 复制环境变量模板
cp .env.example .env
# 编辑.env文件
VITE_API_BASE_URL=http://localhost:5000/api/v1
VITE_APP_TITLE=海洋项目管理系统
```
### 1.3 启动开发服务器
```bash
# 启动开发服务器
npm run dev
# 访问 http://localhost:3000
```
### 1.4 常用命令
```bash
# 开发
npm run dev
# 构建
npm run build
# 预览构建
npm run preview
# 代码检查
npm run lint
# 代码格式化
npm run format
# 运行测试
npm run test
# 测试覆盖率
npm run test:coverage
```
## 2. 代码规范
### 2.1 ESLint配置
```javascript
// .eslintrc.js
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:react/jsx-runtime',
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['react', 'react-hooks'],
rules: {
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
'no-console': ['warn', { allow: ['warn', 'error'] }],
},
settings: {
react: {
version: 'detect',
},
},
};
```
### 2.2 Prettier配置
```javascript
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"arrowParens": "avoid",
"endOfLine": "lf"
}
```
### 2.3 Git提交规范
```
格式:[类型] 简短描述
类型:
- feat: 新功能
- fix: 修复bug
- style: 代码格式调整
- refactor: 重构
- test: 测试
- docs: 文档
- chore: 构建/工具链
示例:
[frontend] feat: 添加项目列表页面
[frontend] fix: 修复登录后Token未保存的问题
[frontend] style: 统一代码格式
[frontend] refactor: 重构API服务模块
[frontend] test: 添加组件单元测试
[frontend] docs: 更新开发文档
[frontend] chore: 更新依赖版本
```
## 3. 目录结构说明
### 3.1 src目录
```
src/
├── assets/ # 静态资源
│ ├── images/ # 图片
│ ├── fonts/ # 字体
│ └── styles/ # 样式文件
├── components/ # 组件
│ ├── Layout/ # 布局组件
│ ├── Common/ # 通用组件
│ └── Business/ # 业务组件
├── pages/ # 页面组件
├── contexts/ # Context上下文
├── hooks/ # 自定义Hooks
├── services/ # API服务
├── utils/ # 工具函数
├── config/ # 配置文件
└── router/ # 路由配置
```
### 3.2 文件命名规范
```
组件:PascalCase
- Button.jsx
- ProjectList.jsx
- UserForm.jsx
Hooks:camelCase
- useAuth.js
- useTable.js
- useApi.js
工具函数:camelCase
- formatDate.js
- formatMoney.js
- validate.js
样式文件:kebab-case
- button.css
- project-list.css
- user-form.css
```
## 4. 组件开发规范
### 4.1 函数式组件
```jsx
// 推荐:使用函数式组件 + Hooks
import React, { useState, useEffect } from 'react';
const MyComponent = ({ title, data }) => {
const [loading, setLoading] = useState(false);
useEffect(() => {
// 副作用逻辑
}, []);
return
{title}
;
};
export default MyComponent;
```
### 4.2 Props类型检查
```jsx
import PropTypes from 'prop-types';
const MyComponent = ({ title, count, onAdd }) => {
return {title}: {count}
;
};
MyComponent.propTypes = {
title: PropTypes.string.isRequired,
count: PropTypes.number,
onAdd: PropTypes.func,
};
MyComponent.defaultProps = {
count: 0,
onAdd: () => {},
};
export default MyComponent;
```
### 4.3 事件处理
```jsx
// 推荐:使用箭头函数或bind绑定
const MyComponent = () => {
const handleClick = () => {
console.log('Clicked');
};
return ;
};
// 不推荐:在JSX中直接定义函数
const MyComponent = () => {
return ;
};
```
## 5. Hooks使用规范
### 5.1 useState
```jsx
// 基本用法
const [count, setCount] = useState(0);
// 函数式更新
const [count, setCount] = useState(0);
const increment = () => setCount(prev => prev + 1);
// 对象更新
const [user, setUser] = useState({ name: '', age: 0 });
const updateName = (name) => setUser(prev => ({ ...prev, name }));
```
### 5.2 useEffect
```jsx
// 副作用
useEffect(() => {
// 执行副作用
return () => {
// 清理函数
};
}, [dependencies]);
// 空依赖数组:只在挂载时执行一次
useEffect(() => {
console.log('Component mounted');
}, []);
// 依赖数组为空时,不要在依赖数组中省略
// 错误示例
useEffect(() => {
fetchUser(userId);
}, []); // eslint-disable-line react-hooks/exhaustive-deps
// 正确示例
useEffect(() => {
fetchUser(userId);
}, [userId]);
```
### 5.3 useMemo和useCallback
```jsx
// useMemo:缓存计算结果
const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
// useCallback:缓存函数
const handleClick = useCallback(() => {
doSomething(a, b);
}, [a, b]);
```
### 5.4 自定义Hooks
```jsx
// 自定义Hook以use开头
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(url);
setData(await response.json());
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
```
## 6. 样式开发规范
### 6.1 CSS模块
```jsx
// MyComponent.jsx
import styles from './MyComponent.module.css';
const MyComponent = () => {
return Content
;
};
```
### 6.2 CSS-in-JS(可选)
```jsx
// 使用styled-components或emotion
import styled from 'styled-components';
const StyledDiv = styled.div`
padding: 20px;
background: #f0f0f0;
&:hover {
background: #e0e0e0;
}
`;
const MyComponent = () => {
return Content;
};
```
### 6.3 Tailwind CSS(可选)
```jsx
// 使用Tailwind CSS工具类
const MyComponent = () => {
return (
Content
);
};
```
## 7. 状态管理规范
### 7.1 使用Context API
```jsx
// Context定义
import { createContext, useContext } from 'react';
const MyContext = createContext();
export const MyProvider = ({ children }) => {
const [state, setState] = useState(initialState);
return (
{children}
);
};
export const useMyContext = () => {
const context = useContext(MyContext);
if (!context) {
throw new Error('useMyContext must be used within MyProvider');
}
return context;
};
```
### 7.2 状态分类
```jsx
// 1. 本地状态:使用useState
const [value, setValue] = useState('');
// 2. 上下文状态:使用Context
const { user, setUser } = useAuth();
// 3. 表单状态:使用Ant Design Form
const [form] = Form.useForm();
// 4. URL状态:使用useSearchParams
const [searchParams, setSearchParams] = useSearchParams();
```
## 8. 路由开发规范
### 8.1 使用React Router
```jsx
import { Routes, Route, Navigate } from 'react-router-dom';
const Router = () => {
return (
} />
} />
} />
} />
);
};
```
### 8.2 路由参数
```jsx
import { useParams, useSearchParams } from 'react-router-dom';
const ProjectDetail = () => {
const { id } = useParams();
const [searchParams] = useSearchParams();
const keyword = searchParams.get('keyword') || '';
return Project ID: {id}
;
};
```
### 8.3 路由守卫
```jsx
const PrivateRoute = ({ children }) => {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) {
return ;
}
return children;
};
```
## 9. API调用规范
### 9.1 使用Axios
```jsx
import { useEffect } from 'react';
import { projectAPI } from '../services/project';
const ProjectList = () => {
const [list, setList] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
const fetchList = async () => {
setLoading(true);
try {
const data = await projectAPI.getList();
setList(data.items);
} catch (error) {
console.error('Fetch error:', error);
} finally {
setLoading(false);
}
};
fetchList();
}, []);
return {/* 渲染列表 */}
;
};
```
### 9.2 使用自定义Hooks
```jsx
import { useList } from '../hooks/useList';
import { projectAPI } from '../services/project';
const ProjectList = () => {
const { list, loading, pagination, handleTableChange } = useList(
projectAPI.getList
);
return ;
};
```
## 10. 错误处理规范
### 10.1 错误边界
```jsx
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return Something went wrong.
;
}
return this.props.children;
}
}
// 使用
```
### 10.2 错误处理组件
```jsx
const ErrorFallback = ({ error, resetErrorBoundary }) => {
return (
Something went wrong:
{error.message}
);
};
```
## 11. 性能优化
### 11.1 代码分割
```jsx
import { lazy, Suspense } from 'react';
const ProjectList = lazy(() => import('./pages/Projects/ProjectList'));
const ProjectDetail = lazy(() => import('./pages/Projects/ProjectDetail'));
const App = () => {
return (
}>
} />
} />
);
};
```
### 11.2 React.memo
```jsx
const ExpensiveComponent = React.memo(({ data }) => {
// 渲染逻辑
});
// 自定义比较函数
const MyComponent = React.memo(({ a, b }) => {
// 渲染逻辑
}, (prevProps, nextProps) => {
return prevProps.a === nextProps.a && prevProps.b === nextProps.b;
});
```
### 11.3 useMemo和useCallback
```jsx
const MyComponent = ({ data, onUpdate }) => {
// 缓存计算结果
const sortedData = useMemo(() => {
return data.sort((a, b) => a.id - b.id);
}, [data]);
// 缓存函数
const handleClick = useCallback(() => {
onUpdate(sortedData);
}, [onUpdate, sortedData]);
return Content
;
};
```
## 12. 测试规范
### 12.1 组件测试
```jsx
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button Component', () => {
test('renders button with children', () => {
render();
expect(screen.getByText('Click me')).toBeInTheDocument();
});
test('calls onClick when clicked', () => {
const handleClick = jest.fn();
render();
fireEvent.click(screen.getByText('Click'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
```
### 12.2 Hooks测试
```jsx
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
describe('useCounter', () => {
test('should increment counter', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
```
### 12.3 集成测试
```jsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
const renderWithRouter = (component) => {
return render({component});
};
test('user can login', async () => {
renderWithRouter();
fireEvent.change(screen.getByLabelText('Username'), {
target: { value: 'admin' },
});
fireEvent.change(screen.getByLabelText('Password'), {
target: { value: 'password' },
});
fireEvent.click(screen.getByText('Login'));
await waitFor(() => {
expect(screen.getByText('Dashboard')).toBeInTheDocument();
});
});
```
## 13. 调试技巧
### 13.1 React DevTools
```jsx
// 安装React DevTools浏览器扩展
// 可以查看组件树、props、state、Hooks等
```
### 13.2 Console日志
```jsx
// 开发环境下的调试日志
if (import.meta.env.DEV) {
console.log('Debug:', { data, loading });
}
// 生产环境下自动移除
```
### 13.3 性能分析
```jsx
// 使用React Profiler分析性能
import { Profiler } from 'react';
const onRenderCallback = (id, phase, actualDuration) => {
console.log(`${id} ${phase} took ${actualDuration}ms`);
};
```
## 14. 部署规范
### 14.1 构建配置
```javascript
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
base: '/app/', // 基础路径
build: {
outDir: 'dist',
sourcemap: false, // 生产环境关闭sourcemap
chunkSizeWarningLimit: 1000,
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
},
},
},
});
```
### 14.2 环境变量
```bash
# .env.development
VITE_API_BASE_URL=http://localhost:5000/api/v1
# .env.production
VITE_API_BASE_URL=https://api.example.com/api/v1
```
### 14.3 Docker部署
```dockerfile
# Dockerfile
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
## 15. 最佳实践
### 15.1 组件设计
- 单一职责原则:每个组件只做一件事
- 组件大小控制在300行以内
- 合理拆分组件,提高复用性
- 使用组合而非继承
### 15.2 性能优化
- 使用React.memo避免不必要的渲染
- 使用useMemo缓存计算结果
- 使用useCallback缓存函数
- 使用懒加载减少初始加载时间
### 15.3 代码质量
- 遵循ESLint和Prettier规范
- 编写单元测试
- 添加必要的注释
- 保持代码简洁清晰
### 15.4 安全性
- 避免直接渲染用户输入
- 使用HTTPS
- Token存储安全
- 设置适当的CORS策略
## 16. 常见问题
### 16.1 如何处理跨域?
```javascript
// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
},
},
},
});
```
### 16.2 如何优化首屏加载?
```jsx
// 使用路由懒加载
const Dashboard = lazy(() => import('./pages/Dashboard'));
// 使用Suspense包裹
}>
```
### 16.3 如何处理表单验证?
```jsx
import { Form, Input, Button } from 'antd';
const MyForm = () => {
const [form] = Form.useForm();
const onFinish = (values) => {
console.log('Form values:', values);
};
return (
);
};
```
---
**文档维护**: 前端程序员
**文档类型**: 前端开发指南
**最后更新**: 2026-01-25