跳转到内容

前端工程规范

前端目录、构建、依赖、代码组织


  • Standard:前端项目统一采用以下顶层目录结构,适用于 React / Vue 项目。
project/
├── public/ # 不经过构建的静态资源(favicon、robots.txt)
├── src/
│ ├── api/ # 接口请求封装
│ ├── assets/ # 经过构建处理的静态资源(图片、字体、样式)
│ ├── components/ # 通用组件(跨页面复用)
│ ├── layouts/ # 布局组件
│ ├── pages/ # 页面级组件
│ ├── hooks/ # 自定义 Hooks / Composables
│ ├── store/ # 全局状态管理
│ ├── router/ # 路由配置
│ ├── utils/ # 工具函数
│ ├── types/ # TypeScript 类型定义
│ ├── constants/ # 常量定义
│ ├── styles/ # 全局样式、主题变量
│ └── main.tsx / main.ts # 应用入口
├── index.html
├── package.json
├── tsconfig.json
├── vite.config.ts
└── .env / .env.local
  • Requirement:页面级组件放在 pages/,复用组件放在 components/,禁止混用。
  • Standard:按功能就近组织,单一功能的组件、hooks、样式放在同一目录下。
  • Standard:utils/ 按职责拆分文件,禁止创建万能 utils/index.ts 堆积所有方法。
  • Caution:components/ 仅放跨页面复用的通用组件,页面私有组件放在对应 pages/xxx/components/ 下。
反模式问题建议
所有组件堆在 components/ 根目录无法区分通用与私有页面私有组件放 pages/xxx/components/
utils/index.ts 堆积所有方法难以查找、循环依赖风险按职责拆分文件
资源直接散放在 src/ 根目录结构混乱归入 assets/ 对应子目录

  • Default:优先使用 Vite 作为构建工具,旧项目可保留 Webpack,新立项必须使用 Vite。
  • Requirement:构建配置文件(vite.config.ts / webpack.config.js)必须提交到仓库,禁止本地保留未提交的配置。
  • Requirement:必须配置 @ 别名指向 src/,禁止在导入中使用多层 ../../ 相对路径。
// vite.config.ts
import { defineConfig } from 'vite';
import path from 'path';
 
export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
});
// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
  • Standard:产物输出到 dist/ 目录,资源按类型分目录(assets/js、assets/css、assets/img)。
  • Standard:文件名携带 hash 用于缓存破坏,格式为 [name].[hash:8].[ext]。
// vite.config.ts
export default defineConfig({
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    rollupOptions: {
      output: {
        entryFileNames: 'assets/js/[name].[hash:8].js',
        chunkFileNames: 'assets/js/[name].[hash:8].js',
        assetFileNames: 'assets/[ext]/[name].[hash:8].[ext]',
      },
    },
  },
});
  • Requirement:环境变量必须以 VITE_ 前缀开头(Vite)或 REACT_APP_ 前缀(CRA),禁止在代码中直接读取 process.env 下的非约定变量。
  • Standard:不同环境的配置放在 .env.development、.env.staging、.env.production,.env.local 不提交到仓库。
# .env.development
VITE_API_BASE_URL=https://dev-api.example.com
VITE_APP_ENV=development
 
# .env.production
VITE_API_BASE_URL=https://api.example.com
VITE_APP_ENV=production
// 正确
const baseURL = import.meta.env.VITE_API_BASE_URL;
 
// 错误
const baseURL = process.env.API_BASE_URL;
  • Standard:配置构建分析工具(rollup-plugin-visualizer / webpack-bundle-analyzer),定期检查包体积。
  • Caution:单 chunk 超过 500KB 时必须拆包或做懒加载优化。

  • Requirement:必须提交 package-lock.json(npm)或 pnpm-lock.yaml(pnpm)或 yarn.lock(yarn),保证团队成员安装的依赖版本一致。
  • Requirement:package.json 中生产依赖使用精确版本号(1.2.3),禁止使用 ^ 或 ~ 前缀,避免非预期的小版本升级。
{
  "dependencies": {
    "react": "18.2.0",
    "vue": "3.3.4"
  },
  "devDependencies": {
    "typescript": "5.1.6",
    "eslint": "8.45.0"
  }
}
  • Requirement:运行时依赖放入 dependencies,开发依赖(构建、测试、lint)放入 devDependencies,禁止混淆。
  • Caution:引入新依赖前必须评估包大小、维护状态、许可证,禁止引入无人维护或体积过大的包。
  • Standard:定期执行 npm audit 或 pnpm audit 检查安全漏洞,高危漏洞必须在 7 天内修复。
  • Suggestion:使用 depcheck 检测未使用的依赖,及时清理。
  • Standard:团队统一使用一种包管理器,优先 pnpm,在 package.json 中通过 packageManager 字段声明。
{
  "packageManager": "pnpm@8.6.12"
}
反模式问题建议
^1.2.3 宽松版本号非预期升级可能引入 bug使用精确版本号
不提交 lock 文件成员依赖版本不一致提交 lock 文件
生产依赖放 devDependencies构建产物缺失依赖正确分类依赖
重复实现已有工具函数代码冗余优先使用 lodash 等成熟库

  • Standard:导入语句按以下顺序排列,每组之间空一行:
    1. 第三方库(react、vue、antd 等)
    2. 项目内部模块(@/components、@/utils 等)
    3. 相对路径模块(./、../)
    4. 样式文件(.css、.scss)
import { useState, useEffect } from 'react';
import { Button, Modal } from 'antd';
 
import { useUserStore } from '@/store/user';
import { fetchUserList } from '@/api/user';
 
import { formatDate } from './utils';
import styles from './UserList.module.css';
  • Requirement:单个文件只承担一种职责,组件文件不混入接口请求逻辑,工具函数不混入组件定义。
  • Standard:单个文件不超过 400 行,超过时按职责拆分。
  • Standard:优先使用命名导出,默认导出仅用于页面组件和路由组件。
// 正确:命名导出
export function UserTable() { ... }
export interface UserTableProps { ... }
 
// 正确:默认导出用于页面
export default function UserListPage() { ... }
 
// 错误:工具函数默认导出
export default function formatDate() { ... }
  • Standard:全局共享的类型定义放在 src/types/,组件专有类型定义在组件文件顶部或同目录的 types.ts。
  • Requirement:禁止使用 any,如确实需要使用 unknown 并做类型收窄。
反模式问题建议
单文件超过 500 行难以阅读和维护按职责拆分
组件内直接写请求逻辑耦合、难以测试抽取到 api/ 层
到处使用 any失去类型保护使用具体类型或 unknown
导入顺序混乱可读性差按规则排列并启用 import-sort

  • Requirement:项目必须配置 ESLint + Prettier + TypeScript,且 CI 中强制校验,未通过的代码不得合并。
工具用途配置文件
ESLint代码质量检查.eslintrc.cjs
Prettier代码格式化.prettierrc
StylelintCSS 规范检查.stylelintrc
HuskyGit Hooks.husky/
lint-staged增量检查package.json
// .eslintrc.cjs
module.exports = {
  root: true,
  env: { browser: true, es2022: true, node: true },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react-hooks/recommended',
    'prettier',
  ],
  parser: '@typescript-eslint/parser',
  rules: {
    '@typescript-eslint/no-explicit-any': 'error',
    '@typescript-eslint/no-unused-vars': 'warn',
    'react-hooks/rules-of-hooks': 'error',
    'react-hooks/exhaustive-deps': 'warn',
  },
  ignorePatterns: ['dist', 'node_modules'],
};
// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100,
  "arrowParens": "always"
}
  • Requirement:配置 pre-commit hook 执行 lint-staged,对暂存文件运行 ESLint 和 Prettier。
  • Requirement:配置 commit-msg hook 校验提交信息是否符合 Conventional Commits 规范。
// package.json
{
  "scripts": {
    "lint": "eslint src --ext .ts,.tsx",
    "format": "prettier --write \"src/**/*.{ts,tsx,css,scss}\""
  },
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.{css,scss}": ["stylelint --fix", "prettier --write"]
  }
}
  • Requirement:CI 流水线必须包含 lint、type-check、build 步骤,全部通过后才能合并。
# .github/workflows/ci.yml
steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-node@v4
    with:
      node-version: 18
      cache: pnpm
  - run: pnpm install --frozen-lockfile
  - run: pnpm lint
  - run: pnpm type-check
  - run: pnpm build