先扔结论:如果你现在把 Claude 或 Cursor 当成 ArkTS 专家来用,大概率会掉坑里。我上周闲得慌,跑了 37 个常见开发场景的测试,结果 AI 生成的代码能直接编译通过的,不到四成。剩下的要么语法错误,要么用了废弃 API,要么逻辑能跑但性能稀烂。
你可能会说,“我也用 AI 写过 ArkTS,感觉还行啊?” —— 那是因为你写的多半是 UI 布局。一旦涉及到状态管理、生命周期或者并发模型,AI 的幻觉率直线飙升。
测试怎么做的
说一下我的测试方法,你也可以复现。
我选了 7 个 ArkTS 开发里最高频的场景,每个场景设计 3-5 个不同难度的需求,分别丢给 Claude 3.7 Sonnet、Cursor Composer 和某国产大模型(名字就不点了)。提示词刻意写得很模糊,模拟真实开发里"哎帮我把这个功能写了"的场景,不额外补充上下文。
七个场景分别是:
- UI 布局与自定义组件
- @State / @Prop / @Provide 状态管理
- 生命周期(aboutToAppear / aboutToDisappear / onPageShow)
- 网络请求与错误处理
- 并发模型(TaskPool / Worker / Promise)
- 动画与手势交互
- 文件读写与沙箱路径
每个场景我按通过标准给了三档评分:
- ✅ 直接能用:编译通过、逻辑正确、没踩废弃 API
- ⚠️ 能修:大体对,但有小错误(比如类型声明漏了、参数写反了)
- ❌ 翻车:编译都过不了,或者用了根本不存在的方法
结果汇总出来是这样的:
| 场景 | Claude | Cursor | 国产模型 |
|---|---|---|---|
| UI 布局 | ✅ 4/5 | ✅ 5/5 | ✅ 3/5 |
| 状态管理 | ⚠️ 2/5 | ⚠️ 3/5 | ❌ 1/5 |
| 生命周期 | ❌ 1/5 | ⚠️ 2/5 | ❌ 0/5 |
| 网络请求 | ⚠️ 3/5 | ✅ 4/5 | ⚠️ 2/5 |
| 并发模型 | ❌ 1/5 | ❌ 2/5 | ❌ 0/5 |
| 动画手势 | ⚠️ 2/5 | ⚠️ 3/5 | ❌ 1/5 |
| 文件读写 | ⚠️ 3/5 | ✅ 4/5 | ⚠️ 2/5 |
你看,UI 布局和文件 IO 这类"模板化"程度高的,AI 表现还行。但一旦碰到状态管理和并发,三个模型集体翻车。最离谱的是生命周期那栏——Claude 有两次把aboutToAppear写成了onLoad,这不是鸿蒙的 API,这是 Vue 的习惯带进来了。
翻车现场:状态管理
来,看个具体的。我让 AI 写一个父子组件双向同步状态的例子,要求子组件修改数据后父组件实时更新。
Claude 给我的代码长这样:
// 子组件 Child.ets@Componentstruct Child{@Propvalue:number@EventonChange:(v:number)=>voidbuild(){Button('++').onClick(()=>{this.onChange(this.value+1)})}}你一眼能看出来问题吗?@Event这个装饰器在 ArkTS 里根本不存在。鸿蒙用的是@Event吗?不,它用的是回调函数传参的方式,或者@Link/@ObjectLink做双向绑定。Claude 这是把 SwiftUI 或者 Vue 的概念混进来了。
正确的写法应该是:
// 子组件 Child.ets@Componentstruct Child{@Linkvalue:numberbuild(){Button('++').onClick(()=>{this.value+=1// 直接修改,父组件同步更新})}}// 父组件 Parent.ets@Entry@Componentstruct Parent{@Statecount:number=0build(){Column(){Text(`当前值:${this.count}`)Child({value:$count})// $语法建立Link绑定}}}这个$count的语法新手本来就容易忘,AI 不仅没帮你,还给你造了个@Event装饰器,让你更晕。
顺便说一句,Claude 在被我指出错误后的回复是:“抱歉,我混淆了 ArkTS 和 ArkUI-X 的语法。” —— 你看,它连承认自己 hallucinate 的时候都在 hallucinate,因为 ArkUI-X 也没有@Event这个鬼东西。
翻车现场:并发模型
再说一个更劝退的。我让 AI 写一个后台线程处理图片压缩的任务,要求不阻塞 UI。
Cursor 给我的方案是:
importworkerfrom'@ohos.worker'functioncompressImage(path:string):Promise<string>{returnnewPromise((resolve)=>{constw=newworker.ThreadWorker('entry/ets/workers/ImageWorker.ets')w.postMessage({path})w.onmessage=(e)=>{resolve(e.data.outputPath)w.terminate()}})}这段代码乍一看挺像回事,对吧?但问题太多了。
第一,worker.ThreadWorker在 HarmonyOS Next 里路径已经变了,应该是@kit.ArkTS或者@ohos.worker下的ThreadWorker,但构造参数格式不对。第二,onmessage的写法是旧版 API,新版本用的是on('message', callback)。第三也是最隐蔽的 —— 它居然在 Promise 里直接resolve后就terminate(),如果 worker 还没执行完,整个线程被杀了,Promise 永远挂在那。
实际上 ArkTS 里图片压缩更推荐用TaskPool,而不是直接操作 Worker:
import{taskpool}from'@kit.ArkTS'@ConcurrentfunctioncompressTask(srcPath:string,quality:number):string{// 实际的压缩逻辑,运行在后台线程constdestPath=getContext().cacheDir+'/compressed_'+Date.now()+'.jpg'// ... imageKit 压缩调用returndestPath}asyncfunctioncompressImage(path:string):Promise<string>{consttask=newtaskpool.Task(compressTask,path,85)returnawaittaskpool.execute(task)asstring}AI 没推荐TaskPool,是因为它训练数据里关于 HarmonyOS Next 的资料太少了。2024 年之前的文档主要讲的是 Worker,TaskPool 是后来推的,AI 的 “知识” 还停留在旧版本。
那 AI 完全不能用吗?
也不是。有两个场景我反而建议你让 AI 代劳:
一是纯 UI 布局。你要写一个复杂的 Grid 或者 List,AI 生成的代码基本能跑。它可能会把ListItem的嵌套层级搞得多一点,但你调调就行了,比自己从零写快。
二是类型定义和接口。你给 AI 一份 JSON 样例,让它生成对应的 ArkTS interface,这个准确率很高。至少比手写快,而且不容易漏字段。
// 这种活丢给 AI,省时间interfaceApiResponse{code:numberdata:UserInfo message:string}interfaceUserInfo{uid:stringnickname:stringavatarUrl:stringvipLevel:numbercreatedAt:number}但记住一个原则:凡是涉及到 “运行时行为” 的代码 —— 状态什么时候刷新、页面什么时候销毁、异步任务怎么调度 —— 你都别信 AI。它对这些的理解大概是 “看过文档但没写过项目” 的水平。
一个小技巧
如果你非要用 AI 写 ArkTS,我的建议是:别问"怎么写",问"这样写对不对"。
比如你已经写了一个@State的组件,不确定父子传参有没有问题,把代码贴给 AI,让它 review。这种 “纠错模式” 比 “生成模式” 准确率高得多,因为 AI 做判断比做创造要靠谱。
还有一个招:在提示词里强制加上 “使用 HarmonyOS Next API 11+ 的语法”。虽然不一定完全管用,但至少能降低它给你返回 2023 年废弃代码的概率。
反正我测完这 37 个案例之后,再写 ArkTS 的时候,AI 帮我写的代码我都会本能地先扫一眼装饰器用得对不对。这习惯挺好,就是有点心累。
你平时用 AI 写鸿蒙代码吗?翻车最狠的一次是什么时候?
本文遵循 MIT 协议,转载请注明出处。