uniapp微信小程序WebSocket接入与心跳重连实战
uniapp开发微信小程序这几年基本成了跨端项目的默认选项。尤其是一碰到实时通讯、聊天、在线状态这类需求WebSocket就是绕不开的那道门槛。我在实际项目里踩过不少坑也反复重写过好多版连接管理逻辑渐渐沉淀出一套比较稳的接入方式。这篇内容适合刚入门uniapp、或者已经在写小程序但WebSocket总是断断续续的开发者内容会从项目设计、环境搭建、连接封装、心跳重连到线上排查都过一遍尽量把关键细节和经验讲透。1. 项目整体设计与技术选型思路1.1 为什么选择uniapp而不是原生小程序我最早是写原生小程序的后来接触到uniapp最大的感受不是语法简单了多少而是“同一套代码能跑多端”这件事很省心。虽然微信小程序原生开发也很成熟但如果你后续要考虑支付宝小程序、抖音小程序甚至App端用uniapp会少维护三套代码。而且uniapp基于Vue语法组件化思路和生命周期跟Vue基本一致团队里只要有人熟悉Vue上手门槛就低很多。从实际工程角度看uniapp还内置了条件编译机制可以针对微信小程序特殊处理。比如有些API在App端和微信小程序端行为不一样可以用#ifdef MP-WEIXIN单独写一段兼容代码。这种灵活性在多端项目里非常实用。选型时我还要考虑一点团队的技术栈是什么。如果团队一直用Vue那uniapp几乎是顺理成章的选择。如果团队全是原生小程序出身可以不急着迁移。但我个人的建议是新项目直接上uniapp长期维护成本相对可控。1.2 WebSocket在什么场景下值得用WebSocket不是万能药用之前要先确认场景是否真的需要“长连接 服务端主动推送”。普通请求-响应场景用uni.request就够了不需要引入额外的连接管理复杂度。我总结过几个典型场景在线客服或IM聊天消息需要实时送达订单状态流转比如支付成功后服务端主动通知小程序刷新行情类应用比如股票、币价、体育比分低频轮询体验太差协同编辑或多人互动比如白板、投票后台任务进度推送比如导入导出大文件的进度条。如果你只是每隔几秒拉一次最新数据用轮询也能顶住那就不建议用WebSocket。WebSocket会带来连接维护成本、心跳设计、异常恢复等问题属于“收益大但复杂度也大”的技术方案。1.3 整体项目目录与模块划分我习惯把项目按业务模块拆分而不是按文件类型堆在一起。一个中小型小程序的目录大概是这样的src ├── api │ ├── index.js │ └── message.js ├── components ├── pages │ ├── index │ └── chat ├── store ├── utils │ ├── socket.js │ └── auth.js └── static在pages里放页面components里放公共组件utils/socket.js单独管理WebSocket连接。业务层不直接写uni.connectSocket而是通过封装的Socket服务去发消息和订阅事件。这样页面只用关心“发个消息”“监听某个事件”不需要知道底层连接是否断开、是否重连。模块边界清楚了后期出问题也好定位。我见过很多项目把WebSocket逻辑直接写在页面onLoad里结果页面一多连接开了一堆排查起来很痛苦。2. 开发环境搭建与项目初始化2.1 HBuilderX安装与项目创建开发uniapp最顺手的工具是HBuilderX当然也可以直接用Vue CLI但可视化脚手架这块HBuilderX确实省事。安装完打开选择“文件 - 新建 - 项目”类型选“uni-app”模板选默认模板或空模板都行。项目创建后第一件事不是急着写页面而是先检查manifest.json里的微信小程序配置。这里的appid很关键如果你还没注册小程序可以先留着空的用测试号也能跑起来但要体验完整能力还是建议自己注册一个。HBuilderX的“运行到浏览器”主要用于快速看Vue组件效果真正调试小程序还是要用微信开发者工具。在HBuilderX菜单栏选择“运行 - 运行到小程序模拟器 - 微信开发者工具”它会自动拉起开发者工具并编译代码。2.2 微信小程序编译配置微信开发者工具打开后如果你是用测试号或者没有配置合法域名会碰到一个很常见的坑开发阶段访问API被拦截。解决办法是在微信开发者工具的“详情 - 本地设置”里勾选“不校验合法域名、web-view业务域名、TLS版本以及HTTPS证书”。但上线前必须在微信公众平台配置Socket合法域名而且必须是wss://协议不能是ws://。这是很多人第一次上线被卡住的地方。开发环境可以用ws://连本地服务生产环境必须是wss://并且证书要有效。这里有个容易忽略的点域名配置不是即时生效的有时候改完要等一小段时间。还有如果你在多个小程序平台发布每个平台都要单独配置Socket合法域名别指望一套配置通吃。2.3 基础页面与组件开发我以一个在线聊天页面为例。页面结构很简单消息列表、输入框、发送按钮。在uniapp里页面就是一个Vue组件模板部分跟普通Vue写法一致。template view classchat-page scroll-view classmessage-list scroll-ytrue view v-formsg in messages :keymsg.id classmessage-item text{{ msg.content }}/text /view /scroll-view view classinput-area input v-modeldraft placeholder输入消息 / button clicksendMessage发送/button /view /view /template写页面的时候我一般会把“业务逻辑”和“连接逻辑”分开。页面里的sendMessage只负责构造消息对象真正的发送动作交给Socket服务。这样页面上没有一堆onSocketMessage回调代码读起来清爽很多。注意小程序里的scroll-view高度问题。如果不给scroll-view设置固定高度内容长了不会滚动这个问题经常让新手困惑。建议用flex布局把输入区域固定到底部消息列表用flex:1撑满剩余空间。3. WebSocket接入实战与核心原理3.1 WebSocket协议与小程序端生命周期WebSocket本质上是基于TCP的全双工通信协议通过一次HTTP握手建立连接之后双方可以互相推送数据。握手时客户端会发一个携带Upgrade: websocket头的请求服务端确认后连接就建立了后续数据帧不再走HTTP协议。微信小程序的uni.connectSocket就是对这个能力的封装。不过小程序端的WebSocket和浏览器端不太一样它受微信生命周期影响很大。小程序切到后台连接很快会被系统挂起甚至断开切回前台连接状态不一定能自动恢复。所以做小程序实时通信不能只写一个简单的uni.connectSocket就完事必须考虑“前后台切换”和“断线重连”。3.2 uniapp中的WebSocket封装思路封装的核心目标是让外部调用简单内部逻辑完整。我写了一个socket.js作为单例统一管理连接、心跳、重连和消息分发。class SocketManager { constructor() { this.socketTask null; this.isConnected false; this.isManualClose false; this.heartbeatTimer null; this.reconnectTimer null; this.reconnectCount 0; this.listeners {}; } connect(url) { this.url url; this.isManualClose false; if (this.socketTask) { return; } this.socketTask uni.connectSocket({ url: url, success: () { console.log(WebSocket连接发起); }, fail: (err) { console.error(连接失败, err); } }); this.socketTask.onOpen(() { this.isConnected true; this.reconnectCount 0; this.startHeartbeat(); this.emit(open); }); this.socketTask.onMessage((res) { this.handleMessage(res.data); }); this.socketTask.onClose(() { this.isConnected false; this.stopHeartbeat(); this.emit(close); if (!this.isManualClose) { this.scheduleReconnect(); } }); this.socketTask.onError((err) { this.isConnected false; console.error(WebSocket错误, err); }); } send(data) { if (this.isConnected) { this.socketTask.send({ data: typeof data string ? data : JSON.stringify(data) }); } else { console.warn(连接未建立无法发送消息); } } close() { this.isManualClose true; this.stopHeartbeat(); if (this.socketTask) { this.socketTask.close({}); } } } export default new SocketManager();这里的onOpen之后才允许发送消息。很多人第一次写小程序WebSocket都会踩这个坑一上来直接sendSocketMessage发现消息发不出去或者直接报错。原因是连接还没建立成功必须先等onOpen回调。3.3 心跳机制与重连策略WebSocket连接看起来是长连接但中间可能被网络设备、代理服务器、微信客户端静默断开。服务端如果不及时检测客户端会一直以为连接还在等到下次发消息才发现已经断了。所以心跳机制是必须的。我常用的心跳方案是客户端每30秒发送一个{ type: ping }如果服务端在10秒内没有返回pong客户端主动断开并重连。服务端也必须配合实现心跳响应逻辑。startHeartbeat() { this.stopHeartbeat(); this.heartbeatTimer setInterval(() { if (this.isConnected) { this.send({ type: ping, timestamp: Date.now() }); } }, 30000); } stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer null; } }重连策略我建议用指数退避第一次失败等1秒第二次等2秒第三次等4秒最大间隔比如30秒。不要刚断开就疯狂重连那样只会增加服务端压力而且小程序端多次快速connectSocket也容易触发系统异常。scheduleReconnect() { if (this.isManualClose) { return; } this.reconnectCount; const delay Math.min(Math.pow(2, this.reconnectCount - 1) * 1000, 30000); this.reconnectTimer setTimeout(() { this.connect(this.url); }, delay); }当页面切换到前台时也可以主动检查连接状态如果发现已经断开立即重连。这个比干等定时器更及时。3.4 消息协议设计与数据推送处理WebSocket消息是文本或二进制格式如果没有统一的协议前后端联调时很容易乱。我习惯把消息设计成一个结构体{ type: chat.message, messageId: uuid-123456, timestamp: 1700000000000, data: { from: user_001, content: 你好 } }客户端根据type分发到不同处理逻辑服务端也根据type决定如何处理。messageId的作用是幂等处理比如网络重试可能导致同一条消息收到两次客户端可以记录最近处理过的messageId并去重。在socket.js里加一个handleMessage方法统一把消息转发给注册的监听器handleMessage(rawData) { let msg null; try { msg JSON.parse(rawData); } catch (e) { console.error(消息解析失败, rawData); return; } const type msg.type; if (this.listeners[type]) { this.listeners[type].forEach((cb) cb(msg.data)); } } on(type, callback) { if (!this.listeners[type]) { this.listeners[type] []; } this.listeners[type].push(callback); } off(type, callback) { const cbs this.listeners[type]; if (cbs) { const index cbs.indexOf(callback); if (index -1) { cbs.splice(index, 1); } } }这样业务页面只需要写onLoad() { socketManager.on(chat.message, this.handleMessage); } onUnload() { socketManager.off(chat.message, this.handleMessage); }发消息的地方也统一走socketManager.send业务方不需要关心底层连接是否在重连。这个设计我把代码量从每个页面几屏回调降到了一页几行排查逻辑也清晰。4. 常见问题与排查技巧实录4.1 连接不稳定或频繁断开如果你发现连接总是刚建立就断开先不要怀疑代码优先检查网络环境和域名配置。开发阶段最常遇到的问题是微信开发者工具开启了“不校验合法域名”真机上却关闭了该配置生产环境域名配成了ws://而不是wss://域名证书过期或TLS配置有问题服务端没有发送心跳响应客户端把它当成断线。排查这类问题建议在onClose和onError回调里把报错信息打出来然后分环境测试。真机调试时用“真机调试”模式看日志比模拟器更真实。如果确认服务端没问题再看是不是小程序切后台导致的。微信对后台小程序有一定限制长时间在后台会回收网络资源。我的处理是页面onHide时手动关闭连接页面onShow时重新建立连接。语音通话这类特殊情况除外普通聊天场景不需要保持后台连接。4.2 onMessage收不到消息这个问题的根源通常是消息解析失败或者事件监听没注册上。第一查看handleMessage打印确认原始数据是否被JSON.parse成功。如果数据是纯文本或者用了二进制格式解析就会失败。第二检查监听器注册时机。如果业务组件销毁时调用了off但消息是在销毁后才来的自然就收不到。第三确认服务端是在连接建立成功后才推送消息。如果服务端在new WebSocket握手后立刻推消息而客户端onOpen还没触发消息可能已经丢失。我常用的办法是服务端不要依赖客户端当前状态在连接完全打开后再发。如果消息属于重放类客户端可以请求一次补拉接口。4.3 页面销毁后连接未关闭这个问题经常发生。用户在聊天页然后返回上一页页面的onUnload里如果没调用close连接会一直在后台挂着。虽然看起来不影响功能但会带来两个隐患内存占用和消息乱入。更隐蔽的是同一个Socket实例被多个页面共用页面A监听chat.message页面B也监听chat.messageA销毁时只移除了A的监听器没有关闭连接那么B页仍然正常。如果你直接用单例模式一定要区分“页面级监听”和“应用级连接”。连接由全局管理监听只在需要的时候挂载和移除。4.4 前后台切换导致连接失效小程序从后台切回前台WebSocket状态需要重新检查。在App.vue的onShow里调用重连逻辑onShow() { const isConnected socketManager.isConnected; if (!isConnected socketManager.url) { socketManager.connect(socketManager.url); } }同时要注意切后台时不要立刻关闭连接。有些场景用户只是短暂切走几秒后回来如果立刻关闭下次回来又要重新握手体验反而差。我一般会允许连接在后台存活一段时间真机测试下来微信小程序在后台3到5分钟内连接大多还能保留但超过这个时间就不可靠了。一个更稳妥的方案是onHide时启动一个定时器比如30秒后才关闭onShow时取消定时器。这样既避免长时间悬挂又不会秒断。5. 项目上线与后续优化建议5.1 分包与性能优化WebSocket相关页面如果体积比较大比如聊天页包含表情包、图片选择器、历史记录组件建议把整个聊天模块拆成独立分包。微信小程序有主包大小限制分包可以有效降低主包体积同时提高首屏加载速度。我的做法是把聊天页面、资料页面、图片预览页都放进一个“子包”subpackage-chat ├── pages │ ├── chat │ └── user-profile └── components页面跳转时使用分包路由/subpackage-chat/pages/chat/chat。初始化时主包只保留首页和公共组件等用户真正进入聊天模块时再加载分包。5.2 消息压缩与流量控制微信小程序WebSocket本身支持字符串和ArrayBuffer。如果消息体很大比如包含Base64图片建议不要直接塞进JSON。二进制的压缩率更高不过也会增加解析复杂度。普通文本消息直接用JSON即可只有遇到批量数据或高频推送才需要改成二进制。流量控制主要指的是客户端和广播逻辑。服务端推消息给所有在线用户前最好先做按需过滤。比如用户A只关心订单状态就不应该把用户B的消息也推过去。客户端收到消息后也要避免高频刷新。每秒推送超过30条时setData压力会很大我通常会把消息先暂存到一个数组中用时间片段一次性刷新列表。5.3 安全校验与Token失效处理WebSocket连接建立时的握手阶段微信小程序只能通过URL携带Token或者其他自定义参数。要注意Token不要直接写在URL里容易在日志中暴露。更好的是在建立连接后客户端发送一条auth消息服务端校验后再进入正常通信。{ type: auth, data: { token: your-token } }服务端收到auth后校验如果Token无效直接关闭连接。客户端收到关闭后不能立刻重连否则会形成死循环。此时应该提示用户重新登录并清掉已保存的Token。我在实际项目里还遇到过一个情况Token过期时间比WebSocket连接时间长导致连接还在但后续请求被服务端拒绝。所以心跳消息里最好也带上Token服务端在心跳处理时顺带校验一遍发现过期就回一个auth.failed客户端收到后主动断线并触发重新登录流程。写在最后微信小程序里的WebSocket说到底不是“能用”就行而是要在连接生命周期、网络切换、前后台切换、服务端异常这几个维度上都做到有策略。我现在做新项目第一件事就是先把socket.js写好再把业务页面接进去。这样后面加消息类型、加推送场景都只是往type分发器里补一个分支的事。这几个坑踩下来我的经验是宁可把连接管理写得“重”一点也不要图省事在页面里堆回调。连接管理是整个实时通信的地基地基稳了上面盖什么业务都不慌。

相关新闻

armclang函数插桩:嵌入式代码执行流程分析实战

armclang函数插桩:嵌入式代码执行流程分析实战

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

2026/10/16 5:19:33 阅读更多 →
两节串联锂电池主动均衡:基于MP2642GR-Z与PIC单片机的设计

两节串联锂电池主动均衡:基于MP2642GR-Z与PIC单片机的设计

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

2026/10/16 5:19:33 阅读更多 →
Ubuntu离线部署GitLab:程序包下载与快速安装配置指南

Ubuntu离线部署GitLab:程序包下载与快速安装配置指南

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

2026/10/16 5:19:33 阅读更多 →

最新新闻

《派出你的AI同事:WorkBuddy案例实战》042:私有化部署落地

《派出你的AI同事:WorkBuddy案例实战》042:私有化部署落地

《派出你的AI同事:WorkBuddy案例实战》042:私有化部署落地 📌 卷三 第六部分 企业级派出:上岗、治理与合规 第13章 企业部署与上线案例 13.1 第六部分引语 企业级派出:上岗、治理与合规 前五个部分,我们已经把你训练成了一个"会派活的人":你能在个人效率…

2026/10/16 6:06:48 阅读更多 →
《派出你的AI同事:WorkBuddy案例实战》038:智能体通信与冲突解决

《派出你的AI同事:WorkBuddy案例实战》038:智能体通信与冲突解决

《派出你的AI同事:WorkBuddy案例实战》038:智能体通信与冲突解决 📌 卷三 第五部分 派出一支 AI 队伍:多智能体与助理 第11章 多智能体协作案例 11.3 11.3 智能体通信与冲突解决 背景与痛点 当队伍里不止一个智能体,真正的难题不是"各自能不能干活",而是…

2026/10/16 6:06:48 阅读更多 →
自发光烘焙消失之谜:Meta Pass链路踩坑与修复指南

自发光烘焙消失之谜:Meta Pass链路踩坑与修复指南

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

2026/10/16 6:06:48 阅读更多 →
Folia 歌词动画音乐播放器下载与安装指南:网盘到 AUR 4 条通道,5 分钟从 0 到第一首歌

Folia 歌词动画音乐播放器下载与安装指南:网盘到 AUR 4 条通道,5 分钟从 0 到第一首歌

Folia 歌词动画音乐播放器下载与安装指南:网盘到 AUR 4 条通道,5 分钟从 0 到第一首歌 【免费下载链接】folia-major 专注于绚丽的歌词动画效果的本地音乐/navidrome/第三方多平台在线音乐播放器 项目地址: https://gitcode.com/GitHub_Trending/fo/fo…

2026/10/16 6:06:48 阅读更多 →
AI写代码的六大安全风险与防护实战指南

AI写代码的六大安全风险与防护实战指南

1. AI写代码的安全问题:一线开发者必须正视的六个真实风险面这两年AI编程助手几乎成了标配,Copilot、Cursor、通义灵码、CodeBuddy,还有各种本地部署的开源模型,写业务代码、写脚本、写测试用例,甚至写SQL和正则&#…

2026/10/16 6:06:48 阅读更多 →
机器视觉:工业相机——从光学传感器到数字图像

机器视觉:工业相机——从光学传感器到数字图像

在讨论各类相机之前,我们首先要互相达成一个共识,即在极度追求成像还原性的工业领域,如果不谈电路、接口和控制系统:相机本质是一种传感器(Sensor)——将采集的被摄物体的颜色/灰度分布特征转换为二维数字特…

2026/10/16 6:05:48 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →