HarmonyOS @Local 完全指南:V2 组件私有状态的不可变更新模式
前言Local是 HarmonyOS 状态管理 V2 中替代State的私有状态装饰器。与State相比Local有两项关键改进严格私有性父组件无任何途径读写它和更清晰的更新语义明确要求不可变更新模式让数据流方向一目了然。本文用一个完整的 TodoList 演示Local操作数组、对象和基本类型的全套模式以及 ArkTS 严格模式下的常见编译陷阱。核心 APILocal 声明语法Entry ComponentV2 struct MyPage { Local count: number 0 // 基本类型 Local title: string 待办清单 // 字符串 Local todos: Todo[] [] // 数组 Local filter: string all // 枚举字符串 }触发 UI 更新的规则数据类型触发更新的写法不触发更新的写法基本类型this.count✅—字符串this.title 新标题✅—数组this.todos [...this.todos, item]✅this.todos.push(item)❌对象this.profile { ...fields }✅this.profile.name 新❌核心规则Local检测的是引用变化不是内容变化。数组和对象必须生成新引用才能触发重渲染基本类型赋值本身就是新值天然触发。Local vs State 关键差异// V1State 可被父组件通过 ObjectLink 访问 State items: Todo[] [] // 父组件可通过 ObjectLink 读写 // V2Local 严格私有外部无法读写 Local items: Todo[] [] // 只有本组件内部能修改实现思路用一个 TodoList 覆盖Local的三种数据类型Local todos: Todo[]展示数组的不可变增删改Local filter: string展示字符串赋值Local nextId: number展示基本类型自增。getStats()返回派生统计数据后续 article68 中Computed会自动缓存此类计算。ArkTS 不允许对象展开{ ...obj }所有对象更新须逐字段构造在第 3、4 步中重点展示。逐步实现第 1 步定义接口和 Local 状态interface Todo { id: number text: string done: boolean } Entry ComponentV2 struct Index { Local todos: Todo[] [] // 任务列表 Local nextId: number 1 // 自增 ID基本类型 Local filter: string all // 过滤器字符串 Local inputText: string // 输入框绑定三种类型一次覆盖数组、基本类型、字符串触发方式各不相同。第 2 步添加任务——数组不可变更新private addTodo(): void { const text this.inputText.trim() if (!text) return // [...旧数组, 新元素] 产生新数组引用触发 Local 更新 this.todos [...this.todos, { id: this.nextId, text, done: false }] this.nextId // 基本类型直接赋值 this.inputText // 清空输入框 }[...this.todos, newItem]是数组的标准不可变添加模式等价于 V1 的this.todos this.todos.concat([newItem])。第 3 步切换完成状态——map 返回新数组private toggleDone(id: number): void { // ArkTS 禁止对象展开 { ...t }须逐字段构造新对象 this.todos this.todos.map(t t.id id ? { id: t.id, text: t.text, done: !t.done } // 新对象 : t ) }map()天然返回新数组配合逐字段构造对象一行完成不可变更新。注意 ArkTS 严格模式禁止{ ...t, done: !t.done }arkts-no-spread错误必须显式列出每个字段。第 4 步删除任务——filter 返回新数组private removeTodo(id: number): void { this.todos this.todos.filter(t t.id ! id) }filter()同样返回新数组是最简洁的不可变删除写法。第 5 步过滤显示与统计private filteredTodos(): Todo[] { if (this.filter active) return this.todos.filter(t !t.done) if (this.filter done) return this.todos.filter(t t.done) return this.todos } private getStats(): Stats { const done this.todos.filter(t t.done).length return { total: this.todos.length, done, active: this.todos.length - done } }切换this.filter字符串值即触发重渲染filteredTodos()返回对应子集。完整代码// Article 62: Local 私有状态深度解析 interface Todo { id: number text: string done: boolean } interface Stats { total: number done: number active: number } Entry ComponentV2 struct Index { Local todos: Todo[] [] Local nextId: number 1 Local filter: string all Local inputText: string private addTodo(): void { const text this.inputText.trim() if (!text) return this.todos [...this.todos, { id: this.nextId, text, done: false }] this.nextId this.inputText } private toggleDone(id: number): void { this.todos this.todos.map(t t.id id ? { id: t.id, text: t.text, done: !t.done } : t ) } private removeTodo(id: number): void { this.todos this.todos.filter(t t.id ! id) } private getStats(): Stats { const done this.todos.filter(t t.done).length return { total: this.todos.length, done, active: this.todos.length - done } } private filteredTodos(): Todo[] { if (this.filter active) return this.todos.filter(t !t.done) if (this.filter done) return this.todos.filter(t t.done) return this.todos } private filterLabel(f: string): string { if (f active) return 待完成 if (f done) return 已完成 return 全部 } build() { Column({ space: 0 }) { Row() { Text(Local 私有状态).fontSize(18).fontWeight(FontWeight.Bold).fontColor(#1a1a1a) } .width(100%).height(56).backgroundColor(#ffffff).padding({ left: 16 }) .border({ width: { bottom: 1 }, color: #f0f0f0 }) Scroll() { Column({ space: 12 }) { Row({ space: 0 }) { Column({ space: 4 }) { Text(${this.getStats().total}).fontSize(28).fontWeight(FontWeight.Bold).fontColor(#1a1a1a) Text(全部).fontSize(11).fontColor(#aaa) } .layoutWeight(1).alignItems(HorizontalAlign.Center) Column({ space: 4 }) { Text(${this.getStats().active}).fontSize(28).fontWeight(FontWeight.Bold).fontColor(#0066ff) Text(待完成).fontSize(11).fontColor(#aaa) } .layoutWeight(1).alignItems(HorizontalAlign.Center) Column({ space: 4 }) { Text(${this.getStats().done}).fontSize(28).fontWeight(FontWeight.Bold).fontColor(#07c160) Text(已完成).fontSize(11).fontColor(#aaa) } .layoutWeight(1).alignItems(HorizontalAlign.Center) } .width(100%).backgroundColor(#ffffff).borderRadius(12).padding(16) .margin({ left: 12, right: 12 }) Column({ space: 10 }) { Text(添加任务).fontSize(13).fontColor(#888).fontWeight(FontWeight.Medium).width(100%) Row({ space: 10 }) { TextInput({ placeholder: 输入任务内容…, text: this.inputText }) .layoutWeight(1).height(44).borderRadius(22).fontSize(14) .backgroundColor(#f5f5f5).padding({ left: 16, right: 16 }) .onChange((v: string) { this.inputText v }) Button(添加) .height(44).borderRadius(22).fontSize(14).padding({ left: 20, right: 20 }) .backgroundColor(this.inputText.trim() ? #0066ff : #e0e0e0).fontColor(#fff) .onClick(() this.addTodo()) } } .width(100%).backgroundColor(#ffffff).borderRadius(12).padding(16) .margin({ left: 12, right: 12 }) Row({ space: 8 }) { ForEach([all, active, done] as string[], (f: string) { Text(this.filterLabel(f)) .fontSize(13).padding({ left: 16, right: 16, top: 6, bottom: 6 }).borderRadius(16) .fontColor(this.filter f ? #fff : #666) .backgroundColor(this.filter f ? #0066ff : #f0f0f0) .onClick(() { this.filter f }) }) } .width(100%).padding({ left: 16, right: 16 }) Column({ space: 8 }) { if (this.filteredTodos().length 0) { Text(暂无任务).fontSize(14).fontColor(#ccc).margin({ top: 20 }) } else { ForEach(this.filteredTodos(), (todo: Todo) { Row({ space: 12 }) { Text(todo.done ? ✅ : ⬜).fontSize(20) .onClick(() this.toggleDone(todo.id)) Text(todo.text) .fontSize(14).layoutWeight(1).fontColor(todo.done ? #bbb : #1a1a1a) .decoration({ type: todo.done ? TextDecorationType.LineThrough : TextDecorationType.None }) Text(✕).fontSize(16).fontColor(#ddd) .onClick(() this.removeTodo(todo.id)) } .width(100%).backgroundColor(#ffffff).borderRadius(12) .padding({ left: 16, right: 16, top: 14, bottom: 14 }) }) } } .width(100%).padding({ left: 12, right: 12, bottom: 24 }) } .width(100%).padding({ top: 12 }) } .layoutWeight(1).backgroundColor(#f8f8f8) } .width(100%).height(100%).backgroundColor(#f8f8f8) } }运行效果初始态列表为空三项统计均为 0添加 4 条任务后全部 4 / 待完成 2 / 已完成 2前两条显示删除线注意事项ArkTS 禁止对象展开{ ...obj, key: val }会触发arkts-no-spread编译错误必须逐字段构造新对象{ id: t.id, text: t.text, done: !t.done }。这是 ArkTS 与标准 TypeScript 的重要区别迁移代码时需全面替换。数组方法的可变/不可变区别push/pop/splice/sort原地变更→ 不触发Local更新map/filter/concat/[...arr]返回新数组→ 触发更新 始终使用返回新数组的方法。build() 内不能声明变量ForEach回调或其他 UI 构建函数内不允许const/let声明Only UI component syntax can be written here需提取为组件方法如filterLabel()。Local 严格私有父组件不能通过任何方式读取或修改子组件的Local状态。如果需要父写子用Paramarticle63子写父用Eventarticle64。派生计算的性能getStats()和filteredTodos()每次渲染都重新计算。当列表较大时1000 项应改用Computed缓存article68避免不必要的重复遍历。

相关新闻

B站评论数据采集系统:基于Selenium的完整评论层级爬取方案

B站评论数据采集系统:基于Selenium的完整评论层级爬取方案

B站评论数据采集系统:基于Selenium的完整评论层级爬取方案 【免费下载链接】BilibiliCommentScraper B站视频评论爬虫 Bilibili完整爬取评论数据,包括一级评论、二级评论、昵称、用户ID、发布时间、点赞数 项目地址: https://gitcode.com/gh_mirrors/b…

2026/7/26 14:00:24 阅读更多 →
阿里云百炼大模型API调用实战指南

阿里云百炼大模型API调用实战指南

1. 从零开始调用大模型API:完整指南 作为一名长期从事AI应用开发的工程师,我深知初学者在接触大模型API时的困惑。第一次调用API时,我也曾对着文档发愣,不知从何下手。本文将带你完整走通阿里云百炼大模型API的调用全流程&#x…

2026/7/26 14:00:24 阅读更多 →
ComfyUI-WD14-Tagger:AI绘画必备的智能图像标签识别工具

ComfyUI-WD14-Tagger:AI绘画必备的智能图像标签识别工具

ComfyUI-WD14-Tagger:AI绘画必备的智能图像标签识别工具 【免费下载链接】ComfyUI-WD14-Tagger A ComfyUI extension allowing for the interrogation of booru tags from images. 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-WD14-Tagger 你是否曾…

2026/7/26 14:00:24 阅读更多 →

最新新闻

终极C++代码保护指南:obfy框架如何让逆向工程成为噩梦

终极C++代码保护指南:obfy框架如何让逆向工程成为噩梦

终极C代码保护指南:obfy框架如何让逆向工程成为噩梦 【免费下载链接】obfy A tiny C obfuscation framework 项目地址: https://gitcode.com/gh_mirrors/ob/obfy 在软件开发领域,保护代码免受逆向工程攻击始终是开发者面临的重要挑战。obfy作为一…

2026/7/26 14:12:29 阅读更多 →
iR Engine完全指南:从安装到创建沉浸式3D社交世界的10个步骤

iR Engine完全指南:从安装到创建沉浸式3D社交世界的10个步骤

iR Engine完全指南:从安装到创建沉浸式3D社交世界的10个步骤 【免费下载链接】etherealengine iR Engine - Bringing us together on the open social spatial web. 🤖 🚀 👓 🕹️ 🧑🏿‍&#…

2026/7/26 14:12:29 阅读更多 →
AI辅助学术专著写作:工具组合与效率提升

AI辅助学术专著写作:工具组合与效率提升

1. 学术写作的智能化转型(开头段落自然引入主题,前100字内包含核心关键词) 最近两年,学术写作领域正在经历一场静悄悄的革命。作为在科研机构工作十余年的研究者,我亲眼见证了AI工具如何改变学术生产的每个环节。从文献…

2026/7/26 14:12:29 阅读更多 →
金融领域RAG问答系统优化实战:从42%到89%的准确率提升

金融领域RAG问答系统优化实战:从42%到89%的准确率提升

1. 项目背景与核心痛点 去年接手了一个金融行业的智能问答系统项目,客户要求实现基于自然语言的企业知识库问答功能。最初采用经典的RAG(Retrieval-Augmented Generation)架构,但在实际落地时发现效果远低于预期——检索准确率波动…

2026/7/26 14:12:29 阅读更多 →
如何用Python高效构建专业信用评分卡模型:scorecardpy完整指南

如何用Python高效构建专业信用评分卡模型:scorecardpy完整指南

如何用Python高效构建专业信用评分卡模型:scorecardpy完整指南 【免费下载链接】scorecardpy Scorecard Development in python, 评分卡 项目地址: https://gitcode.com/gh_mirrors/sc/scorecardpy 在金融风控领域,信用评分卡作为核心的风险评估工…

2026/7/26 14:12:28 阅读更多 →
Docker容器技术详解与CentOS 7实践指南

Docker容器技术详解与CentOS 7实践指南

1. 为什么我们需要Docker? 记得刚入行那会儿,每次在新服务器上部署应用都要重复安装各种依赖,配置环境变量,折腾半天才能跑起来。后来接触了虚拟机,虽然解决了环境隔离问题,但每次启动都要等好几分钟&#…

2026/7/26 14:11:28 阅读更多 →

日新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/26 0:00:31 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/26 0:00:31 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/26 0:00:31 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/26 0:00:31 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/26 0:00:31 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/26 0:00:31 阅读更多 →

月新闻