鸿蒙 组件内状态变量 V1 到 V2 迁移
本文介绍了鸿蒙状态管理从V1迁移到V2的完整方案。V2版本通过装饰器重构和功能增强,解决了V1在嵌套类观测、复杂数据类型处理等局限性。主要内容包括:1)装饰器映射关系,如@State拆分为@Local/@Param+@Once;2)详细迁移方案,涵盖@State、@Link、@Prop等装饰器的代码转换示例;3)新增@Computed计算属性优化性能;4)迁移顺序建议和注意事项。V2通过更清晰的职
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、概述
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 迁移顺序
-
先迁移数据类:将
@Observed类迁移到@ObservedV2+@Trace -
再迁移简单组件:从叶子组件开始,逐步向上迁移
-
最后处理复杂交互:处理
@Link、@Provide等复杂场景
3.2 注意事项
-
@Local禁止外部初始化,需要使用@Param+@Once替代 -
@Param默认只读,需要修改时使用@Once或事件回调 -
@Monitor可以同时监听多个变量,减少回调函数数量 -
@Computed可以有效优化性能,减少重复计算
3.3 性能优化点
-
合理使用
@Computed缓存计算结果 -
使用
@Monitor替代多个@Watch回调 -
复杂对象使用
@ObservedV2+@Trace实现细粒度更新
更多推荐


所有评论(0)