@ObjectLink和@Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步:
@Status
- 需要默认初始化
- @State装饰的变量支持初始化子组件的常规变量、@State、@Link、@Prop、@Provide
@status可以修饰什么
Object、class、string、number、boolean、enum类型,以及这些类型的数组。嵌套类型的场景请参考观察变化。
类型必须被指定。
不支持any,不支持简单类型和复杂类型的联合类型,不允许使undefined和null。
可以这么理解:@status
支持监听基本的类型和简单的嵌套。基本类型的监听不用说了。稍微复杂的,举例说明:
- 见高亮的代码,监听嵌套模型的属性失败
- 这里绑定object时,使用class或interface描述类型均可
ts
@Component
@Preview
@Entry
struct QuickDevView {
@State model: FatherModel = { name: '' }
build() {
Column() {
Text("SuperView:" + this.model.name)
Column() {
Text("Child Name:" + (this.model.child?.name ?? ''))
Text("Child Age:" + (this.model.child?.age ?? ''))
}
Button("sss")
.onClick(() => {
this.model.name = '老王'
if (!this.model.child) {
// 这个监听生效
this.model.child = { age: 12, name: '小王' }
} else {
// 这个嵌套类型的监听无效
this.model.child.name = '小李'
}
})
.margin({ top: 10 })
}
}
}
export interface FatherModel {
name: string
child ?: ChildModel
}
export interface ChildModel {
age ?: number
name ?: string
}
@Component
@Preview
@Entry
struct QuickDevView {
@State model: FatherModel = { name: '' }
build() {
Column() {
Text("SuperView:" + this.model.name)
Column() {
Text("Child Name:" + (this.model.child?.name ?? ''))
Text("Child Age:" + (this.model.child?.age ?? ''))
}
Button("sss")
.onClick(() => {
this.model.name = '老王'
if (!this.model.child) {
// 这个监听生效
this.model.child = { age: 12, name: '小王' }
} else {
// 这个嵌套类型的监听无效
this.model.child.name = '小李'
}
})
.margin({ top: 10 })
}
}
}
export interface FatherModel {
name: string
child ?: ChildModel
}
export interface ChildModel {
age ?: number
name ?: string
}
需要注意的是嵌套简单类型的数组可以触发更新(嵌套object不能触发push更新,只能触发赋值时的更新):
ts
@Component
struct QuickDevView {
@State model: FatherModel = { name: '' }
build() {
Column() {
Column() {
if (this.model.childrenList) {
ForEach(this.model.childrenList, (x:string) => {
Text(x)
ChildView({ name: x }).backgroundColor(Color.Yellow)
})
}
}.backgroundColor(Color.Green)
Button("click me")
.onClick(() => {
if (!this.model.childrenList) {
// 生效
this.model.childrenList = ['a']
} else {
// 生效(push/pop生效。shift不行)
this.model.childrenList.push('b')
}
})
.margin({ top: 10 })
}
.width('100%')
}
}
@Component
struct ChildView {
@Prop name: string = ''
build() {
Text(this.name)
}
}
export interface FatherModel {
name: string
child ?: ChildModel
childrenList ?: string[]
}
export class ChildModel {
age ?: number
name ?: string
}
@Component
struct QuickDevView {
@State model: FatherModel = { name: '' }
build() {
Column() {
Column() {
if (this.model.childrenList) {
ForEach(this.model.childrenList, (x:string) => {
Text(x)
ChildView({ name: x }).backgroundColor(Color.Yellow)
})
}
}.backgroundColor(Color.Green)
Button("click me")
.onClick(() => {
if (!this.model.childrenList) {
// 生效
this.model.childrenList = ['a']
} else {
// 生效(push/pop生效。shift不行)
this.model.childrenList.push('b')
}
})
.margin({ top: 10 })
}
.width('100%')
}
}
@Component
struct ChildView {
@Prop name: string = ''
build() {
Text(this.name)
}
}
export interface FatherModel {
name: string
child ?: ChildModel
childrenList ?: string[]
}
export class ChildModel {
age ?: number
name ?: string
}
注意问题
如下,增加了第三行代码后,发现嵌套类型的监听生效了。因为第三行代码触发了model更新(按官方的话说是冗余更新),造成了@status
可以监听嵌套的class/object的错觉。
js
Button("button click")
.onClick(() => {
this.model.name = '' + Math.random()
if (!this.model.child) {
// 这个监听生效
this.model.child = { age: 12, name: '小王' }
} else {
// 这个嵌套类型的监听生效了
this.model.child.name = '小李' + Math.random()
}
})
Button("button click")
.onClick(() => {
this.model.name = '' + Math.random()
if (!this.model.child) {
// 这个监听生效
this.model.child = { age: 12, name: '小王' }
} else {
// 这个嵌套类型的监听生效了
this.model.child.name = '小李' + Math.random()
}
})
使用@Track装饰器可以避免冗余刷新: @Track装饰器
@Observed / @ObjectLink
可监听嵌套类的数据更新。
简单模型嵌套
注意以下示例, 父view中监听不到嵌套类型的更新;
js
@Observed
class BookName {
public nameSize: number;
constructor(nameSize: number) {
this.nameSize = nameSize;
}
}
@Observed
class Book {
public bookName: BookName;
constructor(bookName: BookName) {
this.bookName = bookName;
}
}
@Component
struct ChildView {
label: string = 'ViewC1';
@ObjectLink bookName: BookName;
build() {
Row() {
Column() {
Text(`${this.bookName.nameSize}`)
}
}
}
}
@Component
@Preview
@Entry
struct QuickDevView {
@State child: Book = new Book(new BookName(0));
build() {
Column() {
// 外部检测不到嵌套类的属性变化
Text(this.child.bookName.nameSize + '')
// 使用子视图结合 Observed ObjectLink 可以在子视图中触发更新
ChildView({
bookName: this.child.bookName
})
Button() {
Text('click').padding(20)
}.onClick(() => {
// 类嵌套类的话,嵌套类的属性变化是检测不到的:
this.child.bookName.nameSize += 10;
})
}.margin(40)
}
}
@Observed
class BookName {
public nameSize: number;
constructor(nameSize: number) {
this.nameSize = nameSize;
}
}
@Observed
class Book {
public bookName: BookName;
constructor(bookName: BookName) {
this.bookName = bookName;
}
}
@Component
struct ChildView {
label: string = 'ViewC1';
@ObjectLink bookName: BookName;
build() {
Row() {
Column() {
Text(`${this.bookName.nameSize}`)
}
}
}
}
@Component
@Preview
@Entry
struct QuickDevView {
@State child: Book = new Book(new BookName(0));
build() {
Column() {
// 外部检测不到嵌套类的属性变化
Text(this.child.bookName.nameSize + '')
// 使用子视图结合 Observed ObjectLink 可以在子视图中触发更新
ChildView({
bookName: this.child.bookName
})
Button() {
Text('click').padding(20)
}.onClick(() => {
// 类嵌套类的话,嵌套类的属性变化是检测不到的:
this.child.bookName.nameSize += 10;
})
}.margin(40)
}
}
模型嵌套模型数组
需要特别注意的数组循环那层,必须提取出子组件,否则监听不到。
ts
@Component
@Preview
@Entry
struct QuickDevView {
@State superModel: SuperModel = new SuperModel(
new MainModel('main',
new XTypeArray()));
build() {
Column() {
Text('QuickDevView arrayModel.length: ' + this.superModel.mainModel.arrayModel.length)
Divider()
MainView({
mainModel: this.superModel.mainModel
})
Divider()
Row() {
Button() {
Text('push')
.foregroundColor(Color.White)
.padding(20)
}.onClick(() => {
this.superModel.mainModel.arrayModel.push(new XTypeWrap(true, {
typeName: 'aaa'
}))
})
Button() {
Text('pop')
.foregroundColor(Color.White)
.padding(20)
}.onClick(() => {
if (this.superModel.mainModel.arrayModel.length) {
this.superModel.mainModel.arrayModel.shift()
}
})
Button() {
Text('清空')
.foregroundColor(Color.White)
.padding(20)
}.onClick(() => {
this.superModel.mainModel.arrayModel = new XTypeArray();
})
Button() {
Text('修改第二个元素')
.foregroundColor(Color.White)
.padding(20)
}.onClick(() => {
if (this.superModel.mainModel.arrayModel.length > 2) {
// 这里页面不刷新
this.superModel.mainModel.arrayModel[1].type.typeName = "续爱怒"
}
})
}
}.margin(60)
}
}
@Component
struct Lower2View {
@ObjectLink list: XTypeArray
build() {
Column() {
ForEach(this.list, (item: XTypeWrap) => {
Column() {
LowerView({ model: item })
}.borderWidth(1)
})
}
}
}
@Component
struct LowerView {
@ObjectLink model: XTypeWrap;
build() {
Column() {
Text('是否展开:' + this.model.isSelected)
.onClick(() => {
this.model.isSelected = !this.model.isSelected
})
Text(this.model.type.typeName)
Text(this.model.type.queueModel?.name ?? '空')
}
}
}
@Component
struct MainView {
@ObjectLink mainModel: MainModel
build() {
Column() {
Text('MainView:' + this.mainModel.arrayModel.length)
Text('Text:' + this.mainModel.name)
// ForEach(this.mainModel.arrayModel, (item: XTypeWrap) => {
// // Text(item.isSelected + 'AS')
// // Text(item.type.typeName)
// // Text(item.type.queueModel?.name ?? '空')
// // 先不管
// // reportModels ?: ListModelItem[]
// LowerView({ model: item })
// })
Lower2View({ list: this.mainModel.arrayModel })
}
}
}
@Observed
class SuperModel {
public mainModel: MainModel
constructor(mainModel: MainModel) {
this.mainModel = mainModel
}
}
@Observed
class MainModel {
public name: string
public arrayModel: XTypeArray
constructor(name: string, arrayModel: XTypeArray) {
this.name = name;
this.arrayModel = arrayModel;
}
}
@Observed
class XTypeArray extends Array<XTypeWrap> {
}
@Observed
export class XTypeWrap {
isSelected: boolean = false
type: InspectTypeModel
constructor(isSelected: boolean, type: InspectTypeModel) {
this.isSelected = isSelected
this.type = type
}
}
interface InspectTypeModel {
typeName: string
queueModel ?: NestedModel
reportModels ?: ListModelItem[]
}
interface NestedModel {
name: string
}
interface ListModelItem {
name: string
}
@Component
@Preview
@Entry
struct QuickDevView {
@State superModel: SuperModel = new SuperModel(
new MainModel('main',
new XTypeArray()));
build() {
Column() {
Text('QuickDevView arrayModel.length: ' + this.superModel.mainModel.arrayModel.length)
Divider()
MainView({
mainModel: this.superModel.mainModel
})
Divider()
Row() {
Button() {
Text('push')
.foregroundColor(Color.White)
.padding(20)
}.onClick(() => {
this.superModel.mainModel.arrayModel.push(new XTypeWrap(true, {
typeName: 'aaa'
}))
})
Button() {
Text('pop')
.foregroundColor(Color.White)
.padding(20)
}.onClick(() => {
if (this.superModel.mainModel.arrayModel.length) {
this.superModel.mainModel.arrayModel.shift()
}
})
Button() {
Text('清空')
.foregroundColor(Color.White)
.padding(20)
}.onClick(() => {
this.superModel.mainModel.arrayModel = new XTypeArray();
})
Button() {
Text('修改第二个元素')
.foregroundColor(Color.White)
.padding(20)
}.onClick(() => {
if (this.superModel.mainModel.arrayModel.length > 2) {
// 这里页面不刷新
this.superModel.mainModel.arrayModel[1].type.typeName = "续爱怒"
}
})
}
}.margin(60)
}
}
@Component
struct Lower2View {
@ObjectLink list: XTypeArray
build() {
Column() {
ForEach(this.list, (item: XTypeWrap) => {
Column() {
LowerView({ model: item })
}.borderWidth(1)
})
}
}
}
@Component
struct LowerView {
@ObjectLink model: XTypeWrap;
build() {
Column() {
Text('是否展开:' + this.model.isSelected)
.onClick(() => {
this.model.isSelected = !this.model.isSelected
})
Text(this.model.type.typeName)
Text(this.model.type.queueModel?.name ?? '空')
}
}
}
@Component
struct MainView {
@ObjectLink mainModel: MainModel
build() {
Column() {
Text('MainView:' + this.mainModel.arrayModel.length)
Text('Text:' + this.mainModel.name)
// ForEach(this.mainModel.arrayModel, (item: XTypeWrap) => {
// // Text(item.isSelected + 'AS')
// // Text(item.type.typeName)
// // Text(item.type.queueModel?.name ?? '空')
// // 先不管
// // reportModels ?: ListModelItem[]
// LowerView({ model: item })
// })
Lower2View({ list: this.mainModel.arrayModel })
}
}
}
@Observed
class SuperModel {
public mainModel: MainModel
constructor(mainModel: MainModel) {
this.mainModel = mainModel
}
}
@Observed
class MainModel {
public name: string
public arrayModel: XTypeArray
constructor(name: string, arrayModel: XTypeArray) {
this.name = name;
this.arrayModel = arrayModel;
}
}
@Observed
class XTypeArray extends Array<XTypeWrap> {
}
@Observed
export class XTypeWrap {
isSelected: boolean = false
type: InspectTypeModel
constructor(isSelected: boolean, type: InspectTypeModel) {
this.isSelected = isSelected
this.type = type
}
}
interface InspectTypeModel {
typeName: string
queueModel ?: NestedModel
reportModels ?: ListModelItem[]
}
interface NestedModel {
name: string
}
interface ListModelItem {
name: string
}
外部修改模型数组某元素的更新
待编写。似乎使用ObservedV2
和Trace
更好一些。