前端注释规范:如何写“后人能看懂”的注释(附示例)

前端注释规范:如何写"后人能看懂"的注释(附示例)

注释不是为当下的"我",而是为未来的"他(她/它)"。好的注释能让后来者快速建立上下文、理解设计意图、准确扩展与修复。本文给出一套面向真实工程的前端注释规范与可复制的写作模板,并配备 TypeScript、React、CSS 等多场景示例。

目标与原则

  • 面向读者:假设读者对业务陌生但具备工程常识,避免"理所当然"的隐性知识。
  • 解释意图,不复述实现:代码讲"怎么做",注释讲"为什么这么做"和"不能这么做的边界"。
  • 贴边界、讲约束:记录输入输出的契约、边界条件、性能权衡、兼容策略与风险。
  • 可维护:注释与代码保持同等"更新责任";随着逻辑变更同步修订。
  • 可检索、可协作:统一标签与格式,便于搜索与团队协作。

注释分层与适用场景

  • 文件/模块头:背景、职责、关键约束、外部依赖、维护者与变更记录。
  • 类型/接口:字段含义、单位/取值范围、跨模块契约、兼容策略。
  • 函数/方法:意图、参数/返回/异常、复杂度来源、边界与副作用。
  • 复杂逻辑块:算法步骤、性能权衡、临时 Hack 的撤销条件。
  • UI 组件:交互约束、无障碍(a11y)约定、状态来源与共享边界。
  • 样式/CSS:命名约定(BEM/Utility)、层叠/覆盖策略、兼容与降级。

标签与格式约定

  • NOTE:重要说明或上下文补充
  • WARNING:风险提醒或易错点
  • TODO:[Owner][Due][Link] 约定待办的责任与时限
  • FIXME:已知缺陷的定位与修复思路
  • HACK:临时性解决方案及撤销条件
  • DEPRECATED:弃用说明与替代方案

统一形如:

  • TODO(@alice, 2025-01-10, JIRA-123): ...
  • FIXME: ... Root cause ... Workaround ...

JSDoc/TSDoc 推荐字段

  • @param 参数契约与单位
  • @returns 返回约定与可能的空值
  • @throws 异常场景与上游处理建议
  • @example 典型调用示例
  • @remarks 设计/业务背景补充
  • @deprecated 弃用提醒与替代

示例一:业务逻辑函数(含边界与约束)

ts 复制代码
/**
 * 计算订单最终价格(含税与优惠)
 *
 * 意图:
 * - 将多来源优惠与税费规整为统一结算口径,保证后端账务一致性
 *
 * 契约与约束:
 * - 所有金额单位为"分",避免浮点误差
 * - 税费按地区配置的 VAT 率计算;部分品类免税
 * - 折扣先于满减,且折扣后不得低于成本价
 *
 * 边界:
 * - 负数输入直接抛错
 * - 免税地区 taxRate=0
 *
 * @param amount 原价(分)
 * @param rules  优惠与税费规则
 * @returns 最终价格(分)
 * @throws 当 amount < 0 或规则非法时抛出 Error
 * @example
 * calculateFinalPrice(10000, { discount: 0.9, vat: 0.13, minCost: 8000 }) // => 10170
 */
export function calculateFinalPrice(
  amount: number,
  rules: {
    discount?: number; // 0-1,缺省视为不打折
    vat?: number;      // 0-1,地区增值税率
    minCost: number;   // 成本价(分)
    category?: 'general' | 'food' | 'book';
  }
): number {
  if (amount < 0) throw new Error('amount must be non-negative');

  const d = rules.discount ?? 1;
  const discounted = Math.max(Math.round(amount * d), rules.minCost);

  // 免税品类(示例:book)
  const vatRate = rules.category === 'book' ? 0 : (rules.vat ?? 0);

  const tax = Math.round(discounted * vatRate);
  return discounted + tax;
}

要点:

  • 明确单位与顺序(折扣→满减→税费)避免歧义。
  • 把"为什么"和"不能怎么做"写清楚,如成本价约束。

示例二:React 组件(交互与 a11y)

tsx 复制代码
/**
 * DebouncedInput:带防抖的文本输入框
 *
 * 意图:
 * - 在频繁输入场景降低后端压力,同时保证用户反馈流畅
 *
 * 交互约束:
 * - 按 Enter 立即触发(绕过防抖),满足可用性期望
 * - Esc 清空输入但不触发 onChange
 *
 * a11y:
 * - 提供 aria-label 或显式 <label htmlFor> 绑定
 * - 按 Enter 的即时提交行为需告知屏幕阅读器
 */
export function DebouncedInput({
  value,
  onChange,
  delay = 300,
  ariaLabel,
}: {
  value: string;
  onChange: (v: string) => void;
  delay?: number;
  ariaLabel?: string;
}) {
  const [inner, setInner] = useState(value);

  useEffect(() => setInner(value), [value]);

  useEffect(() => {
    const t = setTimeout(() => {
      if (inner !== value) onChange(inner);
    }, delay);
    return () => clearTimeout(t);
  }, [inner, delay]);

  return (
    <input
      aria-label={ariaLabel}
      value={inner}
      onChange={(e) => setInner(e.target.value)}
      onKeyDown={(e) => {
        if (e.key === 'Enter') onChange(inner); // 即时触发
        if (e.key === 'Escape') setInner('');   // 清空但不触发
      }}
    />
  );
}

要点:

  • 把"绕过防抖"的意图写明,避免误改。
  • 交互与无障碍约束要可检索。

示例三:异步与重试(取消/退避)

ts 复制代码
/**
 * fetchWithRetry:带指数退避与可取消的请求
 *
 * 设计权衡:
 * - 避免雪崩:指数退避 + 抖动(jitter)
 * - 保持可响应:支持 AbortController 取消
 *
 * 风险与约束:
 * - 仅对网络/5xx 重试,4xx 视为业务失败不重试
 * - 最大重试次数可配置
 *
 * TODO(@owner, 2025-01-31, TICKET-456): 采集重试遥测并接入告警
 */
export async function fetchWithRetry(
  input: RequestInfo,
  init: RequestInit & { retries?: number; baseDelay?: number },
  signal?: AbortSignal
): Promise<Response> {
  const retries = init.retries ?? 3;
  const base = init.baseDelay ?? 200;

  for (let i = 0; i <= retries; i++) {
    try {
      const res = await fetch(input, { ...init, signal });
      if (!res.ok && res.status < 500) return res; // 4xx 不重试
      if (res.ok) return res;
      // 5xx 将进入重试
    } catch (e) {
      // 网络错误继续重试
      if (signal?.aborted) throw e;
    }
    const jitter = Math.random() * 0.2 + 0.9; // 0.9~1.1
    const delay = Math.round(base * Math.pow(2, i) * jitter);
    await new Promise((r) => setTimeout(r, delay));
  }
  throw new Error('Max retries reached');
}

要点:

  • 写清"哪些错误重试",避免误重试导致业务雪崩。
  • 将遥测/告警作为 TODO,明确责任与时限。

示例四:CSS 命名与兼容策略

css 复制代码
/* Header 导航
 * 命名:BEM(.header__nav, .header__item)
 * 层叠策略:工具类优先(.u-hidden),避免 !important
 * 兼容:老版 iOS 不支持 position: sticky,降级为 static
 */

.header {
  position: sticky; /* NOTE: iOS < 12 降级 */
  top: 0;
  z-index: 100;
  background: var(--color-bg);
}

.header__nav {
  display: grid;
  grid-template-columns: repeat(4, minmax(80px, 1fr)); /* WARNING: 列宽低于 80px 会影响可点击区域 */
  gap: 8px;
}

.u-hidden {
  display: none;
}

要点:

  • 命名约定、层叠策略与兼容/降级清晰可见。
  • 用 NOTE/WARNING 提醒关键风险。

示例五:模块头部模板(可复制)

ts 复制代码
/**
 * 模块:优惠结算引擎(Pricing Engine)
 *
 * 背景:
 * - 多渠道优惠并行叠加导致口径不一,需统一规则避免账务对不上
 *
 * 职责:
 * - 计算折扣、满减、税费,并保证不低于成本价
 *
 * 外部依赖:
 * - config/vat.ts(地区 VAT 配置)
 * - services/catalog.ts(品类属性)
 *
 * 关键约束:
 * - 金额单位统一为"分"
 * - 折扣先于满减;免税品类 VAT=0
 *
 * 维护者:@alice(主)、@bob(备)
 * 变更记录:
 * - 2025-11-12:引入免税策略并校正边界
 */

写作模板(三段式)

  • 背景:这段代码存在的业务/技术上下文是什么?
  • 意图:期望达成的目标、选择的策略与不选的理由。
  • 要点:契约/边界、副作用、风险与后续工作。

示例片段:

ts 复制代码
// 背景:移动端输入频繁导致后端压力高
// 意图:防抖 + Enter 直发,兼顾性能与体验
// 要点:Esc 清空不触发;a11y 需有 label

不该写的注释(反例)

  • 重复代码含义:"i++ 自增 1"这类废话。
  • 时间线式注释:"2023-01-01 修复 bug"请放到版本/变更日志或模块头。
  • 永久 TODO:没有责任人/时限/链接的 TODO 会烂尾。
  • 过时注释:与代码不一致的注释比没有更糟。
  • 评注式注释:"这代码太丑"------没有行动信息的情绪表达。

改写示例:

  • 坏注释:// 这里做了折扣计算
  • 好注释:// 折扣先于满减;折后不得低于成本价(防负毛利)

团队协作与维护建议

  • 代码评审包含"注释检查":是否解释了意图与边界?是否落了标签?
  • 变更驱动更新:任何影响契约/边界的改动必须同步修订注释。
  • 定期清理:搜索 TODO|FIXME|HACK|DEPRECATED,核对是否过期。
  • 本地化策略:中文为主,保留关键英文术语(如 debounce、backoff、a11y)。

快速清单(可打印)

  • 是否解释了"为什么"和"不能怎么做"?
  • 是否明确了单位、范围、顺序与副作用?
  • 是否标注风险、兼容策略与撤销条件?
  • 是否使用统一标签并附责任人/时限/链接?
  • 是否与最新代码一致,且可被搜索到?

按此规范写注释,后人不必"读心术",你也能在未来的某一天感谢过去的自己。

相关推荐
小飞大王6662 小时前
JavaScript基础知识总结(四):常见内置构造函数,正则表达式,作用域与闭包
前端·javascript·正则表达式
清凉夏日2 小时前
Flutter 国际化完整指南
前端·flutter
Jony_2 小时前
动态代理机制
前端
掘金一周2 小时前
重新思考 weapp-tailwindcss 的未来 | 掘金一周 11.13
前端·人工智能·后端
Pu_Nine_92 小时前
Vue 3 项目 ESLint 配置详解:初始模板的正确配置
前端·javascript·vue.js
Jolyne_2 小时前
【浏览器插件】一键下载页面图片和文本
前端
Jony_2 小时前
Android 类加载机制
前端·客户端
1024小神3 小时前
在 Swift 中,参数标签(argument label),用于在调用函数时提高代码的可读性。
前端
joker学java3 小时前
el表达式jstl和我们的js都是什么时候使用
前端