WorkBuddy + Supabase 快速搭建移动端 App 实战:六阶段与十六坑
1. 为什么我选择 WorkBuddy 而不是从零手搓一个 App去年年底我接了个私活需求很明确做一个内部用的任务协作工具要有移动端、要能推送、要能离线缓存最重要的是——两周内上线。当时我第一反应是打开 Android Studio 新建项目但冷静下来想了想从零搭一个能上线的 App光是账号体系、数据同步、文件存储这三块就够我喝一壶的。后来一个做独立开发的朋友甩给我一个词WorkBuddy。WorkBuddy 本质上是一个帮你快速搭建工作台类应用的开发框架它把常见的后端能力用户认证、数据库、文件存储、实时订阅和前端壳子WebView 容器、原生桥接、进度条管理打包好了你只需要关注业务逻辑本身。配合 Supabase 做后端整个开发链路会变得非常短。我最终用六个阶段把它推上了线中间踩了十六个坑这篇文章就是把整个过程拆开讲清楚。这篇文章适合谁看如果你是有一定前端基础、想快速验证一个 App 想法的独立开发者或者你是一个小团队的技术负责人需要在有限时间内交付一个可用的移动端产品那这篇内容应该能帮你省下不少试错时间。我不会只给你步骤我会告诉你每个决策背后的原因以及那些文档里不会写的坑。2. 六个阶段的整体拆解与时间分配2.1 阶段划分的逻辑为什么是这六步我把整个项目拆成了六个阶段环境搭建与项目初始化、WebView 容器与原生桥接、Supabase 后端接入、核心业务功能开发、打包与签名配置、上线前的兼容性测试。这个划分不是拍脑袋来的而是按照依赖关系排的——每一步的产出都是下一步的输入。举个具体的例子为什么 WebView 容器要放在 Supabase 接入之前因为 WebView 的调试环境一旦跑通你后面所有业务代码都可以在浏览器里先验证再放进容器里跑。如果你先接后端你会发现每次调接口都要重新打包 APK那个循环太慢了。我实测下来先在 Chrome 里把 Supabase 的增删改查跑通再塞进 WebView效率至少提升三倍。时间分配上我两周的实际投入大概是这样的环境搭建半天WebView 容器两天Supabase 接入一天半核心业务四天打包签名一天兼容性测试两天。剩下的时间全在填坑。下面这张表是我建议的时间预算你可以根据自己的熟悉程度调整。阶段建议时间主要产出最容易卡住的点环境搭建0.5 天可运行的空白项目Android Studio 依赖解析失败WebView 容器2 天能加载本地页面的壳文件访问权限与进度条Supabase 接入1.5 天可读写的云端数据行级安全策略配置核心业务4 天完整功能闭环状态同步与离线缓存打包签名1 天可安装的 release 包签名配置与混淆规则兼容性测试2 天多机型验证报告低版本 WebView 内核差异2.2 每个阶段的验收标准我给自己定了一个规矩每个阶段必须有明确的验收标准不达标不进入下一阶段。环境搭建的验收标准是能跑起来一个空白 Activity 并且不报错WebView 容器的标准是能加载一个本地 HTML 并且 JS 能调用原生方法Supabase 的标准是能从 App 里写入一条数据并在后台看到。这个规矩救了我好几次。有一次我在 WebView 阶段发现进度条一直卡在 80% 不动如果当时硬着头皮往下做后面所有页面加载都会有问题。后来排查发现是onProgressChanged回调里我漏了一个判断条件导致进度条在某个区间反复重置。这种问题越早发现越好因为它的影响面会随着项目推进越来越大。3. WebView 容器搭建中最容易翻车的三个细节3.1 进度条不是加个 ProgressBar 就完事很多人以为 WebView 的进度条就是放一个ProgressBar然后在onProgressChanged里更新进度就完了。我一开始也是这么想的结果实测发现两个问题第一进度条在页面加载完成后不会自动消失因为newProgress到 100 之后还会回调几次第二快速切换页面时进度条会闪烁因为旧的加载还没结束新的就开始了。正确的做法是在onProgressChanged里加一个判断只有当newProgress大于等于 100 时才隐藏进度条并且用一个标志位记录当前是否正在加载。另外进度条的样式建议用横向的HorizontalProgressBar不要用圆形的因为横向的视觉反馈更符合页面加载这个语义。webView.setWebChromeClient(new WebChromeClient() { Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress 100) { progressBar.setVisibility(View.GONE); isLoading false; } else { if (!isLoading) { progressBar.setVisibility(View.VISIBLE); isLoading true; } progressBar.setProgress(newProgress); } } });这段代码看起来简单但isLoading这个标志位是关键。没有它进度条会在每次回调时重新显示造成闪烁。我踩这个坑的时候排查了快一个小时最后用日志打印每次回调的值才定位到。3.2 文件访问权限content:// 协议的那些事WebView 加载本地文件时你会遇到content://开头的 URI。这个协议是 Android 的文件提供者机制目的是让应用之间安全地共享文件。但 WebView 默认是不允许访问content://的你需要手动开启setAllowContentAccess(true)和setAllowFileAccess(true)。这里有个坑从 Android 10 开始setAllowFileAccess对file://协议的访问被限制了你必须用content://或者WebViewAssetLoader。我当时的做法是用WebViewAssetLoader把本地资源映射到一个虚拟的 HTTPS 域名下这样既安全又不会触发权限问题。WebViewAssetLoader assetLoader new WebViewAssetLoader.Builder() .addPathHandler(/assets/, new WebViewAssetLoader.AssetsPathHandler(this)) .build(); webView.setWebViewClient(new WebViewClientCompat() { Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { return assetLoader.shouldInterceptRequest(request.getUrl()); } });用WebViewAssetLoader之后你的本地页面地址会变成https://appassets.androidplatform.net/assets/index.html这种形式WebView 会把它当成正常的 HTTPS 请求处理省掉很多权限麻烦。3.3 原生桥接JS 调用 Android 方法的安全边界WebView 和原生代码的通信靠addJavascriptInterface。这个方法的坑在于如果你在 Android 4.2 以下使用会有安全漏洞因为反射机制可以被恶意 JS 利用。虽然现在最低版本基本都是 5.0 以上了但你还是应该加一个JavascriptInterface注解并且只暴露必要的方法。我当时的做法是定义一个Bridge类里面只放三个方法getToken()、saveData()、showToast()。其他所有业务逻辑都走 Supabase 的 HTTP 接口不通过桥接。这样即使 WebView 被注入攻击面也很小。注意addJavascriptInterface注入的对象方法必须在主线程执行如果你在方法里做耗时操作一定要开子线程否则会阻塞 WebView 的渲染。4. Supabase 接入从建表到行级安全策略4.1 为什么选 Supabase 而不是自己搭后端Supabase 本质上是一个开源的 Firebase 替代品它基于 PostgreSQL提供了自动生成的 REST API、实时订阅、用户认证和文件存储。我选它的原因很简单我不需要写一行后端代码就能让 App 读写云端数据。但这里有个前提你的数据结构不能太复杂。Supabase 的行级安全策略RLS是基于 SQL 的如果你需要复杂的权限逻辑比如只有任务创建者才能删除任务但协作者可以修改状态那你就得写比较复杂的策略。我当时的做法是把权限逻辑简化成创建者全权协作者只读这样策略就很好写。4.2 建表与 RLS 策略的实操步骤首先在 Supabase 控制台建一张tasks表字段包括id、title、status、owner_id、created_at。然后开启 RLS添加两条策略一条是SELECT策略允许所有登录用户读取一条是ALL策略只允许owner_id等于当前用户 ID 的用户进行增删改。-- 开启行级安全 ALTER TABLE tasks ENABLE ROW LEVEL SECURITY; -- 读取策略所有登录用户可读 CREATE POLICY tasks_select ON tasks FOR SELECT USING (auth.role() authenticated); -- 写入策略只有创建者可操作 CREATE POLICY tasks_modify ON tasks FOR ALL USING (auth.uid() owner_id);这两条策略看起来简单但auth.uid()这个函数只有在请求携带了有效的 JWT 时才会返回用户 ID。如果你在 App 里没有正确设置Authorization头策略会直接拒绝所有请求而且错误信息很模糊只会告诉你权限不足。4.3 实时订阅的坑连接断开与重连Supabase 的实时订阅基于 WebSocket。在移动端WebSocket 会因为网络切换、后台切换而断开。我一开始没处理重连结果用户切到后台再回来数据就不更新了。后来加了一个重连逻辑监听onError和onClose事件在断开后延迟 3 秒重新订阅。let subscription supabase .from(tasks) .on(*, payload { console.log(数据变化:, payload); }) .subscribe((status) { if (status CLOSED || status CHANNEL_ERROR) { setTimeout(() { subscription.unsubscribe(); subscribeToTasks(); }, 3000); } });这个重连逻辑我建议你直接抄因为移动端的网络环境太复杂了没有重连的实时订阅基本不可用。5. 打包签名与兼容性测试上线前的最后两道关5.1 签名配置为什么你的 release 包装不上Android 的 release 包必须签名才能安装。我一开始用 Android Studio 自动生成的签名结果发现每次构建都会生成新的签名文件导致覆盖安装失败。正确的做法是手动生成一个 keystore 文件然后在build.gradle里固定引用它。android { signingConfigs { release { storeFile file(../keystore/release.jks) storePassword 你的密码 keyAlias release keyPassword 你的密码 } } buildTypes { release { signingConfig signingConfigs.release minifyEnabled true proguardFiles getDefaultProguardFile(proguard-android-optimize.txt), proguard-rules.pro } } }这里有个细节minifyEnabled true会开启代码混淆但 WebView 的桥接类不能被混淆否则 JS 调用会失败。你需要在proguard-rules.pro里加上-keepclassmembers class com.yourpackage.Bridge { public *; }。5.2 低版本 WebView 内核的兼容性处理Android 的 WebView 内核版本是跟着系统走的低版本系统的 WebView 内核可能不支持某些 ES6 语法。我实测发现Android 8.1 的 WebView 内核大概相当于 Chrome 60 左右不支持可选链操作符?.和空值合并运算符??。解决办法有两个一是用 Babel 把代码转成 ES5二是在 WebView 初始化时检测内核版本如果太低就提示用户升级。我选的是第一种因为转译的成本更低而且不影响用户体验。// 在 WebView 里检测内核版本 const ua navigator.userAgent; const match ua.match(/Chrome\/(\d)/); if (match parseInt(match[1]) 60) { alert(您的浏览器内核版本过低请升级系统或使用最新版浏览器); }5.3 多机型测试的优先级排序兼容性测试不可能覆盖所有机型我的做法是按优先级排序先测主流品牌的中低端机因为性能瓶颈最容易暴露再测高端机验证功能完整性最后测平板和折叠屏验证布局适配。我当时的测试清单是这样的优先级机型类型测试重点常见问题P0中低端机页面加载速度、内存占用进度条卡顿、白屏P1高端机功能完整性、动画流畅度桥接调用失败P2平板/折叠屏布局适配、横竖屏切换元素错位、溢出这个排序的逻辑是中低端机的性能问题会直接影响可用性必须优先解决高端机的功能问题通常是个例可以后续修复平板和折叠屏的用户占比低可以放到最后。6. 十六个坑里最值得说的五个6.1 坑一Android Studio 依赖解析失败这个坑我放在第一个因为它太常见了。报错信息通常是Could not resolve all task dependencies for configuration :app:debugCompileClasspath。原因一般是网络问题或者仓库配置不对。我的解决办法是在settings.gradle里加上阿里云的镜像仓库并且把google()和mavenCentral()的顺序调整一下。dependencyResolutionManagement { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/central } google() mavenCentral() } }提示如果你用的是公司网络可能需要配置代理才能访问外部仓库。但代理配置这块我不展开讲你懂的。6.2 坑二WebView 白屏但控制台无报错白屏是最让人头疼的问题因为没有任何错误信息。我排查了三个小时最后发现是WebViewClient的shouldOverrideUrlLoading返回了true导致所有 URL 都被拦截了。正确的做法是对于内部页面返回false让 WebView 自己处理对于外部链接返回true用系统浏览器打开。Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { String url request.getUrl().toString(); if (url.startsWith(https://appassets.androidplatform.net)) { return false; // 内部页面WebView 自己处理 } // 外部链接用系统浏览器打开 Intent intent new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); return true; }6.3 坑三Supabase 的 JWT 过期导致请求失败Supabase 的 JWT 默认有效期是一小时。如果你的 App 长时间在前台运行JWT 过期后所有请求都会返回 401。我一开始没处理这个结果用户用了一个小时后突然所有操作都失败。后来加了一个拦截器在请求返回 401 时自动刷新 token。async function fetchWithAuth(url, options {}) { const { data: { session } } await supabase.auth.getSession(); if (!session) { await supabase.auth.refreshSession(); } return fetch(url, { ...options, headers: { ...options.headers, Authorization: Bearer ${session.access_token} } }); }6.4 坑四进度条与页面加载不同步这个坑我在 3.1 里提过但这里再补充一个细节如果你的页面里有大量图片onProgressChanged的进度值会在 90% 左右停留很久因为图片加载不计入进度。解决办法是用onPageFinished回调来隐藏进度条而不是依赖进度值到 100。6.5 坑五release 包安装后闪退release 包闪退的原因通常是混淆导致的。我遇到过一次是因为 Supabase 的 SDK 里用了反射被 ProGuard 混淆后找不到类。解决办法是在proguard-rules.pro里加上-keep class com.supabase.** { *; }。这个规则会保留 Supabase 相关的所有类避免混淆问题。7. 可复用的经验清单与后续扩展方向7.1 我总结的十条经验第一环境搭建阶段一定要用镜像仓库否则依赖解析能卡你半天。第二WebView 容器先用浏览器调通再塞进去效率翻倍。第三Supabase 的 RLS 策略一定要在控制台先测试不要直接在 App 里试。第四进度条的逻辑要加标志位否则会闪烁。第五文件访问用WebViewAssetLoader不要用file://。第六桥接类只暴露必要方法减少攻击面。第七实时订阅必须加重连逻辑。第八签名文件要固定不要每次重新生成。第九混淆规则要保留 WebView 桥接类和第三方 SDK。第十兼容性测试按优先级排序先测中低端机。7.2 后续可以扩展的方向这个 App 上线后我陆续加了一些功能离线缓存用 IndexedDB 存任务列表、推送通知用 Supabase 的 Edge Function 触发、深色模式用 CSS 变量切换。如果你也想扩展我建议先从离线缓存开始因为它的实现成本最低但用户体验提升最明显。另外WorkBuddy 本身也在迭代我最近看到它支持了自定义技能Skill的机制你可以把常用的操作封装成 Skill然后在工作台里直接调用。这个机制我还没深入用但看起来很适合做自动化流程。如果你对这个方向感兴趣可以先去研究一下它的 Skill 文档我后续也会写一篇关于 Skill 开发的实操记录。最后说一个我个人的体会用 WorkBuddy 搭 App 最大的价值不是省了多少代码而是它帮你把那些必须做但没技术含量的事情标准化了。你不需要再纠结用哪个库做认证、用哪个方案做存储直接用它给的方案就行。省下来的时间你可以花在真正影响用户体验的细节上比如进度条的动画、错误提示的文案、加载失败的兜底页面。这些细节才是用户能感知到的。

相关新闻

google-api-nodejs-client 中 Cloud Functions 客户端包的版本演进:从 CHANGELOG 解读 18.0.x 的维护与破坏性变更

google-api-nodejs-client 中 Cloud Functions 客户端包的版本演进:从 CHANGELOG 解读 18.0.x 的维护与破坏性变更

后端 【免费下载链接】google-api-nodejs-client Googles officially supported Node.js client library for accessing Google APIs. Support for authorization and authentication with OAuth 2.0, API Keys and JWT (Service Tokens) is included. 项目地址: h…

2026/10/15 20:46:33 阅读更多 →
彩色圆点气泡跟随鼠标动画:Canvas物理引擎实现指南

彩色圆点气泡跟随鼠标动画:Canvas物理引擎实现指南

1. 项目概述:这不是炫技,是交互体验的底层升级“彩色圆点气泡跟随鼠标光标动画特效”——这名字听起来像前端新手练手的小玩具,但在我过去十年带过的十几个跨平台项目里,它反复出现在三个关键节点:某高校数字艺术实验室…

2026/10/15 20:46:33 阅读更多 →
Popeye v0.3.10 新增 PodDisruptionBudget(PDB)可用性与配置检查实战指南

Popeye v0.3.10 新增 PodDisruptionBudget(PDB)可用性与配置检查实战指南

云原生运维可观测性 【免费下载链接】popeye 👀 A Kubernetes cluster resource sanitizer 项目地址: https://gitcode.com/gh_mirrors/po/popeye 点击查看 免费下载 Popeye 是一款扫描 Kubernetes 生产集群、报告已部署资源配置问题的只读型集群消毒器…

2026/10/15 20:46:33 阅读更多 →

最新新闻

2026前端AI编程工具实测:从Figma2Code到Agent闭环,TaoToken统一Key怎么配

2026前端AI编程工具实测:从Figma2Code到Agent闭环,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/15 21:39:55 阅读更多 →
给 Coding Agent 装上 LSP:别再让它靠 grep 猜代码,TaoToken 统一 Key 打通工具链

给 Coding Agent 装上 LSP:别再让它靠 grep 猜代码,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/15 21:39:55 阅读更多 →
蓝桥杯CT107D实战:DS18B20单总线驱动与避坑指南

蓝桥杯CT107D实战:DS18B20单总线驱动与避坑指南

1. 为什么CT107D上的DS18B20值得单独拿出来讲蓝桥杯单片机组的板子——CT107D,几乎是每个参赛选手绕不开的硬件平台。这块板子上集成了不少外设,数码管、独立按键、矩阵键盘、蜂鸣器、继电器、DS1302、AT24C02、DS18B20、超声波、红外接收等等&#xff0…

2026/10/15 21:39:55 阅读更多 →
Java爬虫实战:用Jsoup高效解析网页表格并导出CSV

Java爬虫实战:用Jsoup高效解析网页表格并导出CSV

简介:这是一个基于Java与Jsoup库抓取网页表格数据的入门实例,适用于在MyEclipse环境中学习网络爬虫原理的开发者,目标是掌握从HTML页面提取结构化表格信息的完整方法。资源包为zip格式,共24个文件,其中包含7个Java源码…

2026/10/15 21:39:55 阅读更多 →
树莓派4 UART多串口配置与通信实战指南

树莓派4 UART多串口配置与通信实战指南

1. 为什么树莓派 4 的 UART 值得单独拿出来聊 树莓派 4 这块板子,但凡你玩过一阵子,大概率都会碰到一个绕不开的需求:接一个串口设备。可能是 GPS 模块、可能是某款工业传感器、可能是和另一块单片机做点对点通信,也可能是调试一个…

2026/10/15 21:39:55 阅读更多 →
大模型指令微调实战:从LoRA选型到数据配比与避坑指南

大模型指令微调实战:从LoRA选型到数据配比与避坑指南

简介:这份PPT面向自然语言处理与大模型方向的学习者与开发者,系统梳理大模型指令微调(Instruction Tuning)的核心概念与整体脉络。内容从NLP四范式切入,依次讲解传统机器学习、深度学习、预训练加微调以及预训练加Prom…

2026/10/15 21:38:54 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战: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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →