
一套真正能落地的前端代码注释规范从 Vue 项目实战出发告诉你注释该写什么、不该写什么避开常见坑点写出让 3 年后的自己还能看懂的可维护代码。 文章目录一、第一原则好代码胜过好注释但没有注释也不一定是好代码1.1 一句话核心原则二、注释的四大黄金场景该写什么2.1 解释“为什么这么写”Why而不是“代码在干嘛”What2.2 标记“约定”和“前置条件”别人需要遵守什么2.3 记录“历史遗留”和“坑点说明”这块代码为什么这么丑2.4 对复杂算法 / 业务流程做“概览说明”给后人一张思维导图三、哪些注释是坚决不要写的3.1 重复代码的注释浪费时间 增加维护成本3.2 “心情日志”注释TODO / FIXME 不写清楚内容3.3 和真实逻辑不一致的注释比没有注释更可怕3.4 写在实现细节里的“小说故事”越写越乱四、不同层级怎么写以 Vue 项目为例的一套落地规范4.1 组件层Vue SFC注释重点放在哪里4.2 业务逻辑层hooks / composables / services4.3 工具层utils / helpers何时需要注释五、团队层面的“注释规范建议”可以直接抄到你们 RULE.md 里5.1 总体原则5.2 “必须注释”的场景5.3 “禁止/不鼓励”的注释六、一个完整的小案例从“糟糕注释”到“可维护代码”6.1 初版很多人项目里真实存在的写法6.2 改进版结合命名 注释一起升级七、如何把“注释规范”写成一篇能发 CSDN 的文章八、结语写给 3 年后的自己 系列模块导航同学们好我是 Eugene尤金一名多年中后台前端开发工程师。Eugene 发音 /juːˈdʒiːn/大家怎么顺口怎么叫就好很多前端开发者都会遇到一个瓶颈代码能跑但不够规范功能能实现但维护起来特别痛苦一个人写没问题一到团队协作就各种混乱、踩坑、返工。想写出干净、优雅、可维护的专业代码靠的不是天赋而是体系化的规范 真实实战经验。这一系列《前端规范实战》我会用大白话 真实业务场景不讲玄学、不堆理论只分享能直接落地的规范、标准与避坑指南。帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。前言为什么要认真对待“写注释”这件小事你有没有遇到过这些场景半年前自己写的业务今天改个小需求打开文件之后第一反应“这谁写的垃圾代码”再一看作者是自己。接手别人老项目逻辑绕来绕去偶尔看到一行注释// TODO、// 这里有点问题先这么写……然后就没有然后了。为了“规范”团队强行要求每个函数、每个变量都加注释结果注释和代码一起过期甚至误导后来的人。这篇文章就想解决一个现实问题日常写代码时注释到底该怎么写为什么这么写坑会踩在哪目标是让 3 年后的自己和队友打开代码就能快速搞懂上下文而不是骂人。本文不是讲晦涩的底层原理而是站在一线开发、项目规范的视角用 Vue / 前端开发场景来聊聊“代码注释规范”。一、第一原则好代码胜过好注释但没有注释也不一定是好代码1.1 一句话核心原则能用清晰的命名和结构表达含义就不要用注释补课。注释只做代码无法表达的“额外信息”。很多团队会陷入两个极端极端 1注释洁癖“好的代码不需要注释”结果写一堆晦涩难懂的缩写变量没人看得懂。极端 2注释狂魔几乎每一行都要注释// 声明一个变量 aleta1;// a 加 1a;这种注释只会浪费时间、增加维护成本。正确姿势优先改代码让代码本身更清晰变量名、函数名、拆分方法、抽象组件……其次用注释补充“代码表达不到的信息”例如为什么要这么写业务背景 / 历史原因 / 兼容性注意事项性能、边界条件、已知坑和其他模块的约定接口协议、调用顺序⬆ 返回目录二、注释的四大黄金场景该写什么下面是我在项目里常用、非常推荐的四类注释场景。2.1 解释“为什么这么写”Why而不是“代码在干嘛”WhatWhat 代码自己能看出来Why 只能靠你写出来。❌ 错误示例只是重复代码// 获取用户列表constusersawaitfetchUsers();这行注释几乎就是在重复变量名没有信息增量。✅ 推荐示例解释设计/业务原因// 这里不能直接用缓存的用户列表// 1. 用户状态在线/离线是实时的// 2. 后端会根据当前登录态过滤可见用户// 所以每次都强制请求最新数据constusersawaitfetchUsers({forceRefresh:true});这里的注释说明了为什么不能优化成缓存以后有人想“优化性能”时看到注释就会收手避免踩坑。⬆ 返回目录2.2 标记“约定”和“前置条件”别人需要遵守什么在 Vue 组件、工具函数、API 调用中最容易出问题的往往不是“实现细节”而是使用前提参数有没有默认值有哪些边界情况调用顺序有没有依赖✅ Vue 组件示例在 props / emits 上写注释// UserForm.vue script setup langtsinterfaceProps{/** * 表单模式 * - create新建用户所有字段可编辑 * - edit编辑用户用户名不可修改 * - readonly只读模式所有字段禁用 */mode:create|edit|readonly;/** * 编辑/只读模式下必传 * 后端返回的完整用户信息。 * create 模式下可以不传内部会使用默认值 */user?:User;}constpropsdefinePropsProps();/** * 表单提交事件 * - create: 提交的 user.id 由后端生成 * - edit: 必须包含原有的 user.id */constemitdefineEmits{(e:submit,payload:User):void;}();这里注释的作用非常明确告诉你mode不同模式的差别告诉你user在什么模式下是必传的告诉你submit的 payload 长什么样重点这类注释是“契约”的一部分写在类型interface / props / emits附近最合适。⬆ 返回目录2.3 记录“历史遗留”和“坑点说明”这块代码为什么这么丑有些代码你也知道写得不优雅但短期内又不能重构比如老接口的奇怪字段命名历史版本遗留的时间格式奇怪的兼容写法低版本浏览器 / 特定设备与其未来被队友或自己怒喷“这谁写的怎么这么鬼畜”不如提前写清楚原因。✅ 示例兼容老接口/** * 注意后端这个接口是老系统保留的字段命名非常诡异。 * - usr_nm 对应用户姓名 * - crt_tm 是创建时间字符串格式为 YYYY/MM/DD HH:mm:ss * 暂时不能动这个接口只在这里统一做一次映射。 */functionnormalizeLegacyUser(raw:any):User{return{id:raw.id,name:raw.usr_nm,createdAt:dayjs(raw.crt_tm,YYYY/MM/DD HH:mm:ss).toDate(),};}以后谁要改这个接口时看到注释就会明白这是历史债务不是你写代码水。如果要改要连后端 / 老系统一并考虑。⬆ 返回目录2.4 对复杂算法 / 业务流程做“概览说明”给后人一张思维导图有些模块就算代码写得再优雅逻辑本身就是复杂的多步骤审批流复杂的优惠券 / 价格计算规则权限控制菜单 按钮 数据权限这种时候不要指望“代码自解释”加一段流程性注释是对所有人的救赎。✅ 示例订单价格计算假设你在calculateOrderPrice.ts里/** * 订单价格计算规则简化版 * * 1. 基础金额 所有商品单价 * 数量 之和 * 2. 商品级优惠 * - 满减券优先按商品分类应用不能跨分类凑单 * - 折扣券在满减之后应用最多 2 张 * 3. 订单级优惠 * - 平台券在所有商品级优惠之后应用 * - 封顶逻辑总优惠金额不能超过基础金额的 30% * 4. 运费 * - 满 99 元包邮 * - 其他情况按地区和重量计算 * * 注意 * - 所有金额都用「分」为单位在内部计算避免浮点误差 * - 对外展示时再转换为「元」 */exportfunctioncalculateOrderPrice(order:Order):OrderPriceDetail{// 具体实现略}这里注释的价值在于给出了整体流程按步骤标明了关键约束封顶 30%、单位是“分”以后别人改逻辑时有一个可以“对齐口径”的地方⬆ 返回目录三、哪些注释是坚决不要写的知道“该写什么”之后更重要的是哪些注释写了只会拖团队后腿3.1 重复代码的注释浪费时间 增加维护成本❌ 示例 1重复变量名// 用户名称constuserNamegetUserName();❌ 示例 2重复函数名 / 类型名/** * 获取用户列表 */functiongetUserList(){...}这些注释的问题没有额外信息只要一改函数名/变量名注释就有可能不一致时间久了变成“看着像对的其实是错的”解决办法优先把命名改清晰getList→getUserListdata→userList/formState确实没啥要补充的就不要写注释空着反而更安全。⬆ 返回目录3.2 “心情日志”注释TODO / FIXME 不写清楚内容❌ 典型反面教材// TODO: 后续优化// FIXME: 有 bug半年后你自己也不知道要优化什么有什么 bug复现步骤是什么是否已经修了是否还有影响✅ 推荐写法// TODO(v2.1): 表格数据量1w时滚动卡顿需要引入虚拟列表// 影响范围订单列表、用户列表// FIXME(2025-03-18 by 张三):// 后端偶发返回重复的 orderId导致 set 里丢数据// 临时方案前端用 (orderId createdAt) 拼接作为 key等后端修复后移除规范建议TODO / FIXME 注释建议包含触发条件 / 复现方式影响范围可选目标版本/时间 责任人缩写团队可以规定重要 TODO / FIXME 必须对应 Jira/禅道/飞书任务号比如// TODO(JIRA-1234 v2.2): 支持多语言先写死为中文⬆ 返回目录3.3 和真实逻辑不一致的注释比没有注释更可怕注释一旦和代码不一致就会变成误导信息。❌ 示例注释没更新/** * 返回 true 表示用户未登录 */functionisLoggedIn(){return!!localStorage.getItem(token);}显然逻辑是“有 token 才是登录”但注释写反了。如果后来别人只看注释不看实现很容易写出一堆反逻辑的代码。经验结论写过时注释 欺骗未来的同事。写了就要维护维护不了就少写。所以在团队规范里可以明确改动逻辑时必须同步检查相关注释是否仍然正确Code Review 时把**“注释是否仍然成立”**当成一个检查点⬆ 返回目录3.4 写在实现细节里的“小说故事”越写越乱有同学特别喜欢在函数内部“边写边感想”比如functionfetchData(){// 这里先判断一下是不是有缓存// 如果有缓存的话就不用请求接口了// 但是这里我们又觉得可能缓存会不准// 所以又加了一个时间戳的判断// 总之就是很复杂先这么写吧……}这种注释的问题没有结构像碎碎念日记讲了一堆感受没有讲清楚最终规则以后别人看的时候只会更迷惑更好的做法把真正关键的规则整理成条目其他的犹豫、不确定、吐槽写到需求文档 / 评审记录里而不是代码里✅ 重写示例/** * 缓存策略说明 * 1. 默认命中缓存避免重复请求 * 2. 如果缓存时间超过 5 分钟则强制请求最新数据 * 3. 切换用户时必须清空缓存用户隔离 */functionfetchData(){// 实现略}⬆ 返回目录四、不同层级怎么写以 Vue 项目为例的一套落地规范下面从 Vue 项目常见几层结构出发给一套可直接落地到项目里的注释建议。4.1 组件层Vue SFC注释重点放在哪里4.1.1props/emits/expose是最值得写注释的地方因为它们构成了组件的“对外接口”。✅ 示例表单组件script setup langtsinterfaceProps{/** * 表单初始值 * - 不传则使用内部默认值 * - 传入时会完全覆盖默认值不要只传部分字段 */modelValue?:UserFormModel;/** * 是否立即在 mounted 后拉取远程选项数据 * 默认 true如果父组件要控制时机可以传 false 后手动调用 reloadOptions */autoLoadOptions?:boolean;}constpropswithDefaults(definePropsProps(),{autoLoadOptions:true,});constemitdefineEmits{/** * 表单提交成功时触发 * payload 包含表单内的所有字段 */(e:submit,payload:UserFormModel):void;/** * 任意字段变化时触发用于实时保存草稿 */(e:update:modelValue,value:UserFormModel):void;}();defineExpose({/** * 重新拉取远程下拉选项 */reloadOptions,});/script这里的注释能让你在不看实现的情况下就知道怎么用这个组件这就是高价值注释。4.1.2 复杂模板逻辑优先拆组件其次写块级注释当模板里出现大量条件判断 / 嵌套v-if/v-for时优先选择“拆小组件 / 抽函数”仍然复杂时可以在逻辑块上方加一段块级注释说明大体意图✅ 示例template!-- 展示可见的菜单项 1. 已被后端标记为启用 2. 当前用户有权限 3. 如果是移动端只显示前 5 个 --MenuItemv-foritem in visibleMenuItems:keyitem.id:itemitem//template这里注释的作用总结了visibleMenuItems的过滤规则方便别人查找时快速定位逻辑比如“为什么这个菜单在移动端消失了”⬆ 返回目录4.2 业务逻辑层hooks / composables / services很多 Vue 3 项目会把复杂逻辑拆到useXXX.ts逻辑复用xxxService.ts调用后端接口 业务规则这部分逻辑往往最需要注释但注释也最容易乱写。4.2.1 统一写在函数/方法签名上方说明职责和返回值✅ 示例组合式函数/** * 订单列表的分页 筛选逻辑 * - 对外暴露响应式数据list、loading、pagination * - 支持关键字搜索、状态筛选 * - 初始化时自动加载一次数据 */exportfunctionuseOrderList(){constlistrefOrder[]([]);constloadingref(false);constpaginationreactive({page:1,pageSize:20,total:0,});// ...return{list,loading,pagination,reload,resetFilters,};}4.2.2 和后端接口交互的地方注释协议差异/约束✅ 示例Service 层/** * 获取订单详情 * - 后端只在 statusPAID 时返回 payInfo 字段 * - 如果订单已退款refoundInfo 字段存在但可能为空对象 * - 接口有 500ms 左右的延迟注意不要在输入框输入时频繁调用 */exportasyncfunctionfetchOrderDetail(orderId:string):PromiseOrderDetail{const{data}awaitrequest.get(/api/orders/${orderId});returnnormalizeOrderDetail(data);}这些信息如果不写在这里很难在代码中第一时间发现却又对上层调用逻辑影响极大。⬆ 返回目录4.3 工具层utils / helpers何时需要注释通用的小工具函数命名清晰时可以不用注释exportfunctionformatPrice(amountInCent:number):string{...}如果函数有一些隐含约束或性能特征就应该注释说明✅ 示例/** * 深拷贝对象仅用于小对象 * - 基于 JSON 序列化不支持函数 / Date / Map / Set * - 遇到循环引用会抛错 * 适合用于「接口 mock 数据」等简单场景不要在核心路径频繁使用。 */exportfunctionsimpleCloneT(obj:T):T{returnJSON.parse(JSON.stringify(obj));}⬆ 返回目录五、团队层面的“注释规范建议”可以直接抄到你们 RULE.md 里下面给一份可以直接落地的团队规范草稿你可以根据实际情况微调。5.1 总体原则P1注释是代码的一部分写了就要维护。P2注释说明“为什么 / 有什么坑 / 有什么约定”不要“翻译代码”。P3宁可少写也不要写错宁可写在“合适位置”也不要乱丢。⬆ 返回目录5.2 “必须注释”的场景对外接口组件的props/emits/expose公共工具函数 / Service 层函数的入参、返回值说明特别是有约束时复杂业务逻辑 / 算法在函数 / 模块顶部写整体流程说明或规则列表历史遗留 / 兼容代码必须说明历史背景 / 兼容对象 / 计划替换方案TODO / FIXME必须写明触发条件 / 影响范围 / 预期目标建议关联任务号如TODO(JIRA-1234)⬆ 返回目录5.3 “禁止/不鼓励”的注释重复代码内容的注释变量名 / 函数名已经表达清楚空泛的 TODO / FIXME未说明问题和上下文纯吐槽 / 情绪化注释长篇大论但没有结构的“感想式注释”⬆ 返回目录六、一个完整的小案例从“糟糕注释”到“可维护代码”下面用一个实际例子演示如何从“混乱风格”改到“规范易读”。6.1 初版很多人项目里真实存在的写法!-- OrderList.vue --scriptsetuplangts// 订单列表组件constdataref([]);constloadingref(false);constpageref(1);constpageSizeref(10);consttotalref(0);// 获取列表asyncfunctiongetList(){loading.valuetrue;// 调接口constresawaitrequest.get(/api/list,{params:{p:page.value,ps:pageSize.value,},});// 处理数据data.valueres.data.list;total.valueres.data.total;loading.valuefalse;}// TODO: 后面要加筛选/scripttemplate!-- 列表 --Table:datadata//template问题命名不清晰data/getList//api/list注释几乎都是废话没有说明任何约束TODO 没有说明到底怎么“要加筛选”⬆ 返回目录6.2 改进版结合命名 注释一起升级!-- OrderList.vue --scriptsetuplangts/** * 订单列表页 * - 支持分页 * - 计划后续增加状态筛选、关键字搜索见 TODO */import{fetchOrderList}from/services/order;constordersrefOrder[]([]);constloadingref(false);constpaginationreactive({page:1,pageSize:10,total:0,});/** * 拉取订单列表 * - 后端的页码从 1 开始不要传 0 * - pageSize 最大不超过 100否则后端会报错 */asyncfunctionloadOrders(){loading.valuetrue;constresawaitfetchOrderList({page:pagination.page,pageSize:pagination.pageSize,});orders.valueres.list;pagination.totalres.total;loading.valuefalse;}// TODO(v2.1): 增加筛选条件状态 / 下单时间区间// - 与后端对齐接口 GET /api/orders新增 status / startAt / endAt 参数// - UI 上用折叠面板隐藏高级筛选/scripttemplateOrderTable:dataorders:loadingloading:paginationpaginationchangeloadOrders//template这里我们做了几件事改变量名data→orders、getList→loadOrders提取 Service 层fetchOrderList便于复用与测试用注释补充约束和未来计划而不是重复代码这就是一个**“代码 注释配合良好”的例子**。⬆ 返回目录七、如何把“注释规范”写成一篇能发 CSDN 的文章你可以按本文结构稍作润色就能产出一篇完整的博客。建议大致结构如下引子痛点故事自嘲团队真实场景引出“注释到底该不该写”的问题第一原则好代码优先注释补充 Why 限制四大高价值注释场景Why / 约定 / 历史坑点 / 复杂流程概览四类反面注释示例重复代码、空 TODO/FIXME、过期注释、碎碎念结合 Vue 项目结构的一套实践组件层、业务层、工具层分别给建议和示例前后对比小案例“糟糕版” vs “改进版”总结 个人习惯分享比如写完函数先写注释再实现、Review 时检查注释等你可以直接把上文复制到 CSDN稍微调整标题 / 小节顺序并补充你自己项目中的真实故事和代码片段会更有代入感和说服力。⬆ 返回目录八、结语写给 3 年后的自己注释不是给现在的你看的是给“未来的你”和“曾经不认识你的同事”看的。多写一点“为什么这么写”少写一点“这行在干嘛”多写一点“有什么坑 / 有什么约束”少写一点“将来再说”写得少但每一行都值钱比写一堆废话强太多⬆ 返回目录 系列模块导航 项目基础规范一、《Vue3 前端项目目录结构规范按业务域划分新人也能快速上手项目规范篇》二、《前端命名规范变量 / 函数 / 组件 / 文件 统一标准告别混乱命名项目规范篇》三、《前端代码注释规范Vue 实战避坑让 3 年后的自己还能看懂代码项目规范篇》四、《前端空值处理规范Vue 实战避坑可选链、?? 兜底写法项目规范篇》五、《前端常量管理规范拒绝魔法数字统一枚举与字典项目规范篇》 跟着系列慢慢学把技术功底扎扎实实地打牢⬆ 返回目录技术成长从来不是比谁写得快而是比谁写得稳、规范、可维护。哪怕每次只吃透一条规范长期下来差距会非常明显。后续我会持续更新前端规范、工程化、可维护代码相关实战干货帮你告别面条代码、维护噩梦在开发与面试中更有底气。觉得有用欢迎点赞 收藏 关注不错过每一篇实战内容。我是 Eugene与你一起写规范、写优质代码我们下篇干货见