# 前端开发指南

## 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 <div>{title}</div>;
};

export default MyComponent;
```

### 4.2 Props类型检查

```jsx
import PropTypes from 'prop-types';

const MyComponent = ({ title, count, onAdd }) => {
  return <div>{title}: {count}</div>;
};

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 <button onClick={handleClick}>Click me</button>;
};

// 不推荐：在JSX中直接定义函数
const MyComponent = () => {
  return <button onClick={() => console.log('Clicked')}>Click me</button>;
};
```

## 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 <div className={styles.container}>Content</div>;
};
```

### 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 <StyledDiv>Content</StyledDiv>;
};
```

### 6.3 Tailwind CSS（可选）

```jsx
// 使用Tailwind CSS工具类
const MyComponent = () => {
  return (
    <div className="p-5 bg-gray-100 hover:bg-gray-200">
      Content
    </div>
  );
};
```

## 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 (
    <MyContext.Provider value={{ state, setState }}>
      {children}
    </MyContext.Provider>
  );
};

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 (
    <Routes>
      <Route path="/" element={<Navigate to="/dashboard" replace />} />
      <Route path="/dashboard" element={<Dashboard />} />
      <Route path="/projects" element={<ProjectList />} />
      <Route path="/projects/:id" element={<ProjectDetail />} />
    </Routes>
  );
};
```

### 8.2 路由参数

```jsx
import { useParams, useSearchParams } from 'react-router-dom';

const ProjectDetail = () => {
  const { id } = useParams();
  const [searchParams] = useSearchParams();

  const keyword = searchParams.get('keyword') || '';

  return <div>Project ID: {id}</div>;
};
```

### 8.3 路由守卫

```jsx
const PrivateRoute = ({ children }) => {
  const { isAuthenticated } = useAuth();

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  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 <div>{/* 渲染列表 */}</div>;
};
```

### 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 <Table {...} />;
};
```

## 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 <div>Something went wrong.</div>;
    }

    return this.props.children;
  }
}

// 使用
<ErrorBoundary>
  <App />
</ErrorBoundary>
```

### 10.2 错误处理组件

```jsx
const ErrorFallback = ({ error, resetErrorBoundary }) => {
  return (
    <div role="alert">
      <p>Something went wrong:</p>
      <pre>{error.message}</pre>
      <button onClick={resetErrorBoundary}>Try again</button>
    </div>
  );
};
```

## 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 (
    <Suspense fallback={<Spin />}>
      <Routes>
        <Route path="/projects" element={<ProjectList />} />
        <Route path="/projects/:id" element={<ProjectDetail />} />
      </Routes>
    </Suspense>
  );
};
```

### 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 <div onClick={handleClick}>Content</div>;
};
```

## 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(<Button>Click me</Button>);
    expect(screen.getByText('Click me')).toBeInTheDocument();
  });

  test('calls onClick when clicked', () => {
    const handleClick = jest.fn();
    render(<Button onClick={handleClick}>Click</Button>);
    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(<BrowserRouter>{component}</BrowserRouter>);
};

test('user can login', async () => {
  renderWithRouter(<App />);

  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`);
};

<Profiler id="MyComponent" onRender={onRenderCallback}>
  <MyComponent />
</Profiler>
```

## 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包裹
<Suspense fallback={<Loading />}>
  <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 (
    <Form form={form} onFinish={onFinish} layout="vertical">
      <Form.Item
        name="username"
        label="用户名"
        rules={[{ required: true, message: '请输入用户名' }]}
      >
        <Input />
      </Form.Item>
      <Form.Item>
        <Button type="primary" htmlType="submit">提交</Button>
      </Form.Item>
    </Form>
  );
};
```

---

**文档维护**: 前端程序员
**文档类型**: 前端开发指南
**最后更新**: 2026-01-25
