ArkTS V1 装饰器总结

📌 快速概览

V1 装饰器是 ArkTS 的状态管理系统,通过装饰器语法实现数据驱动 UI 更新。

核心理念:数据驱动 UI、声明式开发、分层管理


🎯 装饰器体系

V1装饰器体系
├─ 组件状态装饰器
│  ├─ @State         ─── 组件内部状态
│  ├─ @Prop          ─── 父到子单向传递
│  ├─ @Link          ─── 父子双向绑定
│  ├─ @Provide       ─── 向后代提供数据
│  ├─ @Consume       ─── 从祖先获取数据
│  ├─ @Observed      ─── 标记类可观察
│  └─ @ObjectLink    ─── 深度观察对象
│
├─ 监听装饰器
│  └─ @Watch         ─── 监听状态变化
│
├─ 构建装饰器
│  └─ @BuilderParam  ─── 自定义UI构建
│
└─ 存储装饰器
   ├─ LocalStorage   ─── 页面级存储
   └─ AppStorage     ─── 应用级存储

📖 组件状态装饰器

@State - 组件内部状态

作用:定义组件的私有状态,状态变化触发 UI 更新。

特点

  • 只能在组件内部使用,外部无法传入
  • 必须初始化
  • 浅观察:只能监听第一层属性变化(对象的嵌套属性变化不会触发更新)

使用场景

  • 计数器、开关状态、表单输入等组件内部状态
  • 不需要与其他组件共享的数据

示例

@State count: number = 0          // ✅ 基本类型
@State list: string[] = []        // ✅ 数组
@State user: { name: string } = { name: '张三' }  // ✅ 对象

// ✅ 能触发更新
this.count = 100
this.list.push('新元素')
this.user = { name: '李四' }

// ❌ 不能触发更新(浅观察)
this.user.name = '李四'  // 第二层属性

@Prop - 父到子单向传递

作用:从父组件传递数据到子组件,子组件只能读取,不能修改父组件的数据。

特点

  • 单向数据流:父 → 子
  • 值传递(深拷贝),子组件修改不影响父组件
  • 可选初始化(可设置默认值)

使用场景

  • 子组件只需要展示父组件的数据,不需要修改
  • 配置型组件(如标题、颜色、尺寸等属性)

示例

// 父组件
@State message: string = 'Hello'
ChildComponent({ message: this.message })

// 子组件
@Prop message: string = '默认值'  // 可选默认值

// 子组件修改只影响自己,不影响父组件
this.message = 'World'  // ❌ 不影响父组件

@Link - 父子双向绑定

作用:父子组件共享同一份数据,任意一方修改都会同步更新。

特点

  • 双向数据流:父 ↔ 子
  • 引用传递,父子共享同一个数据
  • 使用 $ 语法传递
  • 不能初始化,必须由父组件传入
  • 浅观察

使用场景

  • 表单输入:子组件修改需要同步到父组件
  • 开关状态:父子组件都需要修改和读取
  • 简单的父子数据共享

示例

// 父组件
@State count: number = 0
ChildComponent({ count: $count })  // 使用 $ 语法

// 子组件
@Link count: number  // 不能初始化

// 父子任一方修改,都会同步
this.count++  // ✅ 父子同步更新

@Provide / @Consume - 跨级传递

作用:实现跨多级组件的数据传递,无需逐层传递。

特点

  • 祖先组件用 @Provide 提供数据
  • 后代组件用 @Consume 消费数据
  • 双向绑定:后代修改会同步到祖先
  • 通过相同变量名或别名关联

使用场景

  • 主题配置:根组件提供主题,所有子孙组件都能访问
  • 用户信息:登录后的用户信息全局共享
  • 多层嵌套组件间的数据共享

示例

// 祖先组件
@Provide count: number = 0
// 或使用别名
@Provide('theme') currentTheme: string = 'light'

// 后代组件(中间层无需传递)
@Consume count: number
@Consume('theme') theme: string

// 后代修改会同步到祖先
this.count++  // ✅ 祖先同步更新

@Observed / @ObjectLink - 深度观察

作用:实现对象嵌套属性的深度观察,解决 @State/@Link 只能观察第一层的问题。

特点

  • @Observed:装饰类,使类实例可被观察
  • @ObjectLink:装饰变量,接收可观察对象
  • 深度观察:对象任意层级的属性变化都能触发更新
  • 双向绑定
  • 必须配合使用

使用场景

  • 复杂对象:用户信息、商品详情等多层嵌套对象
  • 列表项:数组中的对象需要单独传递给子组件
  • 购物车、表单等需要修改嵌套属性的场景

示例

// 1. 定义类,加 @Observed
@Observed
class Person {
  name: string
  age: number
}

// 2. 父组件用 @State 持有
@State person: Person = new Person('张三', 25)

// 3. 传递给子组件
ChildComponent({ person: this.person })

// 4. 子组件用 @ObjectLink 接收
@ObjectLink person: Person

// ✅ 嵌套属性变化也能触发更新
this.person.name = '李四'  // ✅ 深度观察
this.person.age++          // ✅ 深度观察

🔍 监听装饰器

@Watch - 监听状态变化

作用:监听状态变量的变化,在变化时执行自定义回调函数。

特点

  • 可监听 @State、@Prop、@Link 等装饰的变量
  • 状态变化后自动执行回调
  • 适合执行副作用操作

使用场景

  • 表单验证:输入变化时实时验证
  • 数据联动:价格或数量变化时自动计算总价
  • 本地存储:状态变化时自动保存到本地
  • 日志记录:记录状态变化历史

示例

@State @Watch('onCountChange') count: number = 0

onCountChange() {
  console.log(`count 变化了: ${this.count}`)
  // 执行验证、保存、计算等操作
}

🏗️ 构建装饰器

@BuilderParam - 自定义 UI 构建

作用:允许父组件向子组件传递自定义 UI 构建函数,实现插槽功能。

特点

  • 提高组件复用性
  • 支持传递带参数的构建函数
  • 可设置默认 UI

使用场景

  • 卡片组件:自定义卡片内容
  • 对话框:自定义头部、内容、底部
  • 列表组件:自定义列表项渲染
  • 容器组件:需要自定义子内容的场景

示例

// 子组件
@Component
struct Card {
  @BuilderParam content: () => void

  build() {
    Column() {
      Text('卡片标题')
      this.content()  // 渲染父组件传入的UI
    }
  }
}

// 父组件
@Builder myContent() {
  Text('自定义内容')
  Button('自定义按钮')
}

Card({ content: this.myContent })

💾 存储装饰器

LocalStorage - 页面级存储

作用:页面级的共享状态,同一页面内的组件共享数据。

装饰器

  • @LocalStorageLink:双向绑定,组件修改会同步到 LocalStorage
  • @LocalStorageProp:单向绑定,只读,修改不影响 LocalStorage

使用场景

  • 页面内多个组件共享的临时数据
  • 页面级配置(如筛选条件、显示模式)
  • 不需要跨页面共享的数据

示例

// 创建 LocalStorage 实例
const storage = new LocalStorage({ count: 0 })

@Entry(storage)
@Component
struct Page {
  @LocalStorageLink('count') count: number = 0  // 双向
  @LocalStorageProp('count') readCount: number = 0  // 单向只读
}

AppStorage - 应用级存储

作用:应用级的全局共享状态,所有页面都可以访问。

装饰器

  • @StorageLink:双向绑定,修改会全局同步
  • @StorageProp:单向绑定,只读

使用场景

  • 用户登录信息(用户名、头像)
  • 主题配置(暗黑模式、语言)
  • 全局设置(字体大小、通知开关)
  • 跨页面共享的数据

示例

// 初始化全局数据
AppStorage.SetOrCreate('theme', 'light')

// 任意页面使用
@StorageLink('theme') theme: string = 'light'  // 双向
@StorageProp('theme') readTheme: string = 'light'  // 单向只读

📊 装饰器对比表

组件状态装饰器对比

装饰器 数据流 观察深度 初始化 典型场景
@State 组件内部 浅(第一层) 必须 组件私有状态
@Prop 父 → 子(单向) 可选 子组件只读数据
@Link 父 ↔ 子(双向) 不能 简单的父子共享数据
@Provide 祖先 → 后代 必须 跨级数据提供
@Consume 祖先 ↔ 后代 不能 跨级数据消费
@ObjectLink 父 ↔ 子(双向) 深(所有层) 不能 嵌套对象深度观察

存储装饰器对比

装饰器 作用域 数据流 典型场景
@LocalStorageLink 页面级 双向 页面内组件共享数据
@LocalStorageProp 页面级 单向 页面内组件只读数据
@StorageLink 应用级 双向 全局共享数据(主题、用户)
@StorageProp 应用级 单向 全局只读数据(应用配置)

🎯 选择决策

快速决策树

需要传递数据吗?
├─ 否 → @State(组件内部状态)
│
└─ 是 → 传递给谁?
    ├─ 子组件
    │   ├─ 只读?→ @Prop
    │   ├─ 可改?
    │   │   ├─ 简单对象 → @Link
    │   │   └─ 嵌套对象 → @ObjectLink
    │
    ├─ 跨多级 → @Provide/@Consume
    │
    └─ 全局共享
        ├─ 页面内 → LocalStorage
        └─ 全应用 → AppStorage

场景选择建议

场景 推荐装饰器
计数器、开关 @State
配置属性(颜色、大小) @Prop
表单输入 @Link
用户信息、商品详情 @ObjectLink
主题配置 @Provide/@Consume
监听输入验证 @Watch
自定义列表项 @BuilderParam
页面筛选条件 LocalStorage
全局主题、用户信息 AppStorage

🎓 核心要点

浅观察 vs 深度观察

浅观察(@State、@Prop、@Link):

  • 只能观察第一层属性变化
  • 修改嵌套属性不会触发 UI 更新
  • 解决方法:整体替换对象或使用 @Observed + @ObjectLink

深度观察(@Observed + @ObjectLink):

  • 可以观察任意层级的属性变化
  • 需要给类加 @Observed 装饰器
  • 嵌套类也需要加 @Observed

记忆口诀

State 组件私有,Prop 单向只读
Link 双向美元,Provide 跨级通
Observed 深观察,Watch 来监听
BuilderParam 插槽,Storage 存全局

📌 V1 vs V2

特性 V1 装饰器 V2 装饰器(新一代)
观察粒度 浅观察(第一层) 精细化观察(属性级)
性能 整个对象变化才更新 只更新变化的属性
学习曲线 相对简单 更复杂但更强大
适用场景 简单状态管理 复杂嵌套对象、性能敏感

V2 装饰器

  • @ObservedV2 + @Trace:替代 @Observed/@ObjectLink
  • @Local:替代 @State
  • @Param:替代 @Prop/@Link
  • @Event:事件通信
  • @Monitor:替代 @Watch

📚 总结

核心原则

  1. 数据驱动:状态变化自动更新 UI
  2. 单向数据流优先:优先使用 @State + @Prop
  3. 按需选择:根据场景选择合适的装饰器
  4. 合理拆分:大组件拆分为小组件,提高复用性

何时使用 V1

✅ 推荐场景

  • 现有项目维护
  • 简单的状态管理
  • 学习 ArkTS 基础
  • 兼容性要求高

⚠️ 考虑 V2 场景

  • 新项目开发
  • 复杂的嵌套对象
  • 性能敏感应用
  • 需要精细化更新
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐