代码注释与格式化:从可读性到团队协作的工程实践

前阵子我接手一个历史遗留模块,代码里躺着一行注释:“这段逻辑不要动,动了会出线上事故。”没有署名,没有日期,也没有任何原因说明。我盯着它看了很久,最后还是把那段重构掉了——结果注释里担心的问题,早在三个版本之前就被另一个修复解决了。那行注释唬住了之前至少两个想改代码的人,也让我在排查时白白兜了一圈。今天这篇想聊聊注释与代码格式化。注释管的是语义层的可读性,格式化管的是结构层的可读性,它们看起来是最基础的两个话题,却是我在代码评审里看到争议最多、误解最深的地方。

这篇文章适合刚接触团队协作的新人,也适合正在为“要不要删注释”“要不要统一格式化工具”吵得不可开交的团队。我会把注释的适用场景、反模式、验收标准,以及格式化工具的落地链路和常见坑都拆开讲一遍。内容偏实操,以我这些年看代码、写代码、改代码的真实感受为主。

1. 注释究竟是什么:它补的是表达力,不是写作量

1.1 先分清代码的第一表达通道

我一直坚持一个观点:代码本身才是第一表达通道,注释是次要的兜底手段。变量名、函数名、模块边界、参数顺序、返回类型,这些才是每天要被读几十遍的东西。一个函数如果叫validateOrderStatus,它做的事情已经说了大半,这时候你再在旁边写“// 验证订单状态”,就是纯粹的复读机。

真正需要注释的地方,往往是代码无法独立表达的“为什么”。比如一个校验逻辑必须先查数据库再查缓存,因为缓存每天凌晨才刷新一次。这段因果链埋在三层函数调用里,单看某一行的代码根本猜不出来。这时候注释就是在补表达力的缺口,不是在凑字数。

我常用的判断标准很简单:如果删掉这行注释,读者仍然能通过代码结构、命名、测试用例推断出同样的信息,那它就没有存在价值。如果删掉后读者会产生误解,或者根本无法判断前置条件,那它才有资格留下来。

1.2 写注释的真正时机

在我见过的代码里,真正有价值、删不得的注释通常只出现在三类位置。第一类是公共 API 的边界。别人调你的函数,不需要读完整实现,但需要知道传入参数的取值范围、返回值的含义、会抛出什么异常。这种注释写在接口签名旁边,相当于给调用者一份“使用手册”。第二类是业务规则背后的隐藏约束。典型例子是“这里不能走缓存,因为实时性要求高”或“必须先更新订单状态再扣库存,否则对账会不平”。这些约束往往来自一场漫长的需求讨论,代码本身看不到理由。第三类是排查问题后沉淀下来的因果结论。比如“此处 Service 的实例是以接口方式注入,千万不要改成实现类注入,否则启动阶段的循环依赖会复现”。这类注释是我最珍惜的,因为它是用线上故障换来的经验。

1.3 给注释设定的验收标准

我自己写注释之前会做三个检查。第一问,这行注释丢了一年后,后面的人会不会因为少了它而做错事?如果答案是不会,那就不写。第二问,这个注释是回答 why 还是回答 what?what 应该由代码自己回答,why 才需要人补充。第三问,这行注释会不会在业务变更后过期?如果可能过期,我要么把“失效条件”写进去,要么干脆不写。

有一种注释很容易被忽略:它写的时候是对的,但代码后来变了,注释没人同步更新,它就变成了一颗定时炸弹。所以注释不只是写的时候要克制,维护的时候更要用对待代码一样的态度去对待它。看到注释与代码不符时,不是顺手改一下代码,而是要追问哪边才是当前的真相。

2. 最该删掉的四种注释:评审现场几乎天天见

2.1 复读机式注释:删掉无损失

我在 Code Review 里见过最多的,就是下面这类注释:

let count = 0; // 计数器 for (const user of users) { // 遍历所有用户 process(user); }

这种注释单独看无害,但放在一起就会形成严重的视觉噪音。人眼扫过代码时,大脑会不自觉地把注释也读进去,信息量明明只有一行,视野里却堆了三行。密集的复读注释会稀释真正重要的代码和真正重要的注释。它们就像网页里的无效广告,不提供任何信息,只是挡住视线。遇到这种注释,我通常直接一整片删掉,没有任何心理负担。

2.2 圈地式注释与年表式注释

还有一类注释长这样:

// ========== 初始化模块 ========== // ---------- 分界线 ---------- // 2023-01 某A 新增登录逻辑 // 2023-05 某B 修复支付回调问题

圈地式注释在编辑器没有代码折叠功能的年代还有存在意义,它能帮你快速定位代码块的边界。但现在的 IDE 早就有结构折叠、书签、全局搜索,再靠一长串横线来导航,完全是重复劳动。更麻烦的是,每次移动函数、重构模块,你都得记得同步挪这些线条,忘一次就成了错位导航。

年表式注释更没必要。版本控制系统的提交记录已经把每一次变更的日期、作者、原因、差异全记下来了,注释里的“谁在什么时候改了什么”是版本库的劣质复述。留着它,还会让评审时有人惯性去读这段年表,忽略真正的变更内容。同样的信息,git log和git blame查得又快又全,而且永远不会因为代码挪了位置而丢。

2.3 误导性注释:比没有注释更危险

没有注释,读者至少会谨慎地读代码。有了注释,读者会天然地信任它,哪怕代码和注释已经对不上。有一次我排查一个数据一致性问题,看到接口上方赫然写着“本接口只用于旧版客户端兼容,新端请调用新接口”。我放心地跳过了这个方法,结果查了整整一个下午才发现,那条“只用于旧客户端”的路径其实早就被新逻辑复用了,注释是半年前改版时留下的,代码在注释之后又改过三轮。那个下午我唯一的收获,就是对一个老观点产生了刻骨铭心的认同:代码至少会被编译器检查,注释永远没人检查,所以注释的可信度天然低于代码。

遇到误导性注释,正确的动作是先确认代码当前的真实意图,再用 git 历史确认这条约束是已经失效还是仍然成立,最后决定是删掉注释还是改写注释。最忌讳的就是注释和代码打架时顺手把代码改回注释描述的样子,那样可能把已经修好的逻辑又改坏了。

2.4 被注释掉的代码:版本库里的僵尸

“这段先注释掉,以后可能用回来。”这句话我听过太多次,但真实情况是:注释掉的代码极少被用回来,它们只是在文件里慢慢腐烂。被注释的代码有几个隐蔽危害。第一,它会让搜索变得不可靠,你搜一个函数名,结果这个函数已经注释了两百行,你很难判断它到底是废弃的还是临时关闭的。第二,它在评审里制造噪音,别人必须花时间判断“这段是死的还是活的”。第三,它给人错误的心理暗示:代码是临时的,所以不需要认真对待。

版本库本身就是最好的恢复机制。你删除的每一段代码,在历史提交记录里都完整保留着,真要找回,git log加git revert随时可以做到。所以“怕删了找不回来”这个顾虑在 git 时代根本不成立。我的做法是:看到注释掉的代码,先问作者要不要,不要就当场删,绝不手软。

3. 格式化的自动化:把人类从无意义的争论中解放出来

3.1 手工对齐是在浪费注意力

写代码久了你会意识到,手工格式化是一个巨大的时间黑洞。团队里有人喜欢等号两边加空格,有人不喜欢;有人坚持函数参数超过三个就换行,有人觉得五个以内都应该放一行;有人用两个空格缩进,有人用四个。这些争论发生在代码评审里,一句“你觉得这边是不是该换个行”就能扯出十分钟讨论,而讨论的对象跟业务逻辑没有任何关系。

格式化工具的使命不是让你喜欢它的输出结果,而是让团队根本不需要讨论结果。它把“唯一且自动”变成默认选项,把“缩进几个空格”“单引号还是双引号”“分号加不加”这类问题彻底移出评审清单。最初你会觉得工具的输出不好看,但很快你会发现,审美分歧让位于一致性之后,人的注意力终于可以集中在真正的设计问题上。

3.2 格式化工具的根本逻辑:基于语法树,不是文本替换

很多人以为格式化工具只是把文本按规则重新排一下,其实不是。主流工具都是先解析源码,生成抽象语法树,再按规则重写输出。这意味着它可以正确识别字符串、注释、模板字面量里的内容,不会因为里面有“{”或“;”就误判结构。正则替换做不到这一点,这也是那些粗暴的“代码美化脚本”经常把代码改坏的原因。

常见语言和工具对应起来大概是这样:

语言 / 生态格式化工具特点
JavaScript / TypeScriptPrettier、ESLint --fix覆盖面广,配置项多,社区标准成熟
PythonBlack配置极少,号称“不可配置”
Gogofmt官方强制格式,Go 社区默认遵守
Rustrustfmt官方工具,与语言版本绑定
C / C++clang-format可定制性强,适合接入遗留项目
Javagoogle-java-format 等风格统一,方便团队对齐

选工具时不要只看“哪个生成的代码好看”,要看团队能不能统一接受它的输出风格。Go 社区的成功已经证明了一点:当格式化成为语言规范和工具链的默认部分,关于代码长相的争吵就会消失,新人和老人写出来的代码看起来几乎一样,code review 的效率直接上了一个台阶。

3.3 格式化的极限:它能做的和不能做的

格式化能解决的是结构规则:缩进、换行、空格、引号、分号、尾逗号。它不能解决的是真正的可读性问题:一个 300 行的函数要不要拆成三个;一个变量该叫flag还是shouldNotifyUser;一段状态机逻辑是否应该改用策略模式。这些问题的答案依赖业务判断和设计经验,机器无法自动决定。

理解这个边界很重要。很多人把格式化工具当成代码质量的银弹,结果发现它跑完之后代码只是“整齐的难读”,于是转身说格式化没意义。这个结论下得太早了。格式化的价值在于把结构层的琐碎问题清零,让你在 review 时只需要关注语义层——注释、命名、函数划分、业务逻辑。它清理掉了噪声,但并没有替你产生信号。

4. 团队落地格式化:从零到一的全过程

4.1 分三步推进,别一步到位

给团队引入格式化工具,最忌讳的就是第一天就把它挂进 CI,然后所有人 commit 失败。正确做法是分三步走。

第一步,先把格式化命令写进项目的脚本里,比如在package.json里加一行:

"scripts": { "format": "prettier --write \"src/**/*.{ts,tsx}\"" }

这一步让团队里愿意用的人可以先跑起来,同时形成“格式化是项目的一部分”这个共识。

第二步,接进提交钩子,让本地 commit 前自动格式化暂存区文件。这一步要特别注意只处理暂存区,不要一跑就把全仓库都格式化了。常见组合是 lint-staged 加 Husky,配置大概是:

npx lint-staged --allow-empty

配置里写成对*.js、*.ts、*.css等文件执行格式化命令。这样一次提交里只会包含你真正改动过的文件的格式化结果,不会产生大面积无关 diff。

第三步,等团队已经适应了本地格式化,再在 CI 里加检查。CI 的任务是兜底——防止有人通过git commit --no-verify绕过本地钩子,把没格式化的代码推到远程。到这一步,格式化才真正成为团队纪律。

4.2 提交钩子里最容易踩的三个坑

第一个坑是全局格式化。团队里如果有人直接对全项目跑了一次格式化,提交信息里会混入几百个无关文件的改动,评审人员根本没法判断哪些是业务变更、哪些只是格式变化。解法就是我上面说的 lint-staged——只处理本次提交涉及的文件,把格式化范围收缩到最小。

第二个坑是工具版本不一致。不同版本的 Prettier、Black 在某些边界情况下的输出可能不同,本地格式化没问题,CI 里一跑就报错。这个问题很隐蔽,因为大多数格式化工具升级时不会大张旗鼓地通知你。解法是锁版本,把格式化工具的精确版本放进项目依赖和 CI 环境里,升级工具版本时单独提交一次,顺便让全组重新格式化。

第三个坑是跳过钩子。git commit --no-verify是本地命令,任何人随时都能绕过。这不是道德问题,而是流程设计问题——你必须假设会有人这么做。CI 检查就是用来兜住这个风险的,它管不到每个人的本地环境,但只要代码进了远程分支,就必须过格式校验。

4.3 老项目怎么渐进式格式化

老项目格式化是最需要耐心的事。如果一次性全量格式化,git 历史里的 blame 信息会被全面冲掉,以后想查某一行代码是什么时候、因为什么原因改的,会非常困难。我经历过一个项目,格式化提交合进去之后,所有人查 blame 都看到的是那一次格式化提交,真正的业务变更完全被淹没了。

推荐的做法是分目录分阶段推进。先把一个子目录格式化成新风格,单独提交一次;再格式化下一个目录,再独立提交一次。每个格式化提交独立存在,不要和业务提交混在一起。同时,在仓库里维护一个.git-blame-ignore-revs文件,把每一次纯格式化的提交 hash 记进去。之后执行 git blame 时带上忽略文件,git 就会自动跳过那些格式化提交,继续追溯格式化之前的原始变更。

这里是个典型配置流程:

git config blame.ignoreRevsFile .git-blame-ignore-revs

这个细节知道的人不多,但对历史包袱重的代码库非常关键。格式化本身是为了让代码更易维护,如果它反而毁掉了历史追溯能力,代价就大了。

4.4 格式化讨论边界:把分歧留给真正的代码设计

即使有了格式化工具,团队里关于“可读性”的讨论也不会完全消失,这其实是好事。区别在于,没有工具的时候,讨论的是一千个人有一千种偏好的流水账;有工具之后,讨论聚焦在真正值得讨论的设计问题上。

举个例子,格式化工具可以决定三元表达式怎么写,但它不知道某个嵌套了三层的三元表达式是否应该拆成独立函数。工具能做的是强制执行“每条语句换行”,它做不到判断“这个逻辑的复杂程度已经超过了人类大脑的短期记忆容量”。所以我在评审时,一旦有人对某段代码的“读起来费劲”提出异议,我会顺着这个信号往深处挖:是不是函数太长了?是不是状态太多?是不是变量名没起好?这些才是人和人之间真正有意义的讨论。

5. 我自己这几年关于注释与格式化的习惯

5.1 看到可疑注释,我的处理顺序

我现在看到一条可疑注释,不会立刻改代码,也不会立刻删注释。我会先用 git blame 找到这条注释是哪个提交引入的,再看关联的提交说明和 diff,搞清楚注释在写下的那一刻对应的是什么约束。然后我再对照当前代码,判断这条约束是否仍然成立。最后才决定是删除、改写,还是原样保留。这个流程能避免两个常见错误:一是误删还有价值的业务规则,二是保留已经失效的误导信息。注释不是不可动的文物,它需要和代码一样接受审查。

5.2 新写代码时的注释原则

我给自己立了一个很简单的规矩:能用命名说清楚的,绝对不用注释;能用结构说清楚的,也绝对不用注释;只有当“为什么这样做”只能靠语言表达的时候,才动用注释。而且这样的注释,我会尽量把有效期限写出来。比如“该兼容逻辑只在旧版本客户端存在期间有效,旧版本下线后可以删除”,这句话比单纯写“这里做兼容”有价值得多,因为它给了后人一个明确的清理信号。

5.3 评审注重的四个检查项

最后分享一下我在评审代码时,关于注释和格式化的固定检查清单。第一,注释是不是在复述代码做了什么。是的话,要求删掉。第二,注释里有没有“为什么”。没有的话,看看是否需要补一条。第三,注释和代码是否一致。不一致的话,要求当场解决,而不是留到以后。第四,格式化工具是否已经运行。没有运行的话,让提交者先跑完再提交。

这四条检查项执行久了,团队会慢慢形成默契:注释是稀缺资源,只留给重要信息;格式化是自动流程,不需要人操心。等到这两种状态都成为日常,我发现自己写代码的心态也变了——我不再需要费心维护那些视觉上的整齐,也不用时刻担心一行注释会不会误导三个月后的自己。注意力被彻底解放出来,放在真正需要人做判断的事情上:这个模块边界是否清晰,这段逻辑是否真的做了它该做的事。

格式化工具用起来可能只需要半天,但把“注释要克制、格式要自动”这两条刻进习惯,值得花很长时间反复练。每次代码评审都是一次训练,每次重构都是一次验证。慢慢地你会发现,干净代码不是靠某一招实现的,而是这些看起来不起眼的小事,一件一件累积出来的。

相关新闻

artcraft从创意到实物:艺术手工融合的完整实操指南

artcraft从创意到实物:艺术手工融合的完整实操指南

1. 从"artcraft"这个名字说起:它到底想解决什么问题第一次看到"artcraft"这个词,我脑子里蹦出来的第一反应是"艺术"加"手艺"的组合。这不是一个随便拼出来的名字,它背后指向的是一类非常具体的东西—…

2026/10/20 5:49:17 阅读更多 →
UE4 ListView性能优化实战:EntryWidget回收机制与选型指南

UE4 ListView性能优化实战:EntryWidget回收机制与选型指南

1. 为什么ListView值得单独拎出来聊做UE4 UI开发的人,迟早会撞上ListView这堵墙。不是因为它难用,恰恰相反,是因为它太好用,好用到很多人第一次接触就把它当成万能列表控件,结果项目做到中期,列表一卡、数据…

2026/10/20 5:49:17 阅读更多 →
artcraft从创意到成品:艺术手工项目全流程拆解与避坑指南

artcraft从创意到成品:艺术手工项目全流程拆解与避坑指南

1. 从“artcraft”这个名字说起:它到底想解决什么问题第一次看到“artcraft”这个标题,我脑子里蹦出来的第一反应是:这大概率不是一个纯工具,也不是一个纯内容平台,而是一个把“艺术创作”和“手工实现”这两件事捏在一…

2026/10/20 5:48:17 阅读更多 →

最新新闻

HTTP POST表单提交全解析:Content-Type、编码格式与实战避坑指南

HTTP POST表单提交全解析:Content-Type、编码格式与实战避坑指南

1. 从一个被忽略的细节说起&#xff1a;为什么表单提交远不止填个action很多人第一次接触Web开发&#xff0c;接触到的第一个"能跑起来"的功能&#xff0c;大概率就是一个HTML表单。写一个<form>标签&#xff0c;配上action和method"post"&#xff0…

2026/10/20 6:25:32 阅读更多 →
自动驾驶多模态数据生成:统一传感器输出的关键技术与实践

自动驾驶多模态数据生成:统一传感器输出的关键技术与实践

自动驾驶算法做到后面&#xff0c;真正卡脖子的往往不是模型结构&#xff0c;而是数据。尤其当你同时要喂摄像头、激光雷达、毫米波雷达这几个完全不同的传感器时&#xff0c;数据稀缺和模态不一致会被放大成一个系统工程问题。OmniGen 这个名字听起来很高端&#xff0c;去掉包…

2026/10/20 6:25:32 阅读更多 →
三层网络架构冗余配置实战:VRRP、浮动路由与链路聚合详解

三层网络架构冗余配置实战:VRRP、浮动路由与链路聚合详解

前阵子搭了一套三层结构的网络实验环境&#xff0c;核心两台、汇聚两台、接入一台&#xff0c;实验目标很明确&#xff1a;把三层架构里的各路冗余手段都配上&#xff0c;然后人为制造故障&#xff0c;看流量到底能不能自己绕过去。做完最大的感受是——冗余两个字说起来容易&a…

2026/10/20 6:25:32 阅读更多 →
最新论文追踪指南:平台选择、检索技巧与自动订阅全攻略

最新论文追踪指南:平台选择、检索技巧与自动订阅全攻略

想第一时间看到领域里刚出炉的论文&#xff0c;最怕的不是搜不到&#xff0c;而是根本不知道该去哪个平台搜、用什么关键词、按什么排序。组会汇报前临时抱佛脚、开题时需要确认自己做的方向有没有人抢先发表、审稿时想查一查某篇预印本到底有没有正式见刊——这些场景我都经历…

2026/10/20 6:25:32 阅读更多 →
Web服务器Nginx与集群架构——Java开源litemall小商城项目应用实践

Web服务器Nginx与集群架构——Java开源litemall小商城项目应用实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 6:25:32 阅读更多 →
AI大模型自动化测试实战:全流程智能测试设计与落地

AI大模型自动化测试实战:全流程智能测试设计与落地

我在一线做测试自动化和质量保障已经快十年&#xff0c;说实话&#xff0c;前八年大部分时间都花在写脚本、调框架、跟环境和用例维护作斗争上。最近这两年&#xff0c;AI大模型自动化这个方向确实改变了我的工作节奏。之前接手某个跨平台系统&#xff0c;每个版本回归要花上两…

2026/10/20 6:24:32 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →