简介:《前端开发规范手册》是一份面向互联网行业前端开发者的团队协作与代码质量参考文档,适合初中级开发者或需要统一编码规范的研发团队。手册覆盖HTML、CSS、JavaScript三大核心,强调结构、样式、行为分离,统一使用UTF-8编码与两个空格缩进;HTML部分关注语义化标签和注释,CSS部分强调模块组织、BEM命名及Less规范,JavaScript部分则建议遵循ESLint或Airbnb风格,并给出jQuery使用建议。性能优化与移动端适配单独成章,包括内联关键CSS、延迟加载非关键JavaScript、压缩合并、缓存利用、媒体查询以及Autoprefixer/PostCSS等工具。压缩包内为单个PDF文件,大小约1.45MB,便于下载后离线查阅或分发给团队成员。目前已有196人学习下载;文档由书栈社区整理,章节结构清晰,读者可据此快速建立规范意识,并在实际项目中灵活应用、定期更新。
1. 前端开发规范手册:为什么你们团队越写越乱
每次接手别人写的页面,打开源码的那一刻是不是都想直接按 Ctrl+A 删掉重来?没有规范的项目,三个月后连原作者自己都看不懂自己写的代码。前端开发规范手册不是给老板看的装饰品,也不是挂在 Wiki 里吃灰的文档,而是团队协作的底线契约——它决定了一个新人接手项目需要三天还是三周,决定了一次需求迭代要改 5 个文件还是 50 个文件,决定了线上出问题时定位 Bug 要花十分钟还是三个小时。
这篇文章不打算给你一份现成的手册抄,而是把搭建这套规范体系的全过程拆开:从风格约束、目录结构、组件设计到 Git 提交、自动化校验、Code Review 落地,每一层都给出可以直接抄走的配置和参数,以及我在真实项目里踩过的坑。你缺的不是规范本身,而是一条让团队真的愿意照着做的落地路径。
2. 规范手册的骨架:先定风格、命名与目录结构
规范手册最容易犯的错是一上来就写"变量名要有意义""代码要整洁",这种废话写满十页也没人看。真正能落地的规范,第一层必须解决三个最表面但也最磨人的问题:代码长什么样、文件叫什么名、东西放在哪里。
2.1 命名规范:从文件名到组件名的完整约束
命名是规范里最难执行的部分,因为 ESLint 只能管到变量名,管不到文件名。我在手册里先把命名规则按对象拆成四类,每一类都给出正反例,而不是只写一句"用驼峰命名"。
| 对象 | 规则 | 好例子 | 坏例子 |
|---|---|---|---|
| 文件/目录 | kebab-case,全小写连字符 | user-profile-card.tsx | UserProfileCard.tsx(用于组件文件时除外) |
| 组件名 | PascalCase,与文件名一致 | UserProfileCard | userProfileCard |
| 变量/函数 | camelCase,动词开头 | getUserList/isLoading | UserList/loading |
| 常量/枚举 | UPPER_SNAKE_CASE | MAX_RETRY_COUNT | maxRetryCount |
组件文件到底用 PascalCase 还是 kebab-case,这个争论不用写进手册。我一般建议:组件文件.tsx用 PascalCase 方便 IDE 自动导入识别,非组件文件(hooks、utils、常量)全用 kebab-case。这个规则要在手册里白纸黑字定死,否则一半人用驼峰一半人用连字符,Git 提交记录里全是改名 commit。
2.2 目录结构:按功能分还是按路由分
目录结构是规范手册里的第二层。按文件类型分(components/、utils/、hooks/ 全堆在 src 下)是新手团队的做法,项目一大人就傻了——改一个订单功能要在五个目录间跳来跳去。我建议的常见做法是按业务模块划分,配合 typescript 路径别名:
src/ ├── api/ # 接口请求层,按模块拆文件 ├── components/ # 全局共享组件(跨模块复用) ├── features/ # 按业务模块划分 │ ├── order/ # 订单模块 │ │ ├── components/ # 模块内私有组件 │ │ ├── hooks/ │ │ ├── pages/ │ │ └── utils/ │ └── user/ ├── hooks/ # 全局共享 hooks ├── layouts/ # 布局组件 ├── routes/ # 路由配置 ├── stores/ # 全局状态 └── types/ # 全局类型定义这个结构的关键参数是features目录的边界:什么组件放components什么放features/xxx/components?规则是"被两个以上模块引用的才上提全局",只被一个模块用的打死不放全局。这个约束必须写进手册,不然过两个月components目录膨胀到上百个文件,谁也说不清哪个是公用的。
2.3 组件设计规范:Props 的命名与默认值约定
第三层是组件设计约定。这一层最容易被忽略,但它决定了团队能否沉淀出像样的组件库。我在规范里只定四条硬规则:
Props 统一用 camelCase,事件回调统一on开头,布尔值统一is/has/can开头(例如isVisible而不是visible);Props 必须写类型不能写any;有默认值的 Props 要配合defaultProps或解构默认值;每个组件文件只能默认导出一个组件,命名导出留给工具函数和类型。
// 反例:内部状态与外露 Props 混在一起 const Modal = (props: any) => { const [visible, setVisible] = useState(false) return props.visible ? <div>{props.content}</div> : null } // 正例:Props 自解释,语义可控 interface ModalProps { isVisible: boolean title: string width?: number onClose: () => void } const Modal = ({ isVisible, title, width = 480, onClose }: ModalProps) => { if (!isVisible) return null return ( <div className="modal" style={{ width }}> <h3>{title}</h3> <button onClick={onClose}>关闭</button> </div> ) }上面这两段代码要放进手册里当"标准范式",比写十句"Props 要有意义"管用。后端同学接手前端也能看懂什么是对的样子。
3. 用 ESLint + Prettier 把规范变成强制:最小可运行配置
规范手册写完只是第一步,如果靠人肉 review 来执行,撑不过两周。真正让规范落地的是自动化工具链:ESLint 管代码质量规则、Prettier 管格式统一、Stylelint 管样式、Husky 在提交前拦截。这一步不做,手册就是摆设。
3.1 ESLint 配置:从 0 到 1 搭起来
在 Vite 或 Create React App 项目里,ESLint 一般已经预装了,但默认配置太宽松。我一般会在根目录新建eslint.config.js(ESLint 9 用 flat config)或者在.eslintrc.cjs里覆盖规则。下面这套是 React + TypeScript 项目的最小可用配置:
// eslint.config.js import js from '@eslint/js' import tseslint from 'typescript-eslint' import reactHooks from 'eslint-plugin-react-hooks' import reactRefresh from 'eslint-plugin-react-refresh' export default [ js.configs.recommended, ...tseslint.configs.recommended, { files: ['**/*.{ts,tsx}'], plugins: { 'react-hooks': reactHooks, 'react-refresh': reactRefresh, }, rules: { ...reactHooks.configs.recommended.rules, '@typescript-eslint/no-explicit-any': 'off', // 团队能驾驭 any 就先关掉 'no-console': ['warn', { allow: ['warn', 'error'] }], '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], }, }, { ignores: ['node_modules/**', 'dist/**', 'build/**'] }, ]这里几个参数值得展开。no-console设成warn而不是error,因为很多后端接口联调时输出关键日志是刚需,一棍子打死会逼着人写// eslint-disable-next-line。argsIgnorePattern: '^_'允许_event这种占位参数存在,否则写事件处理函数时经常被未使用参数报错。no-explicit-any关掉是因为存量代码里的 any 太多,一上来开error会让 eslint 直接跑不过,团队会产生抵触情绪。
3.2 Prettier 与 ESLint 的配合:先格式化再校验
格式问题(缩进、引号、分号)ESLint 也能管,但性能和覆盖面不如 Prettier。我的搭配原则是:Prettier 负责所有跟"长得好不好看"有关的规则,ESLint 只负责"写得对不对"的质量规则。两者在rules里有重叠的地方,必须通过配置让它们各管一边,否则会出现"ESLint 说这里该加分号,Prettier 保存时又删掉了"的翻车场景。
{ "semi": false, "singleQuote": true, "trailingComma": "es5", "printWidth": 100, "tabWidth": 2, "endOfLine": "lf" }这份.prettierrc.json是团队里最常见的配置参数。semi: false关掉分号,singleQuote: true用单引号,printWidth: 100一行最长 100 字符,endOfLine: "lf"强制换行符统一——这一个参数能原地解决 Windows 和 Mac 协作时整个文件被标红的经典血泪问题。
3.3 Husky + lint-staged:提交前的最后一道闸
规范和工具都配好了,但如果不做提交拦截,总有混子直接git commit跳过检查。这里用 Husky 在 Git 钩子触发 lint-staged,只检查本次改动的文件,避免检查全量代码导致提交慢到爆。
// package.json 中的 scripts 与依赖(示意) { "scripts": { "lint": "eslint . --ext .ts,.tsx", "lint:fix": "eslint . --ext .ts,.tsx --fix", "format": "prettier --write \"src/**/*.{ts,tsx,css,scss}\"" }, "lint-staged": { "*.{ts,tsx}": ["eslint --fix", "prettier --write"], "*.{css,scss}": ["stylelint --fix", "prettier --write"] }, "devDependencies": { "husky": "^9.0.0", "lint-staged": "^15.0.0" } }# 初始化 husky 并写入 pre-commit 钩子 npx husky init cat > .husky/pre-commit << 'EOF' npx lint-staged EOFlint-staged的关键参数是文件 glob 和命令数组的顺序:先eslint --fix再prettier --write。顺序反了会导致格式化后的代码再次触发 ESLint 报错,钩子永远跑不过。Husky 9 的husky init会自动生成.husky/目录,注意这个命令要求在 Git 仓库根目录执行,否则钩子不会被激活。
4. Git 提交规范与 Code Review 检查清单:让规范跑进流程
代码风格只是规范的表层,更深一层是协作流程。提交信息乱写、PR 没人 review、分支命名随心所欲,这些问题比代码风格更伤项目。规范手册里必须有专门一章讲 Git 工作流,否则自动化工具再强也管不了人。
4.1 Conventional Commits 约定式提交:为什么提交信息要有格式
我在规范里直接采用 Conventional Commits 标准,并用 commitlint 强制校验。每条提交信息要求按type(scope): subject的格式,常见的 type 就六种:feat(新功能)、fix(修 Bug)、docs(文档)、style(格式)、refactor(重构)、chore(杂务)。scope 写模块名,比如feat(order): 新增订单批量导出。
这个约定的价值不在提交信息本身好看,而是它让后续的自动化全部跑得起来——自动生成 CHANGELOG、根据提交类型自动决定发 minor 还是 patch 版本、CI 里判断这次改动要不要跑对应模块的测试。没有格式统一的提交信息,这些全部免谈。
// commitlint.config.js module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']], 'subject-empty': [2, 'never'], 'type-empty': [2, 'never'], }, }# 把 commitlint 接到 commit-msg 钩子上 cat > .husky/commit-msg << 'EOF' npx --no -- commitlint --edit "$1" EOF参数说明:type-enum后面的[2, 'always', [...]]三个值分别代表错误级别(2 是 error)、触发条件(always 表示总是应用规则)、允许的值列表。如果团队有人非要加build或perf类型的提交,直接把值加进数组即可,别让 commitlint 版本升级把你自定义的类型冲掉了。
4.2 分支命名与 PR 模板:小项目也值得定的规矩
分支命名规范我一般定得很轻:feature/xxx、bugfix/xxx、release/xxx,后面接需求编号(如果用的是 Jira、TAPD 或禅道)。这条规则的意义在于,后端发版或运维部署时看到分支名就能判断该不该合入这个环境,省去翻聊天记录的时间。
PR/MR 模板是规范手册里另一个高性价比模块。我见过太多 PR 描述只写"改了点样式",review 的人要打开 diff 一行行猜意图。模板不搞复杂,四个段落就够了:
## 需求背景 (这个改动解决什么问题,关联哪个需求的编号) ## 改动清单 - [ ] 新增/修改了哪些文件 - [ ] 是否新增依赖 - [ ] 是否需要迁移数据 ## 自测情况 (本地跑过哪些用例,有没有手动验证边界情况) ## 需要 review 关注的重点 (哪里容易出问题,比如状态更新逻辑、异步竞态)4.3 Code Review 检查清单:从"看心情"到"照单抓药"
Code Review 是最容易被跳过也最值得用规范约束的环节。我在团队里推的不是"必须怎么 review",而是一张 15 分钟能过完的检查清单,要求至少检查下面五项:
| 检查项 | 具体关注点 | 判定标准 |
|---|---|---|
| 类型安全 | 有没有any、有没有不必要的类型断言 | 类型能推导就不写死 |
| 状态管理 | 该用服务端状态还是本地状态、有没有过度全局化 | 只在跨组件共享时才进全局 store |
| 异步链路 | 有没有竞态、有没有 catch 丢失、取消请求是否处理 | 接口返回顺序不确定时要以最新请求为准 |
| 样式隐患 | 有没有硬编码颜色/尺寸、响应式断点有没有写死 | 设计变量要先抽成 token |
| 边界处理 | 空数组、超长文本、网络超时、用户重复点击 | 每个列表页都要有 empty 态 |
这张清单要放进手册的附录。review 不是找茬大赛,是为了拦住低级事故。我在清单开头就注明:粘贴复制的代码、注释掉的死代码、魔法数字,这三点看到当场打回,其他问题可以在评论区讨论后合入。
5. 避坑手册:规范落地时最容易翻车的五个环节
讲完了怎么建规范,得讲讲最现实的翻车经验。我做过三个团队的规范落地,几乎每个项目都从同一个坑里爬出来过。这些踩坑记录,比规范正文更值得写进团队文档。
5.1 坑一:ESLint 和 Prettier 规则互相打架
现象:保存文件时 Prettier 自动格式化和 ESLint --fix 的结果不一致,改完这边的报错那边又亮起来,两个工具来回拉锯。
原因:ESLint 里带格式类的规则(比如indent、quotes、semi)和 Prettier 的规则冲突了。ESLint 默认推荐的规则集里就包含这些格式规则,而 Prettier 的理念是所有格式问题它来管。
解决:把 ESLint 里所有跟代码格式相关的规则全部关掉,或者直接引入eslint-config-prettier把冲突规则集体禁用。这是标准解法,不要在.prettierrc里通过改参数去凑 ESLint 的规则,那是死路。顺手把前端开发规范里的"格式问题以 Prettier 为准,逻辑问题以 ESLint 为准"这句话写进文档,从此两个工具各管一段,再没有冲突。
5.2 坑二:Husky 装了但钩子不生效
现象:npx husky init成功,.husky/pre-commit文件也在,但 commit 时钩子就是不跑,代码带着 lint 错误照样提交上去。
原因:Husky 9 在大版本升级后初始化方式有变化。最常见的原因是npx husky init执行时当前目录不在 Git 仓库根目录,或者项目用了 monorepo 结构,.husky目录放在了子包里面。
解决:确认.husky目录在 Git 仓库根目录,然后重新执行npx husky init再写入钩子。如果还不行,检查一下core.hooksPath是否被其他工具改过——执行git config core.hooksPath,如果不是.husky就手动改回来:
git config core.hooksPath .husky这是我们团队真实遇到过的情况:某个成员装过别的 Git 钩子管理工具,把hooksPath指到了别的目录,Husky 的钩子全被架空。这种黑匣子问题靠看文档看不出来,得一个个查配置项。
5.3 坑三:规范定得太严,团队选择沉默式对抗
现象:规则从warn直接升到error,@typescript-eslint/no-non-null-assertion、no-any全部拉满,结果项目里出现几百个// eslint-disable-next-line注释,代码里全是 "不要检查" 的标记。
原因:存量代码的债务不会因为新规范自动消失。一上来就开最严格模式,老代码必然成片报错,团队成员的选择是批量贴 disable 注释而不是改代码,因为改代码风险大、时间不够。
解决:新规范落地初期把报错级别设为warn,然后规定"新增代码不允许用 disable 注释,存量代码分期整改"。我在规范手册里专门有一条:每天或者每个迭代选一个目录做存量清理,每次提交顺手修几处,三个月后这批债就清完了。规范是要追的,不是一步到位的,这个预期管理得做在前面。
5.4 坑四:规范手册躺在 Wiki 里吃灰
现象:手册写得很详尽,但团队成员根本不看。新同事入职第一周没人提到这份文档,代码该怎么乱还是怎么乱,规范手册变成了一份"存在但没人承认它存在"的文件。
原因:规范没有嵌入到任何强制环节里。人只有在被拦下来的时候才会去翻规则,如果代码提交不经过任何检查,规范手册就是一个免费的知识库,不承担任何纪律功能。
解决:把规范手册的检查项拆解成可执行的自动化。说得冷酷一点:凡是能被工具检查的规范,绝不能依赖人的阅读和记忆。格式交给 Prettier、命名用 ESLint 的命名规则插件、提交信息交给 commitlint、目录结构靠 Code Review 时对照手册的目录树。人手检查的范围压缩到"工具管不了的地方"(比如业务逻辑的边界处理、状态管理的合理性),这样手册才会被当作权威而不是摆设。
5.5 坑五:目录结构调整引起的大面积 import 路径报错
现象:按规范重排目录结构后,几十个文件的 import 路径全部失效,TypeScript 编译直接红灯,团队花了一整天专门改引用路径。
原因:目录结构调整没有配合路径别名和批量工具。纯手工改路径,必然出错。
解决:如果用的 Vite,先配置好了@指向src,然后全量替换就能少改一半路径。更彻底的做法是目录重排直接用 codemod 脚本处理 import 声明,别用编辑器一个个点。规范落地本身应该是一次技术活,不应该靠手工作业来刷存在感。
6. 让规范持续演进:版本化手册与自动化检查入口
规范手册最忌讳"写完定稿、从此冻结"。前端生态变化太快,团队也在变,规范必须跟着演进,但演进要有机制,不能谁想加一条就加一条。
我现在维护规范手册的做法是把它放进 Git 仓库,作为docs/frontend-convention.md管理,任何变更走正常的 PR 流程修改并记录 changelog。每半年发起一次全员评审,每个人可以提新增条目或删除过时条目,但新增条目必须附上"为什么需要"的具体案例(比如某个线上事故促使这条规范诞生),否则不讨论直接驳回。这种机制保证了规范是有生命力的,而不是一份压箱底的死文档。
另外把规范检查入口做成一条命令是很有价值的投资,比让每个人都去翻文档靠谱十倍。常见做法是在package.json里加一个npm run check,把 eslint、prettier、stylelint、tsc 的检查串成一行:
{ "scripts": { "check": "npm run lint && npm run format:check && tsc --noEmit", "format:check": "prettier --check \"src/**/*.{ts,tsx,css,scss}\"" } }prettier --check不修改文件,只报告哪些文件不符合格式,适合在 CI 里用。tsc --noEmit做类型检查,这条往往被忽略但其实非常关键——很多 ESLint 查不出来的类型问题,TypeScript 编译器能一枪毙命。
我自己的习惯是每次提交前先跑一遍npm run check,等它报错、修复、再跑,直到绿了才 commit。这已经成了肌肉记忆。回头看,团队里能坚持这个动作的人,代码 review 被驳回的次数明显少一截。规范的意义不在于让代码变成同一个模子刻出来的,而是让每个人不用在"代码怎么组织"上消耗脑力,把有限的注意力留给真正有挑战的逻辑。希望这份落地方案对你有用,把团队代码从"能跑"推进到"好改"的程度。
本文还有配套的精品资源,点击获取