本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、概述

1.1 背景介绍

鸿蒙状态管理从 V1 升级到 V2,带来了更强大的状态管理能力和更简洁的 API 设计。V2 版本解决了 V1 在嵌套类观测、复杂数据类型处理等方面的局限性。

1.2 装饰器映射关系

V1 装饰器 V2 装饰器 主要变化
@State @Local / @Param+@Once 拆分功能,明确职责
@Prop @Param 只读性增强
@Link @Param+@Event 双向绑定方式改变
@ObjectLink @Param+@Event 简化复杂对象处理
@Provide/@Consume @Provider/@Consumer 功能增强,支持重载
@Watch @Monitor 支持多变量监听和变化值获取
无对应 @Computed 新增计算属性

二、迁移方案

2.1 @State → @Local/@Param+@Once

简单类型迁移

V1 实现:

@Entry
@Component
struct CounterView {
  @State count: number = 0;

  build() {
    Column() {
      Text(this.count.toString())
        .onClick(() => {
          this.count++;
        })
    }
  }
}

V2 迁移:

@Entry
@ComponentV2
struct CounterView {
  @Local count: number = 0;

  build() {
    Column() {
      Text(this.count.toString())
        .onClick(() => {
          this.count++;
        })
    }
  }
}
复杂对象迁移

V1 实现:

class UserInfo {
  score: number = 0;
  level: string = 'beginner';
}

@Component
@Entry
struct UserProfile {
  @State user: UserInfo = new UserInfo();

  build() {
    Column() {
      Text(`Score: ${this.user.score}`)
      Button('Increase Score')
        .onClick(() => {
          this.user.score++; // V1可观察第一层变化
        })
    }
  }
}

V2 迁移:

@ObservedV2
class UserInfo {
  @Trace score: number = 0;
  @Trace level: string = 'beginner';
}

@ComponentV2
@Entry
struct UserProfile {
  @Local user: UserInfo = new UserInfo();

  build() {
    Column() {
      Text(`Score: ${this.user.score}`)
      Button('Increase Score')
        .onClick(() => {
          this.user.score++; // 需要@ObservedV2+@Trace
        })
    }
  }
}
外部初始化迁移

V1 实现:

@Component
struct ProgressBar {
  @State progress: number = 0;

  build() {
    Column() {
      Text(`Progress: ${this.progress}%`)
    }
  }
}

@Entry
@Component
struct AppScreen {
  build() {
    Column() {
      ProgressBar({ progress: 50 }) // 外部初始化
    }
  }
}

V2 迁移:

@ComponentV2
struct ProgressBar {
  @Param @Once progress: number = 0;

  build() {
    Column() {
      Text(`Progress: ${this.progress}%`)
    }
  }
}

@Entry
@ComponentV2
struct AppScreen {
  build() {
    Column() {
      ProgressBar({ progress: 50 })
    }
  }
}

2.2 @Link → @Param+@Event

V1 实现:

@Component
struct VolumeControl {
  @Link volume: number;

  build() {
    Column() {
      Text(`Volume: ${this.volume}`)
      Button('Volume Up')
        .onClick(() => {
          this.volume += 10;
        })
    }
  }
}

@Entry
@Component
struct AudioPlayer {
  @State currentVolume: number = 50;

  build() {
    Column() {
      Text(`Current: ${this.currentVolume}`)
      VolumeControl({ volume: $currentVolume })
    }
  }
}

V2 迁移:

@ComponentV2
struct VolumeControl {
  @Param volume: number = 0;
  @Event increaseVolume: () => void;

  build() {
    Column() {
      Text(`Volume: ${this.volume}`)
      Button('Volume Up')
        .onClick(() => {
          this.increaseVolume();
        })
    }
  }
}

@Entry
@ComponentV2
struct AudioPlayer {
  @Local currentVolume: number = 50;

  build() {
    Column() {
      Text(`Current: ${this.currentVolume}`)
      VolumeControl({ 
        volume: this.currentVolume, 
        increaseVolume: () => this.currentVolume += 10 
      })
    }
  }
}

2.3 @Prop → @Param

简单类型迁移

V1 实现:

@Component
struct DisplayText {
  @Prop content: string;

  build() {
    Text(this.content)
  }
}

V2 迁移:

@ComponentV2
struct DisplayText {
  @Param content: string = '';

  build() {
    Text(this.content)
  }
}
复杂对象深拷贝

V1 实现:

class Settings {
  brightness: number = 80;
  contrast: number = 60;
}

@Component
struct DisplaySettings {
  @Prop settings: Settings;

  build() {
    Column() {
      Text(`Brightness: ${this.settings.brightness}`)
      Button('Adjust')
        .onClick(() => {
          this.settings.brightness += 5; // 不影响父组件
        })
    }
  }
}

V2 迁移:

@ObservedV2
class Settings {
  @Trace brightness: number = 80;
  @Trace contrast: number = 60;
  
  copy(): Settings {
    const newSettings = new Settings();
    newSettings.brightness = this.brightness;
    newSettings.contrast = this.contrast;
    return newSettings;
  }
}

@ComponentV2
struct DisplaySettings {
  @Param settings: Settings = new Settings();

  build() {
    Column() {
      Text(`Brightness: ${this.settings.brightness}`)
      Button('Adjust')
        .onClick(() => {
          this.settings.brightness += 5;
        })
    }
  }
}

@Entry
@ComponentV2
struct SystemSettings {
  @Local systemSettings: Settings = new Settings();

  build() {
    Column() {
      DisplaySettings({ settings: this.systemSettings.copy() })
    }
  }
}
子组件修改本地值

V1 实现:

@Component
struct LocalCounter {
  @Prop localCount: number = 0;

  build() {
    Column() {
      Text(`${this.localCount}`)
      Button('Local Add')
        .onClick(() => {
          this.localCount += 100; // 仅本地修改
        })
    }
  }
}

V2 迁移:

@ComponentV2
struct LocalCounter {
  @Local localCount: number = 0;
  @Param externalCount: number = 0;
  
  @Monitor('externalCount')
  onExternalChange(monitor: IMonitor) {
    console.info(`External changed to ${monitor.value()?.now}`);
    this.localCount = this.externalCount;
  }

  build() {
    Column() {
      Text(`${this.localCount}`)
      Button('Local Add')
        .onClick(() => {
          this.localCount += 100; // 仅本地修改
        })
    }
  }
}

@Entry
@ComponentV2
struct ParentView {
  @Local mainCount: number = 10;

  build() {
    Column() {
      Button('Parent Add')
        .onClick(() => {
          this.mainCount += 1;
        })
      LocalCounter({ externalCount: this.mainCount })
    }
  }
}

2.4 @Provide/@Consume → @Provider/@Consumer

别名匹配规则

V1 实现:

@Component
struct UserCard {
  @Consume('username') userName: string;
  @Consume displayName: string;

  build() {
    Column() {
      Text(this.userName)
      Text(this.displayName)
    }
  }
}

@Entry
@Component
struct AppContainer {
  @Provide('username') name: string = 'John Doe';

  build() {
    Column() {
      UserCard()
    }
  }
}

V2 迁移:

@ComponentV2
struct UserCard {
  @Consumer('username') userName: string = 'default';
  @Consumer() displayName: string = 'default';

  build() {
    Column() {
      Text(this.userName)   // 显示 John Doe
      Text(this.displayName) // 显示 default
    }
  }
}

@Entry
@ComponentV2
struct AppContainer {
  @Provider('username') name: string = 'John Doe';

  build() {
    Column() {
      UserCard()
    }
  }
}
本地初始化支持

V2 新特性:

@ComponentV2
struct ThemeConsumer {
  @Consumer() themeColor: string = 'light'; // 支持本地默认值

  build() {
    Column() {
      Text(`Theme: ${this.themeColor}`)
        .backgroundColor(this.themeColor === 'dark' ? Color.Black : Color.White)
    }
  }
}
外部初始化处理

V1 实现:

@Entry
@Component
struct MainApp {
  @State appTheme: string = 'dark';

  build() {
    Column() {
      ThemeProvider({ initialTheme: this.appTheme })
    }
  }
}

@Component
struct ThemeProvider {
  @Provide currentTheme: string = 'light';

  build() {
    Column() {
      ThemeConsumer()
    }
  }
}

V2 迁移:

@Entry
@ComponentV2
struct MainApp {
  @Local appTheme: string = 'dark';

  build() {
    Column() {
      ThemeProvider({ initialTheme: this.appTheme })
    }
  }
}

@ComponentV2
struct ThemeProvider {
  @Param @Once initialTheme: string = 'light';
  @Provider() currentTheme: string = this.initialTheme;

  build() {
    Column() {
      ThemeConsumer()
    }
  }
}
重载支持

V2 默认支持:

@Entry
@ComponentV2
struct RootView {
  @Provider('apiKey') apiKey: string = 'parent-key';

  build() {
    Column() {
      MiddleLayer()
    }
  }
}

@ComponentV2
struct MiddleLayer {
  @Provider() apiKey: string = 'child-key'; // 默认重载父级

  build() {
    Column() {
      ApiConsumer()
    }
  }
}

@ComponentV2
struct ApiConsumer {
  @Consumer() apiKey: string = 'default';

  build() {
    Text(`API Key: ${this.apiKey}`) // 显示 child-key
  }
}

2.5 @Watch → @Monitor

单变量监听

V1 实现:

@Entry
@Component
struct StockMonitor {
  @State @Watch('onPriceChange') stockPrice: number = 100;

  onPriceChange(): void {
    console.info(`Price updated: ${this.stockPrice}`);
  }

  build() {
    Column() {
      Text(`Price: $${this.stockPrice}`)
      Button('Change Price')
        .onClick(() => {
          this.stockPrice += 5;
        })
    }
  }
}

V2 迁移:

@Entry
@ComponentV2
struct StockMonitor {
  @Local stockPrice: number = 100;

  @Monitor('stockPrice')
  onPriceChange(monitor: IMonitor) {
    console.info(`Price changed from $${monitor.value()?.before} to $${monitor.value()?.now}`);
  }

  build() {
    Column() {
      Text(`Price: $${this.stockPrice}`)
      Button('Change Price')
        .onClick(() => {
          this.stockPrice += 5;
        })
    }
  }
}
多变量监听

V1 实现:

@Entry
@Component
struct MultiWatcher {
  @State @Watch('onTempChange') temperature: number = 25;
  @State @Watch('onHumidityChange') humidity: number = 60;

  onTempChange(): void {
    console.info(`Temperature: ${this.temperature}°C`);
  }

  onHumidityChange(): void {
    console.info(`Humidity: ${this.humidity}%`);
  }

  build() {
    Column() {
      Text(`Temp: ${this.temperature}°C`)
      Text(`Humidity: ${this.humidity}%`)
      Button('Change Both')
        .onClick(() => {
          this.temperature += 1;
          this.humidity += 5;
        })
    }
  }
}

V2 迁移:

@Entry
@ComponentV2
struct MultiMonitor {
  @Local temperature: number = 25;
  @Local humidity: number = 60;

  @Monitor('temperature', 'humidity')
  onEnvironmentChange(monitor: IMonitor) {
    monitor.dirty.forEach((field: string) => {
      console.info(`${field} changed from ${monitor.value(field)?.before} to ${monitor.value(field)?.now}`);
    });
  }

  build() {
    Column() {
      Text(`Temp: ${this.temperature}°C`)
      Text(`Humidity: ${this.humidity}%`)
      Button('Change Both')
        .onClick(() => {
          this.temperature += 1;
          this.humidity += 5;
        })
    }
  }
}

2.6 @Computed 计算属性

V1 无计算属性:

@Entry
@Component
struct ShoppingCart {
  @State items: string[] = ['Apple', 'Banana', 'Orange'];
  @State itemPrices: number[] = [2, 1, 3];

  build() {
    Column() {
      // 每次渲染都重复计算
      Text(`Total: $${this.itemPrices.reduce((sum, price) => sum + price, 0)}`)
      Text(`Items: ${this.items.join(', ')}`)
      
      ForEach(this.items, (item: string) => {
        Text(item)
      })
      
      Button('Add Item')
        .onClick(() => {
          this.items.push('Grape');
          this.itemPrices.push(4);
        })
    }
  }
}

V2 使用计算属性:

@Entry
@ComponentV2
struct ShoppingCart {
  @Local items: string[] = ['Apple', 'Banana', 'Orange'];
  @Local itemPrices: number[] = [2, 1, 3];

  @Computed
  get totalPrice(): number {
    return this.itemPrices.reduce((sum, price) => sum + price, 0);
  }

  @Computed
  get itemsDescription(): string {
    return this.items.join(', ');
  }

  @Computed
  get itemCount(): number {
    return this.items.length;
  }

  build() {
    Column() {
      Text(`Total: $${this.totalPrice}`) // 缓存计算结果
      Text(`Items: ${this.itemsDescription}`)
      Text(`Count: ${this.itemCount}`)
      
      ForEach(this.items, (item: string) => {
        Text(item)
      })
      
      Button('Add Item')
        .onClick(() => {
          this.items.push('Grape');
          this.itemPrices.push(4);
        })
    }
  }
}

三、总结

3.1 迁移顺序

  1. 先迁移数据类:将 @Observed 类迁移到 @ObservedV2 + @Trace

  2. 再迁移简单组件:从叶子组件开始,逐步向上迁移

  3. 最后处理复杂交互:处理 @Link@Provide 等复杂场景

3.2 注意事项

  • @Local 禁止外部初始化,需要使用 @Param + @Once 替代

  • @Param 默认只读,需要修改时使用 @Once 或事件回调

  • @Monitor 可以同时监听多个变量,减少回调函数数量

  • @Computed 可以有效优化性能,减少重复计算

3.3 性能优化点

  • 合理使用 @Computed 缓存计算结果

  • 使用 @Monitor 替代多个 @Watch 回调

  • 复杂对象使用 @ObservedV2 + @Trace 实现细粒度更新

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐