HarmonyOS应用开发实战:猫猫大作战-被动批量(同帧)、主动批量(batchUpdate)、跨帧批量的陷阱、批量与深观察的协同
前言第 32 篇我们讲过「同帧批量更新」——一个回调里改多个StateArkUI 合并成一次重渲染。但那是「被动批量」——靠回调天然同帧。实战中还有「主动批量」场景连续多次逻辑操作改 state要强制合并成一次重渲染避免中途触发浪费。HarmonyOS 提供了batchUpdate机制V2和「手动批」套路V1。本篇以「猫猫大作战」结束游戏时连续改 5 个 state 为锚点把被动批量同帧、主动批量batchUpdate、跨帧批量的陷阱、批量与深观察的协同四大要点讲透。提示本系列不讲 ArkTS 基础语法与环境搭建假设你已跟完第 1–44 篇。本篇是阶段二第十五篇。一、场景拆解endGame 连续改 5 个 state回顾「猫猫大作战」endGame第 33、36 篇// 来源entry/src/main/ets/pages/Index.ets endGame() { this.gameState GameState.GAME_OVER; // 改 1 this.maxCombo this.gameEngine.getMaxCombo(); // 改 2 this.mergeCount this.gameEngine.getMergeCount(); // 改 3 this.highestLevel this.gameEngine.getHighestLevel(); // 改 4 if (this.score this.highScore) { this.highScore this.score; // 改 5 } this.clearTimers(); }核心问题5 个State连续改ArkUI 重渲染几次答案1 次——因为都在endGame同一个调用栈同帧ArkUI 收集所有变更下一 VSync 只 build() 一次。这是第 32 篇讲的「被动批量」。但有种进阶坑endGame() { this.gameState GameState.GAME_OVER; // 改 1 this.clearTimers(); // 调方法可能内部又改 state this.maxCombo this.gameEngine.getMaxCombo(); // 改 2 }clearTimers()内部如果改了State比如清空cats仍然同帧批量——只要都在endGame调用栈内不论调多少层方法都合并。二、被动批量同帧合并2.1 同帧收集机制endGame() { this.gameState GameState.GAME_OVER; // 标记 1 this.maxCombo 5; // 标记 2 this.mergeCount 10; // 标记 3 this.highestLevel CatLevel.BIG; // 标记 4 this.highScore 1500; // 标记 5 } // 调用栈结束 → 下一 VSync → 一次 build() 所有脏组件机制ArkUI 在每次State赋值时标记依赖组件为脏不立即重渲染。当前 JS 调用栈结束endGame return。下一个 VSync 帧一次性 build() 所有脏组件。关键经验「同调用栈」「同帧」「批量」——不论改多少 state、调多少层方法只要在一个调用栈内都合并。2.2 跨帧多次重渲染的浪费// ❌ 错误用 setTimeout 分次改跨帧多次重渲染 endGame() { this.gameState GameState.GAME_OVER; setTimeout(() { this.maxCombo 5; }, 0); // 下一帧 setTimeout(() { this.mergeCount 10; }, 0); // 再下一帧 setTimeout(() { this.highScore 1500; }, 0); // 再下一帧 } // 4 次重渲染gameState maxCombo mergeCount highScore 各一次对比方式重渲染次数性能同帧批量endGame 内连续改1 次✅ 最优setTimeout(0) 分次4 次❌ 浪费 4 倍2.3 被动批量的覆盖范围endGame() { this.gameState GameState.GAME_OVER; this.clearTimers(); // 内部改 state仍同帧 this.helperA(); // 内部改 state仍同帧 this.helperB(); // 内部改 state仍同帧 this.highScore 1500; } // 全部在 endGame 调用栈内1 次重渲染 helperA() { this.maxCombo 5; } helperB() { this.mergeCount 10; } clearTimers() { /* 可能改 cats 等 */ }关键经验被动批量覆盖整个调用栈——不论嵌套多少层方法只要起点是同一个用户/定时器回调都合并。三、主动批量batchUpdate3.1 什么时候需要主动批量被动批量已经覆盖大部分场景但有种情况要主动批量——异步操作中途改 stateasync endGame() { this.gameState GameState.GAME_OVER; // 改 1本次帧 const stats await this.fetchStats(); // 异步等待 this.maxCombo stats.maxCombo; // 改 2下一帧 this.mergeCount stats.mergeCount; // 改 3再下一帧 } // gameState 单独一次重渲染maxCombomergeCount 同帧一次共 2 次痛点await让调用栈断开后续改 state 跨帧无法被动批量。3.2 batchUpdate 主动合并HarmonyOS V2 提供batchUpdate主动批量import { batchUpdate } from kit.ArkUI; async endGame() { this.gameState GameState.GAME_OVER; // 改 1本次帧 const stats await this.fetchStats(); // 异步等待 // 主动批量后续改合并成一次 batchUpdate(() { this.maxCombo stats.maxCombo; // 改 2 this.mergeCount stats.mergeCount; // 改 3 this.highestLevel stats.highestLevel; // 改 4 if (stats.score this.highScore) { this.highScore stats.score; // 改 5 } }); } // gameState 一次重渲染batchUpdate 内 4 个改合并一次共 2 次拆解片段含义batchUpdate(() { ... })主动批量装饰器回调内所有 state 改动合并回调内this.xxx ...改的 state 都标记但延迟到 batchUpdate 结束才刷新关键经验batchUpdate 用于「异步后连续改 state」场景——把跨帧的改动强制合并成一次重渲染。3.3 batchUpdate vs 被动批量维度被动批量batchUpdate触发同调用栈自动手动包回调覆盖同步连续改异步后连续改V1/V2V1 V2 都支持V2 专属推荐同步场景异步场景实战经验同步连续改用被动批量不包 batchUpdate异步后连续改用 batchUpdate。四、跨帧批量的陷阱4.1 await 后的改动天然跨帧async loadData() { this.loading true; // 改 1本次帧 const data await fetch(/api); // 异步等待 this.loading false; // 改 2下一帧 this.data data; // 改 3同改 2 帧 } // loadingtrue 单独一次重渲染loadingfalse data 同帧一次共 2 次机制await让函数返回调用栈断开。await 后的代码在新微任务帧执行与 await 前不同帧。4.2 多个 await 的多次跨帧async loadData() { this.loading true; // 改 1帧 A const user await fetch(/user); // 异步 this.user user; // 改 2帧 B const posts await fetch(/posts); // 异步 this.posts posts; // 改 3帧 C } // 3 次重渲染loading、user、posts 各一次优化用 batchUpdate 合并后两个async loadData() { this.loading true; const user await fetch(/user); const posts await fetch(/posts); batchUpdate(() { this.user user; // 改 2 this.posts posts; // 改 3 this.loading false; // 改 4 }); } // 2 次重渲染loadingtrue、batchUpdate 内合并一次4.3 Promise.all 并发再批量async loadData() { this.loading true; const [user, posts] await Promise.all([ fetch(/user), fetch(/posts) ]); // 并发只一次 await batchUpdate(() { this.user user; this.posts posts; this.loading false; }); } // 2 次重渲染最优关键经验Promise.all 并发 batchUpdate 批量 异步最少重渲染——并发减少 await 次数批量减少重渲染次数。五、批量与深观察的协同5.1 Observed 实例改属性也批量// Cat 是 Observed class第 43 篇 endGame() { this.gameState GameState.GAME_OVER; // 改 State // 改 Observed 实例属性 this.cats.forEach((cat) { cat.falling false; // 改每个猫的 falling }); this.score this.gameEngine.getScore(); // 改 State } // gameState 所有 cat.falling score 都同帧批量1 次重渲染机制Observed实例改属性也走「标记脏 延迟刷新」机制与State共用批量队列。5.2 深观察与被动批量混用// 同帧改 State 和 Observed 属性全部合并 endGame() { this.gameState GameState.GAME_OVER; // State this.cats[0].y 7; // Observed 属性 this.cats[0].falling false; // Observed 属性 this.score 99; // State } // 1 次重渲染所有脏组件GameOverOverlay、CatItem、HUD一起刷关键经验State 浅观察和 Observed 深观察共用批量队列——同帧内都合并不论观察层级。六、完整代码endGame 批量更新// 来源entry/src/main/ets/pages/Index.etsV1 被动批量版 Entry Component struct Index { State Watch(onGameStateChange) gameState: GameState GameState.IDLE; State score: number 0; State cats: Cat[] []; State combo: ComboInfo { count: 0, multiplier: 1, lastMergeTime: 0 }; State nextCatLevel: CatLevel CatLevel.SMALL; State highScore: number 0; State gameTime: number 0; State maxCombo: number 0; State mergeCount: number 0; State highestLevel: CatLevel CatLevel.SMALL; private gameEngine: GameEngine new GameEngine(); private gameLoopTimer: number -1; private spawnTimer: number -1; private timeTimer: number -1; // 结束游戏被动批量5 个 state 连续改1 次重渲染本篇重点 endGame() { this.gameState GameState.GAME_OVER; // 改 1 this.maxCombo this.gameEngine.getMaxCombo(); // 改 2 this.mergeCount this.gameEngine.getMergeCount(); // 改 3 this.highestLevel this.gameEngine.getHighestLevel(); // 改 4 if (this.score this.highScore) { this.highScore this.score; // 改 5条件 } this.clearTimers(); // 不改 state不影响批量 // 所有改动在 endGame 调用栈内ArkUI 下一 VSync 一次 build() // GameOverOverlay HUD 棋盘如果有残留一起重渲染 } // gameState 变化副作用第 39 篇 onGameStateChange(newVal: GameState): void { switch (newVal) { case GameState.GAME_OVER: this.playSfx(over); this.vibrate(); break; } } startGame() { this.clearTimers(); this.gameEngine.reset(); this.gameState GameState.PLAYING; // 改 1 this.score 0; // 改 2 this.cats []; // 改 3 this.gameTime 0; // 改 4 this.combo { count: 0, multiplier: 1, lastMergeTime: 0 }; // 改 5 this.nextCatLevel this.gameEngine.getNextCatLevel(); // 改 6 // 6 个 state 连续改1 次重渲染 this.gameLoopTimer setInterval(() { if (this.gameState ! GameState.PLAYING) return; this.cats this.gameEngine.updateCats(); // 改 1 this.score this.gameEngine.getScore(); // 改 2 this.combo this.gameEngine.getCombo(); // 改 3 if (this.gameEngine.isGameOver()) { this.endGame(); } }, 100); // 主循环每 100ms 改 3-4 个 state每帧 1 次重渲染 /* spawnTimer、timeTimer 筥略 */ } handleColumnClick(column: number) { if (this.gameState ! GameState.PLAYING) return; if (this.gameEngine.dropCat(column)) { this.cats this.gameEngine.getAllCats(); // 改 1 this.nextCatLevel this.gameEngine.getNextCatLevel(); // 改 2 } // 2 个 state 同帧1 次重渲染 } /* pauseGame / resumeGame / clearTimers / formatTime / aboutToDisappear 筥略 */ build() { Stack() { if (this.gameState GameState.IDLE) { this.MainMenuView() } else { this.GameView() } if (this.gameState GameState.PAUSED) { PauseOverlay({ gameState: this.$gameState, score: this.score }) } if (this.gameState GameState.GAME_OVER) { this.GameOverOverlay() } } .width(100%).height(100%) } /* GameView / MainMenuView / GameOverOverlay / StatItem 等略 */ }七、踩坑提示7.1 setTimeout 分次改破坏批量// ❌ 错误setTimeout 分次跨帧多次重渲染 endGame() { this.gameState GameState.GAME_OVER; setTimeout(() { this.maxCombo 5; }, 0); setTimeout(() { this.mergeCount 10; }, 0); } // ✅ 正确同调用栈连续改 endGame() { this.gameState GameState.GAME_OVER; this.maxCombo 5; this.mergeCount 10; }7.2 await 后忘 batchUpdate// ❌ 错误await 后连续改跨帧多次重渲染 async endGame() { this.gameState GameState.GAME_OVER; const stats await this.fetchStats(); this.maxCombo stats.maxCombo; // 跨帧 this.mergeCount stats.mergeCount; // 跨帧 } // ✅ 正确await 后用 batchUpdate async endGame() { this.gameState GameState.GAME_OVER; const stats await this.fetchStats(); batchUpdate(() { this.maxCombo stats.maxCombo; this.mergeCount stats.mergeCount; }); }7.3 以为 batchUpdate 是 V1// ❌ 错误V1 没有 batchUpdate编译报错 import { batchUpdate } from kit.ArkUI; // V1 项目可能无此导出 // ✅ 正确V1 用被动批量同调用栈V2 才有 batchUpdate // V1把异步后的改动用 Promise.all 合并减少 await 次数 async endGame() { this.gameState GameState.GAME_OVER; const stats await this.fetchStats(); // 只一次 await // stats 拿到后连续改被动批量 this.maxCombo stats.maxCombo; this.mergeCount stats.mergeCount; }7.4 Watch 触发时机误判endGame() { this.gameState GameState.GAME_OVER; // 触发 onGameStateChange this.maxCombo 5; // 改 state } // onGameStateChange 在 endGame 调用栈内同步触发Watch 在赋值后立即 // 但 onGameStateChange 里改其他 state 也并入本次批量关键经验Watch 回调在「赋值后同步触发」仍在同一调用栈——回调里改 state 也并入批量。八、调试技巧console.info打重渲染次数在 build() 首行 log统计调用次数对比批量效果。DevEco Profiler 看 Render统计帧的 build() 调用数被动批量应只 1 次。跨帧排查检查是否有 setTimeout/await 断调用栈检查异步后是否 batchUpdate。Watch 触发排查在 onGameStateChange 首行 log追是否同帧触发。九、性能与最佳实践同步连续改用被动批量——同调用栈自动合并不用包 batchUpdate。异步后连续改用 batchUpdateV2——强制合并跨帧改动。避免 setTimeout(0) 分次改——跨帧多次重渲染浪费。Promise.all 并发减少 await——一次 await 拿所有数据后续被动批量。State 浅观察和 Observed 深观察共用批量队列——同帧都合并。Watch 回调同帧触发——回调里改 state 也并入本次批量。十、阶段二进度小结31-45本篇是阶段二「状态管理 交互 动画」第 15 篇进度小结篇主题核心要点31State响应式状态改变触发重渲染32多 State同帧批量更新一次重渲染33setInterval 主循环100ms 物理周期暂停短路34计时器1000ms 秒级formatTime 格式化35spawnTimer2000ms 自动生成随机列选择36clearTimers-1 哨兵三时机清理aboutToDisappear 兜底37onClick 列投放闭包捕获 colhandleColumnClick 三步38箭头函数 this回调统一箭头保留 this普通函数打断链39Watch状态变化副作用集中防递归守卫40Prop父子单向只读浅拷贝显示型子组件41Link父子双向$val 传引用编辑型子组件42Provide/Consume跨层隐式共享避免 prop drilling43ObservedObjectLink类实例深观察改内部属性触发44数组项替换ForEach 密钥 diff 深观察协同45本篇批量更新被动批量同帧 batchUpdate异步后接下来第 46-50 篇会进入 V1 收尾与 V2 迁移嵌套陷阱、Reusable 列表复用、LazyForEach 大列表、V1 局限总结然后 V2 迁移Local、Param、Event、ObservedV2Trace、Monitor。总结本篇我们从批量状态更新切入掌握了被动批量同调用栈自动合并、主动批量batchUpdate 异步后强制合并、跨帧陷阱setTimeout/await 断调用栈、批量与深观察协同四大要点并给出了 endGame 5 个 state 同帧批量的完整代码。核心要点同步连续改被动批量异步后用 batchUpdate避免 setTimeout 分次Promise.all 减少 await。下一篇我们将拆解嵌套陷阱——State 嵌套对象/数组的更新坑。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源「猫猫大作战」项目源码本仓库entry/src/main/ets/pages/Index.etsArkUI 状态管理批量更新官方指南ArkUI 状态管理概述ArkUI 渲染管线与性能最佳实践开源鸿蒙跨平台社区HarmonyOS 开发者官方文档首页系列索引本仓库articles/INDEX.md

相关新闻

HarmonyOS应用开发实战:猫猫大作战-秒级计时器周期、gameTime 递增与格式化、暂停不计时间、计时器与主循环的分工

HarmonyOS应用开发实战:猫猫大作战-秒级计时器周期、gameTime 递增与格式化、暂停不计时间、计时器与主循环的分工

前言 上一篇我们搭好了 100ms 物理主循环——猫咪下落、合并、得分都靠它驱动。但游戏里还有个独立时钟:从开局到结束的累计时间(gameTime)。这个时钟和主循环分离——主循环 100ms 更新物理,计时器 1000ms 递增秒数,…

2026/7/28 0:34:51阅读更多 →
不懂乐理也能创作商用歌曲?MELO 音乐生成实战:手把手教你打造版权自有神曲

不懂乐理也能创作商用歌曲?MELO 音乐生成实战:手把手教你打造版权自有神曲

前言:在这个时代,创意是唯一的门槛 如果我告诉你,现在创作一首发行级音质、包含人声演唱、编曲完整、且完整版权完全属于你的歌曲,只需要你会打字、会拍照、甚至会随口哼一段调调,你相信吗? 不需要你买昂…

2026/7/28 0:34:51阅读更多 →
Mission Planner:从飞行控制到任务执行的3种实用解决方案

Mission Planner:从飞行控制到任务执行的3种实用解决方案

Mission Planner:从飞行控制到任务执行的3种实用解决方案 【免费下载链接】MissionPlanner Mission Planner Ground Control Station for ArduPilot (c# .net) 项目地址: https://gitcode.com/gh_mirrors/mi/MissionPlanner 当无人机操作从简单的遥控飞行升级…

2026/7/28 0:34:51阅读更多 →
Minecraft服务器管理神器:EssentialsX插件完整安装与优化指南

Minecraft服务器管理神器:EssentialsX插件完整安装与优化指南

Minecraft服务器管理神器:EssentialsX插件完整安装与优化指南 【免费下载链接】Essentials The modern Essentials suite for Spigot and Paper. 项目地址: https://gitcode.com/GitHub_Trending/es/Essentials 作为现代Minecraft服务器管理的终极解决方案&a…

2026/7/28 3:17:14阅读更多 →
Unity 2020安卓异形屏黑边适配:从原理到实战解决方案

Unity 2020安卓异形屏黑边适配:从原理到实战解决方案

1. 项目概述:安卓异形屏适配的“最后一公里”如果你是一名Unity开发者,尤其是专注于移动端,特别是安卓平台的开发者,那么“异形屏黑边”这个问题,你大概率遇到过,甚至可能被它折磨过。这几乎是Unity 2020.x…

2026/7/28 3:17:14阅读更多 →
Thorium浏览器终极指南:为什么这个Chromium优化版本值得你立即尝试?

Thorium浏览器终极指南:为什么这个Chromium优化版本值得你立即尝试?

Thorium浏览器终极指南:为什么这个Chromium优化版本值得你立即尝试? 【免费下载链接】thorium Chromium fork named after radioactive element No. 90. Source code and Linux releases. Windows/MacOS/ARM builds served in different repos, links ar…

2026/7/28 3:17:14阅读更多 →
MaixPy固件更新全攻略:从工具选型到烧录验证的嵌入式开发第一步

MaixPy固件更新全攻略:从工具选型到烧录验证的嵌入式开发第一步

1. 项目概述:为什么固件更新是开发的第一步拿到一块崭新的MaixPy开发板,或者准备开始一个新项目时,很多朋友会迫不及待地打开IDE写代码。但根据我多年的嵌入式开发经验,第一步往往不是写“Hello World”,而是确认并更新…

2026/7/28 3:17:14阅读更多 →
GTA5增强版终极菜单指南:YimMenuV2完整功能与快速入门教程

GTA5增强版终极菜单指南:YimMenuV2完整功能与快速入门教程

GTA5增强版终极菜单指南:YimMenuV2完整功能与快速入门教程 【免费下载链接】YimMenuV2 Experimental menu for GTA 5: Enhanced 项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenuV2 想要在GTA5增强版中获得前所未有的游戏体验吗?YimMen…

2026/7/28 3:17:13阅读更多 →
影刀RPA定时截图监控:自动记录网页变化完全指南

影刀RPA定时截图监控:自动记录网页变化完全指南

影刀RPA定时截图监控:自动记录网页变化完全指南 作者:林焱 定时截图是很多监控场景的基础工具——监控竞品价格变化、监控服务器状态页面、定期保存网页内容快照……配好流程,每隔一段时间自动截图保存,不用盯着屏幕。 一、使用…

2026/7/28 3:15:13阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/27 1:14:34阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:29阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:29阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:29阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/27 16:57:54阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/28 3:17:03阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →