前端开发检查清单

提交前自检,CR 时对齐标准 —— 不是代码规范,但会影响你的编码习惯。

如何使用

在 Code Review 新同学的代码时,有很多知识和细节需要反复解释。这份清单用于提交前自检,也用于 Review 时对齐标准。

  • 提交 PR 前按清单完成一次自检
  • CR 时用清单项说明问题,而不是只给出个人偏好
  • 清单不是机械打分标准,优先关注业务边界、异常处理、可维护性和用户体验
  • 确实需要违反某条规则时,应在 PR 描述中说明原因、影响范围和兜底方案

这份清单也是一个 AI 编程助手 Skill

已打包为 Claude Code / Kiro / Cursor / Codex 的 Skill。选择工具和安装范围,复制命令执行即可;脚本内含校验、失败回滚、临时文件自动清理,不污染你的环境。

安装完成后在对话里说 /frontend-checklist 或 "按前端清单 review 这段代码" 触发审查。仅在显式请求时运行,不打扰日常写代码。

查看 Release · 审查安装脚本 · 查看 Skill 源文件

Skill 实战案例

下面的 review 报告由已安装的 Skill 对真实坏代码执行产出。这里放一个最有代表性的案例,完整案例库见独立页面。

查看完整案例库 →

🏷️命名规范

  • 变量缩写必须得到团队认可,否则使用完整单词
  • 常量全大写,单词间用下划线分隔,语义完整清晰
  • 函数命名用 动词 + 名词,如 sendSms;模板/JSX 中用 handle + 名词 + 动词,如 handleSmsSend
  • 善用 forbyfromwhenthen 等词表达函数意图

🧩数据与类型

  • 在数据边界处(接口响应、用户输入、路由参数、本地缓存、第三方 SDK 回调)检查数据是否存在及类型
  • 减少不必要的类型默认转换与强制转换,优先分析数据为何不是预期类型
  • 区分"缺失""为空""为 0""为空数组""为空字符串"的业务含义
  • 布尔运算中明确 false0'' 是否与 nullundefined 语义相同
  • 使用 TypeScript 时避免 any,合理定义类型和接口
  • 优先用 interface 描述对象结构,type 用于联合类型等复合场景
  • 函数参数和返回值都应有明确类型,避免隐式 any
  • 用联合类型、泛型、类型守卫表达真实约束,而不是绕过类型系统
  • 在最小范围内声明数据对象

🧠函数设计

  • 宽入严出:对接受的数据宽容,对输出的数据严格
  • 持续拆分函数,直到每个函数只做一件事、能用一句话描述
  • 函数返回值清晰统一,不要在同一函数中混用同步和异步结果
  • 参数超过 3 个时改用对象传参
  • 避免布尔参数,需要时用对象传参
  • 钩子函数中只写调用,不写具体逻辑
  • 不要轻易封装重复的业务代码,可能只是巧合而非真正重复

🗂️状态管理

  • 区分服务端状态和客户端状态,避免把接口数据无意义地复制到多个状态中
  • 不存储可以稳定计算得到的派生状态
  • 状态更新保持不可变,避免直接修改已有对象或数组
  • 状态的来源、更新路径和消费范围要清晰,避免跨组件隐式共享
  • 复杂状态优先集中到 reducer、状态机或专门的状态管理方案中

🔀控制流

  • 优先处理异常情况,再处理正常业务逻辑
  • 条件判断和循环最多三层
  • 判断顺序:先大范围(权限)再小范围(数据权限、业务判断)
  • 复杂布尔运算加括号,方便他人阅读
  • ifelseforwhiledoswitchtrycatchfinally 的执行体无论多少行都加 { }
  • 注意差 1 错误,时刻区分 <=<
  • 多用可提前终止的循环:someeveryfind(注意空数组时 some 返回 falseevery 返回 true

异步处理

  • 不在同一语句中混用同步数据和异步数据
  • 异步处理要考虑异常;Promise 必须通过 resolvereject 进入下一状态,避免卡死
  • 不用 setTimeout 解决异步问题,这会制造难以复现的 bug

📡数据请求

  • 每个请求都应考虑加载中、空数据、失败、重试和取消场景
  • 避免重复请求,尤其是组件重复渲染、路由切换、依赖变化导致的请求放大
  • 处理并发请求的竞态问题,避免旧响应覆盖新状态
  • 明确接口错误和业务错误的处理方式,不要只判断 HTTP 状态码
  • 接口响应进入页面前完成必要的归一化、兜底和字段兼容
  • 写操作成功后应使相关查询缓存失效,避免读到过期数据
  • 分页、无限滚动场景需处理重复数据、翻页竞态和边界去重

🖼️UI 与渲染

  • 轻易不要手动操作 DOM
  • 尽量不使用行内样式,即使通过 props 传递,也应在一定范围内传递 class
  • 操作数组不需要返回值时用 forEach,而不是 map
  • 不在渲染函数中修改数据
  • 不在渲染函数中创建随机值(Math.random()Date.now()
  • 不在渲染函数中发出网络请求
  • 不在渲染中创建新组件,否则 React 会反复销毁并重建子组件树
  • 列表渲染 key 使用稳定唯一 id,不用数组下标或随机值
  • 同一表单/输入控件不要在受控与非受控之间切换
  • useEffect / watch 的依赖项必须完整,避免闭包捕获过期值
  • 属性每个单独一行,方便阅读

🎨样式与响应式

  • 页面需覆盖常见移动端、平板和桌面尺寸
  • 长文本、超大数字、空数据、极端数据不能破坏布局
  • 样式优先使用设计变量或统一约定,避免散落的魔法颜色和尺寸
  • 交互状态需覆盖 hover、focus、active、disabled、loading
  • 组件应避免依赖外层页面的偶然样式

🔐路由与权限

  • 页面权限、操作权限和数据权限需分别考虑
  • 直接访问 URL、刷新页面、登录过期、权限变化时都应有合理兜底
  • 路由参数需要校验,不要默认认为参数一定合法
  • 未授权、无数据、资源不存在应展示不同状态

性能

  • 避免大数组进行查询等运算,用 ObjectMapSet 优化
  • 避免大量数据递归
  • 程序要懒惰,不到最后一刻不获取或处理数据
  • 没有明确性能瓶颈时,不做过早优化
  • 图片/资源懒加载,避免首屏加载过多资源
  • 对大组件、重计算、长列表和大型依赖进行按需加载或缓存优化
  • 控制首屏资源体积,关注构建产物、依赖体积和关键路径资源
  • 避免在循环中频繁操作 DOM 或触发重排
  • 自定义字体设置 font-display 策略,避免 FOIT/CLS
  • 第三方脚本延后加载(defer/async/idle),不阻塞首屏

🛡️安全与健壮性

  • 所有用户输入和外部输入都应基于业务规则进行验证
  • 输入框根据业务添加长度、格式、范围、必填等限制
  • HTML 标签和属性操作必须限定/过滤传入变量值,防止 XSS
  • dangerouslySetInnerHTML / v-html 仅用于可信来源,且内容需先经过消毒
  • 跨域写操作考虑 CSRF:使用 SameSite cookie 或 CSRF token
  • target="_blank" 必须配 rel="noopener noreferrer",防止反向 tabnabbing
  • 需要防嵌入的页面应通过 X-Frame-Options 或 CSP frame-ancestors 防点击劫持
  • 不在前端存储敏感信息(token 存 httpOnly cookie,而非 localStorage)
  • 定期检查第三方依赖安全漏洞(npm audit
  • 定时器和事件监听记得清除,否则可能引发内存泄漏
  • 注重事物生命周期:初始化时创建,结束前清理
  • 解决 bug 要分析根本原因,而不只是修复表象
  • 对 API 调用保持敏感,尤其是低频调用变为高频调用的场景

📝表单与交互

  • 表单校验规则应与后端约束保持一致
  • 提交按钮应处理提交中、成功、失败和防重复提交状态
  • 表单错误应定位到具体字段,而不是只给出笼统提示
  • 危险操作需要二次确认或可撤销机制
  • 用户操作后应有明确反馈,避免用户不知道操作是否生效

🚨错误处理

  • 为关键业务模块添加错误边界,避免局部错误导致整个页面崩溃
  • 接入错误上报系统,确保线上异常可被感知和追踪
  • 捕获错误时保留必要上下文:接口、参数、用户操作路径和环境信息
  • 不要吞掉异常,无法处理的错误应继续抛出或进入统一错误处理

🧪测试

  • 核心业务逻辑必须有单元测试覆盖
  • 组件测试关注行为而非实现细节
  • 关键业务流程需要集成测试或端到端测试覆盖
  • 修复 bug 时优先补充能复现该问题的测试
  • 测试需覆盖正常、异常、边界、权限和空数据场景

无障碍访问(a11y)

  • 交互元素(按钮、链接)必须有可读文本或 aria-label
  • 图片必须有 alt 属性
  • 表单控件必须有对应的 label
  • 页面可通过键盘完成核心操作
  • 文本、背景、状态色需满足基本对比度要求
  • 动态内容变化应通知屏幕阅读器(aria-live
  • 模态框打开时焦点应移入,关闭后应还原到触发元素
  • 颜色不能作为传递信息的唯一手段

💡用户体验

  • 提示信息使用陈述句,不加语气助词和网络用语
  • 错误提示应说明发生了什么,以及用户下一步可以做什么
  • 空状态需给出明确含义和可执行的下一步
  • 关键流程避免让用户重复输入已提供过的信息
  • 删除、离开、覆盖、提交等不可逆操作应避免误触

代码质量

  • 避免魔法数字,用命名常量替代
  • PR 提交前自我 review 一遍,不要依赖他人发现低级错误
  • 优先使用 const,直到变量需要改变时再用 let
  • 解构优先用于提升可读性,不为形式牺牲可读性
  • 注释侧重 WHY:数据结构设计 > 不合理的业务逻辑 > 普通函数;不在注释中包含侮辱性词汇
  • 用删除代替注释来精简代码
  • 尽量不使用默认导出
  • 减少不必要的依赖引入,开源库尽量只引入所需部分
  • package.json 依赖项建议锁定版本号,避免使用 ^ ~
  • 对引入的开源工具浅封装一层,降低未来替换成本
  • 不要轻易复制代码;必须复制时手敲一遍,确保每行都理解
  • 使用更新的语言特性提升程序健壮性
  • 单文件行数应有限制(由团队自行定制)
  • 业务功能不要依赖语言特性的隐式行为

🛠️工程化

  • 提交前通过格式化、Lint、类型检查和必要测试
  • 依赖变更需说明原因,并关注体积、安全性、维护状态和许可证
  • 环境变量、构建配置、代理配置不能与本地环境强绑定
  • 新增脚本、配置或目录时,命名应与现有项目结构保持一致
  • 自动化校验尽量在 CI 中完成,避免只依赖人工检查
  • 生产 source map 仅上传至错误监控平台,不随静态资源公开部署
  • .env 等含敏感变量的文件不入库,改由 .env.example 声明所需字段

🌐国际化(i18n)

  • 文案不硬编码,统一走翻译函数
  • 日期、数字、货币格式根据 locale 处理,不手动拼接
  • 避免拼接字符串构造句子,语序因语言而异
  • 图片、图标中不嵌入文字

📈日志与监控

  • 关键业务操作(支付、提交、权限变更)应有埋点
  • 生产环境不输出敏感信息到 console
  • 前端性能指标(LCP、FID、CLS)应可观测
  • 错误上报应区分业务异常和程序异常,便于分类处理
  • 埋点和日志中的手机号、身份证、邮箱等 PII 需脱敏或哈希

📦依赖管理

  • 引入新依赖前先确认项目中是否已有等价能力
  • 避免引入已停止维护的库
  • 注意 license 兼容性(GPL 类库不能随意商用)
  • 大体积依赖优先按需引入,避免全量打包

🌍浏览器兼容

  • 明确目标浏览器矩阵,并在构建工具(browserslisttargets)中同步
  • 新特性先做特性检测('IntersectionObserver' in window),不要做 UA 嗅探
  • 按目标矩阵配置 polyfill,避免全量引入拖慢首屏
  • 使用 CSS 新特性(:has、容器查询、color-mix 等)时考虑降级
  • 核心页面需在目标浏览器上真机或等价环境验证

📚文档与协作

  • 复杂组件/函数应有使用示例
  • 破坏性变更(接口、props、事件名)需在 PR 中明确标注
  • 公共 API 变更需同步通知依赖方

PR 自检

  • 是否处理了加载中、空数据、失败、重试和无权限状态
  • 是否处理了异常输入、边界数据和接口字段缺失
  • 是否存在重复请求、重复渲染、内存泄漏或竞态问题
  • 是否影响已有页面、路由、权限、埋点、接口契约或缓存逻辑
  • 是否补充或更新了必要测试
  • 是否需要更新文档、配置、示例或迁移说明