鸿蒙修饰符
文章目录
- 一、引言
- 1.1 什么是修饰符
- 1.2 修饰符在鸿蒙开发中的重要性
- 1.3 修饰符的作用机制
- 二、UI装饰类修饰符
- 2.1 @Styles修饰符
- 2.1.1 基本概念和使用场景
- 2.1.2 使用示例
- 2.1.3 最佳实践
- 2.2 @Extend修饰符
- 2.2.1 基本概念
- 2.2.2 使用示例
- 2.2.3 @Extend vs @Styles 对比
- 2.2.4 使用建议
- 三、组件类修饰符
- 3.1 @Component修饰符
- 3.1.1 基本概念
- 3.1.2 @Component vs 普通函数对比
- 3.1.3 基础使用示例
- 3.1.4 高级特性
- 3.1.5 最佳实践
- 3.1.6 性能优化建议
- 3.2 @BuilderParam修饰符
- 3.2.1 基本概念
- 3.2.2 使用场景
- 3.2.3 注意事项与最佳实践
- 四、状态管理类修饰符
- 4.1 @State修饰符
- 4.1.1 基本概念
- 4.1.2 使用规则
- 4.1.3 @State注意事项和最佳实践
- 4.2 @Prop修饰符
- 4.2.1 基本概念
- 4.2.2 使用示例
- 4.2.3 @Prop vs 普通变量对比
- 4.2.4 @Prop的高级用法
- 4.2.5 最佳实践
- 4.3 @Link修饰符
- 4.3.1 基本概念
- 4.3.2 使用示例
- 4.3.3 @Link vs @Prop 对比
- 4.3.4 @Link最佳实践
- 4.4 @Provide/@Consume修饰符
- 4.4.1 基本概念
- 4.4.2 基础用法
- 4.4.3 高级特性
- 4.4.4 使用建议与最佳实践
- 4.5 @Watch修饰符
- 4.5.1 基本概念
- 4.5.2 基础用法
- 4.5.3 高级用法
- 4.5.4 @Watch使用场景
- 4.5.5 最佳实践
- 4.5.6 @Watch注意事项
- 4.6 @Observed/@ObjectLink修饰符
- 4.6.1 基本概念
- 4.6.2 基础用法
- 4.6.3 关键点
- 4.6.4 实际应用示例
- 4.6.5 注意事项
- 对比
- 1. 使用场景对比
- 2. 具体示例对比
- 3. 特点对比
- @State
- @Prop
- @Link
- @Observed/@ObjectLink
- 4. 数据同步方式对比
一、引言
在HarmonyOS(鸿蒙)应用开发中,修饰符(Decorator)是一个极其重要的概念。它们不仅能够简化开发过程,还能提供强大的功能扩展性。本文将深入探讨鸿蒙中的各类修饰符,帮助开发者更好地理解和使用这些工具。本文主要介绍@Styles
,@Extend
,@Builder
,@Component
,@BuilderParam
,@State
,@Prop
,@Link
,@Provide
,@Watch
,@Observed
等等。
1.1 什么是修饰符
修饰符是一种特殊的语法元素,用@符号作为标识,可以用来修饰类、方法、属性等代码元素,为其添加额外的行为或特性。在鸿蒙开发中,修饰符主要用于以下几个方面:
- UI样式定义和复用
- 组件声明和管理
- 状态管理和数据同步
- 组件间通信
1.2 修饰符在鸿蒙开发中的重要性
1.3 修饰符的作用机制
修饰符在编译时会被转换为特定的代码,它们主要通过以下机制发挥作用:
- 装饰器模式: 在不改变原有代码结构的情况下,动态地给对象添加新的功能
- 编译时转换: 将声明式的代码转换为命令式代码
- 运行时注入: 在运行时为目标对象注入额外的行为
二、UI装饰类修饰符
2.1 @Styles修饰符
2.1.1 基本概念和使用场景
@Styles修饰符用于定义可复用的样式集合,类似于Web开发中的CSS类。它可以:
- 提取共同样式,实现复用
- 保持代码整洁
- 统一管理样式
2.1.2 使用示例
// 定义全局样式
@Styles function globalStyles() {.width('100%').height(100).backgroundColor('#f5f5f5').padding(20)
}// 使用样式
@Entry
@Component
struct StylesDemo {build() {Column() {Text('Hello').fontSize(20).globalStyles()Text('World').fontSize(16).globalStyles()}}
}
2.1.3 最佳实践
样式分类管理
// 按功能分类样式
@Styles function cardStyles() {.width('100%').padding(15).borderRadius(8).backgroundColor(Color.White)
}@Styles function buttonStyles() {.width(120).height(40).borderRadius(20).backgroundColor('#007DFF')
}
2.2 @Extend修饰符
2.2.1 基本概念
@Extend用于扩展现有组件的样式和行为,创建具有特定样式的新组件类型。
2.2.2 使用示例
// 扩展Text组件
@Extend(Text) function primaryText() {.fontSize(16).fontColor('#333333').fontWeight(FontWeight.Medium)
}// 扩展Button组件
@Extend(Button) function primaryButton(color:Color) {.width(200).height(50).backgroundColor('#007DFF').fontColor(color)
}@Entry
@Component
struct TestCase {build() {Column({ space: 20 }) {Text('这是普通文本').primaryText()Button('点击按钮').primaryButton(Color.White)}.height('100%').width('100%')}
}
2.2.3 @Extend vs @Styles 对比
特性 | @Extend | @Styles |
---|---|---|
使用对象 | 特定组件 | 任意组件 |
复用性 | 只能用于指定的组件类型 | 可以应用于任何组件 |
类型安全 | 更强的类型检查 | 相对宽松 |
适用场景 | 组件定制化 | 通用样式复用 |
是否能接收参数 | 是 | 否 |
2.2.4 使用建议
-
选择合适的场景
- 需要创建特定样式的组件变体时,使用@Extend
- 需要跨组件复用样式时,使用@Styles
-
命名规范
// 好的命名示例
@Extend(Text) function primaryText() {}
@Extend(Text) function warningText() {}
@Extend(Button) function roundButton() {}// 避免的命名方式
@Extend(Text) function txt() {}
@Extend(Button) function btn() {}
三、组件类修饰符
3.1 @Component修饰符
3.1.1 基本概念
@Component是鸿蒙开发中最核心的修饰符之一,用于声明自定义组件。它将一个struct转换为可重用的UI组件,具有以下特点:
- 必须包含build()方法
- 支持组件生命周期
- 可以包含状态变量
- 支持组件间通信
3.1.2 @Component vs 普通函数对比
特性 | @Component | 普通函数 |
---|---|---|
状态管理 | 支持@State等状态修饰符 | 不支持状态管理 |
生命周期 | 有完整生命周期 | 没有生命周期 |
重渲染机制 | 响应式更新 | 需手动处理更新 |
使用场景 | 复杂UI组件 | 简单功能封装 |
3.1.3 基础使用示例
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct TestCase {// 组件私有状态
@State counter: number = 0// 生命周期函数aboutToAppear() {promptAction.showToast({message:'Component is about to appear'})}aboutToDisappear() {promptAction.showToast({message:'Component is about to disappear'})}build() {Column() {Text(`计数器: ${this.counter}`).fontSize(20).fontWeight(FontWeight.Bold)Button('增加').onClick(() => {this.counter++}).margin(10)}.width('100%').padding(20)}
}
3.1.4 高级特性
1. 组件参数传递
@Component
struct AdvancedComponent {// 从父组件接收的属性@Prop title: string@State private description: string = ''build() {Column({ space: 10 }) {Text(this.title).fontSize(24)TextInput({ placeholder: '请输入描述' }).onChange((value: string) => {this.description = value})}}
}
2. 组件插槽(Slot)
@Component
struct SlotComponent {@BuilderParam content: () => voidbuild() {Column() {this.content()}.width('100%').backgroundColor('#f5f5f5')}
}// 使用组件插槽
@Component
struct ParentComponent {build() {SlotComponent() {Row() {Text('自定义内容')Image('icon.png')}}}
}
3.1.5 最佳实践
- 组件拆分原则
// 好的实践:功能单一,职责明确
@Component
struct UserAvatar {@Prop userName: string@Prop avatarUrl: stringbuild() {Stack() {Image(this.avatarUrl).width(50).height(50).borderRadius(25)Text(this.userName[0]).fontSize(20).fontColor(Color.White)}}
}// 使用组件
@Component
struct UserProfile {build() {Row() {UserAvatar({userName: "张三",avatarUrl: "avatar.png"})Text("其他信息")}}
}
- 生命周期使用建议
@Component
struct LifecycleComponent {@State private data: any = {}aboutToAppear() {// 初始化数据,订阅事件this.initData()}aboutToDisappear() {// 清理资源,取消订阅this.cleanUp()}private initData() {// 数据初始化逻辑}private cleanUp() {// 清理逻辑}build() {// UI渲染}
}
3.1.6 性能优化建议
- 状态管理优化
@Component
struct OptimizedComponent {// 使用私有变量存储不需要响应式更新的数据private staticData: string = 'static'// 只将需要触发更新的数据声明为@State@State dynamicData: number = 0build() {Column() {// 静态内容Text(this.staticData)// 动态内容Text(`${this.dynamicData}`)}}
}
- 条件渲染优化
@Component
struct ConditionalComponent {@State loading: boolean = true@State data: Array<string> = []build() {Column() {if (this.loading) {LoadingSpinner()} else {ForEach(this.data, (item) => {DataItem({ content: item })})}}}
}
3.2 @BuilderParam修饰符
3.2.1 基本概念
@BuilderParam用于声明自定义构建函数参数,使组件能够接收和渲染自定义内容。
3.2.2 使用场景
- 自定义列表项渲染
@Component
struct MainPage {
@State items: string[] = ['项目1', '项目2', '项目3']
@Builder myItemBuilder(item: string):void{}
@BuilderParam itemBuilder: (item: string) => void = this.myItemBuilderbuild() {List() {ForEach(this.items, (item:string,idx:number) => {ListItem() {this.itemBuilder(item)}})}}
}// 使用示例
@Entry
@Component
struct TestCase {build() {Column(){MainPage({itemBuilder: (item:string) => {Row() {Text(item).width('100%').height('100%').backgroundColor(Color.Pink)}}})}}
}
- 可定制化容器组件
@Component
struct Container {@BuilderParam header?: () => void@BuilderParam content: () => void@BuilderParam footer?: () => voidbuild() {Column() {if (this.header) {this.header()}this.content()if (this.footer) {this.footer()}}}
}
3.2.3 注意事项与最佳实践
- 参数命名规范
// 推荐的命名方式
@BuilderParam headerBuilder: () => void
@BuilderParam contentBuilder: () => void
@BuilderParam footerBuilder: () => void// 不推荐的命名方式
@BuilderParam hdr: () => void
@BuilderParam cnt: () => void
- 性能考虑
@Component
struct PerformanceOptimizedList {// 使用私有变量缓存不常变化的构建函数@Builder myStaticBuilder() :void{Text('Static Content')
}@BuilderParam private staticBuilder:()=>void = this.myStaticBuilder@BuilderParam dynamicBuilder: () => voidbuild() {Column() {// 静态内容this.staticBuilder()// 动态内容this.dynamicBuilder()}}
}
四、状态管理类修饰符
4.1 @State修饰符
4.1.1 基本概念
@State是鸿蒙应用中最基础的状态管理修饰符,用于组件内部状态管理。当@State装饰的变量发生变化时,框架会自动重新渲染相关UI。
4.1.2 使用规则
- 基本语法
interface IUser{name: string,age: number
}// 使用示例
@Entry
@Component
struct TestCase {// 基础类型@State count: number = 0// 对象类型@State user: IUser = {name: 'tata',age: 25}// 数组类型@State list: string[] = ['item1', 'item2']build() {Column({ space: 20 }) {// 使用状态变量Text(`Count: ${this.count}`)Button('Add').onClick(() => {this.count++})Text(`User: ${this.user.name}, ${this.user.age}`)Button('Update User').onClick(() => {// 对象更新要使用新对象this.user = {name:this.user.name,age: this.user.age + 1}})}}
}
4.1.3 @State注意事项和最佳实践
- 状态初始化
@Component
struct StateInitDemo {// ✅ 推荐:直接初始化@State counter1: number = 0// ⚠️ 不推荐:延迟初始化可能导致问题@State counter2: numberaboutToAppear() {// 延迟初始化可能导致首次渲染问题this.counter2 = 0}
}
- 状态粒度控制
@Component
struct StateGranularityDemo {// ❌ 错误:粒度过大@State entirePage: {header: object,content: object,footer: object}// ✅ 正确:适当的状态粒度@State headerConfig: object@State contentList: array@State footerVisible: booleanbuild() {Column() {// UI实现}}
}
4.2 @Prop修饰符
4.2.1 基本概念
@Prop用于父子组件间的单向数据传递,子组件通过@Prop接收父组件传递的数据。
4.2.2 使用示例
// 子组件
@Component
struct ChildComponent {@Prop count: number // 接收父组件传递的数值@Prop message: string // 接收父组件传递的字符串build() {Column() {Text(`Count: ${this.count}`)Text(`Message: ${this.message}`)}}
}// 父组件
@Component
struct ParentComponent {@State parentCount: number = 0@State parentMessage: string = 'Hello'build() {Column() {// 传递属性给子组件ChildComponent({count: this.parentCount,message: this.parentMessage})Button('Update Parent').onClick(() => {this.parentCount++this.parentMessage = 'Updated'})}}
}
4.2.3 @Prop vs 普通变量对比
@Component
struct PropComparisonDemo {// @Prop装饰的变量@Prop propValue: number// 普通变量normalValue: numberbuild() {Column() {// @Prop变量会响应父组件的更新Text(`Prop Value: ${this.propValue}`)// 普通变量不会响应更新Text(`Normal Value: ${this.normalValue}`)}}
}
特性 | @Prop变量 | 普通变量 |
---|---|---|
数据流向 | 单向(父到子) | 无数据流 |
响应更新 | 自动响应 | 不响应 |
可修改性 | 本地可修改但不影响父组件 | 可以自由修改 |
使用场景 | 父子组件通信 | 组件内部数据 |
4.2.4 @Prop的高级用法
- 默认值处理
@Component
struct PropDefaultDemo {// 使用装饰器提供默认值@Prop defaultValue: number = 100build() {Column() {Text(`Value: ${this.defaultValue}`)}}
}
- 属性监听与处理
@Component
struct PropWatchDemo {@Prop @Watch('onCountChange') count: number@State localCount: number = 0onCountChange() {// 当@Prop值变化时执行this.localCount = this.count * 2}build() {Column() {Text(`Prop Count: ${this.count}`)Text(`Local Count: ${this.localCount}`)}}
}
4.2.5 最佳实践
- 属性命名规范
@Component
struct PropNamingDemo {// ✅ 推荐:语义化命名@Prop isVisible: boolean@Prop userName: string@Prop itemCount: number// ❌ 避免:模糊的命名@Prop flag: boolean@Prop str: string@Prop num: number
}
- 类型安全
@Component
struct PropTypeDemo {// ✅ 推荐:明确的类型定义@Prop items: Array<{id: number,name: string}>// ❌ 避免:使用any类型@Prop data: any
}
4.3 @Link修饰符
4.3.1 基本概念
@Link修饰符用于实现父子组件间的双向数据绑定。与@Prop不同,@Link装饰的变量在子组件中的修改会同步回父组件。
4.3.2 使用示例
// 子组件
@Component
struct LinkChildComponent {@Link countValue: number // 双向绑定数据build() {Column() {Text(`Count: ${this.countValue}`)Button('在子组件中修改').onClick(() => {this.countValue++ // 修改会影响父组件})}}
}// 父组件
@Entry
@Component
struct TestCase {@State parentCount: number = 0build() {Column() {Text(`Parent Count: ${this.parentCount}`)LinkChildComponent({ countValue: $parentCount })Button('在父组件中修改').onClick(() => {this.parentCount++})}}
}
4.3.3 @Link vs @Prop 对比
特性 | @Link | @Prop |
---|---|---|
数据流向 | 双向 | 单向(父到子) |
子组件修改 | 直接影响父组件 | 不影响父组件 |
使用场景 | 需要子组件修改父组件状态 | 只需要展示父组件数据 |
语法标记 | 变量名 | 变量名 |
4.3.4 @Link最佳实践
- 合理使用场景
@Component
struct FormComponent {// ✅ 适合使用@Link的场景:表单数据@Link formData: {username: string,password: string}build() {Column() {TextInput({ placeholder: '用户名' }).onChange((value) => {this.formData.username = value})TextInput({ placeholder: '密码' }).type(InputType.Password).onChange((value) => {this.formData.password = value})}}
}
- 避免过度使用
@Component
struct OptimizedComponent {// ✅ 只对需要双向绑定的数据使用@Link@Link editableData: string// ✅ 只读数据使用@Prop@Prop readOnlyData: string// ✅ 组件内部状态使用@State@State localState: number = 0build() {Column() {// 实现UI}}
}
4.4 @Provide/@Consume修饰符
4.4.1 基本概念
@Provide和@Consume用于实现跨组件层级的数据共享,避免多层props传递(即"prop drilling"问题)。
4.4.2 基础用法
interface IUser{name:string,role:string
}
// 顶层组件
@Entry
@Component
struct TestCase {@Provide('theme') theme: string = 'light'@Provide('user') userInfo:IUser = {name: 'tata',role: 'admin'}build() {Column() {ConsumerComponent()}}
}// 消费组件(可以是任意层级的子组件)
@Component
struct ConsumerComponent {@Consume('theme') theme: string@Consume('user') userInfo: IUserbuild() {Column() {Text(`Current Theme: ${this.theme}`)Text(`User: ${this.userInfo.name}`)}}
}
4.4.3 高级特性
- 使用别名
@Component
struct AdvancedProvider {@Provide('systemTheme') theme: string = 'light'
}@Component
struct AdvancedConsumer {// 使用别名访问共享数据@Consume('systemTheme') currentTheme: stringbuild() {Column() {Text(`Theme: ${this.currentTheme}`)}}
}
- 多层级数据共享
@Component
struct AppRoot {@Provide('globalState') userInfo:IUser = {name:'tata',role:'admin'}build() {Column() {// 可以被任意深度的子组件访问DeepNestedComponent()}}
}
4.4.4 使用建议与最佳实践
- 合理的数据粒度
// ✅ 推荐:适当的粒度
@Component
struct GoodProvider {@Provide('userSettings') settings: UserSettings@Provide('appTheme') theme: Theme
}// ❌ 避免:过大的共享范围
@Component
struct BadProvider {@Provide('entireAppState') state: AppState
}
- 性能优化
@Component
struct OptimizedProvider {// 将频繁变化的数据和稳定数据分开@Provide('staticConfig') staticConfig: Config // 稳定数据@Provide('dynamicState') dynamicState: State // 变化数据build() {Column() {// 实现UI}}
}
- 错误处理
@Component
struct SafeConsumer {@Consume('data') data?: DataType // 使用可选类型build() {Column() {if (this.data) {// 安全地使用数据Text(this.data.toString())} else {// 提供后备UIText('数据未就绪')}}}
}
4.5 @Watch修饰符
4.5.1 基本概念
@Watch用于监听状态变量的变化,当被监听的状态发生改变时,会触发相应的回调函数。
4.5.2 基础用法
// 顶层组件
@Entry
@Component
struct TestCase {@State @Watch('onCountChange') count: number = 0@State message: string = ''onCountChange() {this.message = `计数变为 ${this.count.toString()}`}build() {Column({ space: 20 }) {Text(`Current count: ${this.count}`)Text(this.message)Button('Increment').onClick(() => {this.count++})}}
}
4.5.3 高级用法
- 监听多个属性
@Component
struct MultiWatchDemo {@State @Watch('onScoreChange') score: number = 0@State @Watch('onScoreChange') bonus: number = 0// 监听多个数值变化onScoreChange() {console.info(`Total: ${this.score + this.bonus}`)}build() {Column() {// UI实现}}
}
- 条件监听
@Component
struct ConditionalWatchDemo {@State @Watch('onValueChange') value: number = 0@State isEnabled: boolean = trueonValueChange(newValue: number, oldValue: number) {if (this.isEnabled) {// 只在启用状态下处理变化this.handleValueChange(newValue, oldValue)}}private handleValueChange(newVal: number, oldVal: number) {// 处理逻辑}build() {Column() {Switch().checked(this.isEnabled).onChange((value: boolean) => {this.isEnabled = value})}}
}
4.5.4 @Watch使用场景
- 表单验证
@Component
struct FormValidationDemo {@State @Watch('validateEmail') email: string = ''@State emailError: string = ''validateEmail(newValue: string) {const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/if (!emailRegex.test(newValue)) {this.emailError = '请输入有效的邮箱地址'} else {this.emailError = ''}}build() {Column({ space: 10 }) {TextInput({ placeholder: '请输入邮箱' }).onChange((value: string) => {this.email = value})if (this.emailError) {Text(this.emailError).fontSize(12).fontColor(Color.Red)}}}
}
- 数据同步
@Component
struct DataSyncDemo {@State @Watch('syncToServer') localData: IUser = {}async syncToServer(newValue: IUser) {try {await this.saveToServer(newValue)console.info('Data synced successfully')} catch (error) {console.error('Sync failed:', error)}}private async saveToServer(data: object) {// 实现服务器同步逻辑}build() {Column() {// UI实现}}
}
4.5.5 最佳实践
- 性能优化
@Component
struct WatchOptimizationDemo {@State @Watch('onDataChange') data: IUser = {name:'tata',role:'admin'}// ✅ 优化:添加防抖处理private debounceTimer: number = -1onDataChange() {if (this.debounceTimer !== -1) {clearTimeout(this.debounceTimer)}this.debounceTimer = setTimeout(() => {this.processDataChange(this.data)this.debounceTimer = -1}, 300)}private processDataChange(data: IUser) {// 处理数据变化}build() {Column() {// UI实现}}
}
- 错误处理
@Component
struct WatchErrorHandlingDemo {@State @Watch('onDataChange') data: IUser = {name:'tata',role:'admin'}onDataChange() {try {// 数据验证if (!this.isValidData(this.data)) {throw new Error('Invalid data format')}// 处理数据this.processData(this.data)} catch (error) {console.error('Error in watch handler:', error)// 恢复到之前的值// 显示错误提示this.showError(error.message)}}private isValidData(data: IUser): boolean {// 实现数据验证逻辑return true}private processData(data: IUser) {// 处理数据}private showError(message: string) {// 显示错误提示}build() {Column() {// UI实现}}
}
4.5.6 @Watch注意事项
- 避免无限循环
@Component
struct WatchCycleDemo {@State @Watch('onValueChange') value1: number = 0@State @Watch('onValue2Change') value2: number = 0// ❌ 错误:可能导致无限循环onValueChange() {this.value2++ // 触发value2的watch}onValue2Change() {this.value1++ // 触发value1的watch}// ✅ 正确:添加循环保护private updateCount: number = 0private readonly MAX_UPDATES: number = 3onSafeValueChange() {if (this.updateCount < this.MAX_UPDATES) {this.updateCount++this.value2++} else {this.updateCount = 0}}
}
- 同步与异步处理
@Component
struct WatchAsyncDemo {@State @Watch('onDataChange') data: object = {}// ✅ 处理异步操作async onDataChange(newValue: object) {try {await this.validateData(newValue)await this.saveData(newValue)} catch (error) {console.error('Async operation failed:', error)}}private async validateData(data: object): Promise<void> {// 异步数据验证}private async saveData(data: object): Promise<void> {// 异步保存数据}build() {Column() {// UI实现}}
}
- @Watch 回调函数实际上不支持有效的参数传递
- 回调函数中第一个参数是被监听属性的名称字符串
4.6 @Observed/@ObjectLink修饰符
4.6.1 基本概念
@Observed和@ObjectLink是用于对象类型数据的响应式管理修饰符:
- @Observed:将一个类标记为可观察对象
- @ObjectLink:用于在组件间建立对象的引用关系,实现对象级别的双向同步
4.6.2 基础用法
// 定义可观察类
@Observed
class Student {name: stringage: numberscores: number[]constructor(name: string, age: number) {this.name = namethis.age = agethis.scores = []}
}// 父组件
@Component
struct ParentComponent {// 创建可观察对象实例@State student: Student = new Student('tata', 18)build() {Column() {// 传递给子组件StudentCard({ studentInfo: this.student })Button('修改信息').onClick(() => {this.student.age++this.student.scores.push(100)})}}
}// 子组件
@Component
struct StudentCard {// 使用ObjectLink引用父组件的对象@ObjectLink studentInfo: Studentbuild() {Column() {Text(`姓名: ${this.studentInfo.name}`)Text(`年龄: ${this.studentInfo.age}`)ForEach(this.studentInfo.scores, (score:number) => {Text(`分数: ${score}`)})}}
}
4.6.3 关键点
- 类级别装饰器
// ✅ 正确:整个类使用 @Observed
@Observed
class Model {property1: stringproperty2: number
}// ❌ 错误:不能装饰属性
class WrongModel {@Observed property: string // 这是错误的用法
}
- 嵌套对象处理
@Observed
class Parent {child: Child // 如果 Child 需要观察,Child 类也需要使用 @Observed 装饰器
}@Observed
class Child {name: string
}
- 数组处理
@Observed
class TodoList {items: Array<TodoItem> // TodoItem 类需要使用 @ObservedaddItem(item: TodoItem) {this.items.push(item)}
}@Observed
class TodoItem {content: stringcompleted: boolean
}
4.6.4 实际应用示例
// 定义观察类
@Observed
class Profile {name: stringage: numberconstructor(name: string, age: number) {this.name = namethis.age = age}
}// 父组件
@Component
struct ProfileManager {@State profile: Profile = new Profile('tata', 25)build() {Column() {// 传递给子组件ProfileEditor({ userProfile: this.profile })ProfileDisplay({ userProfile: this.profile })}}
}// 编辑组件
@Component
struct ProfileEditor {@ObjectLink userProfile: Profilebuild() {Column() {Button('修改年龄').onClick(() => {this.userProfile.age++ // 直接修改将触发更新})}}
}// 显示组件
@Component
struct ProfileDisplay {@ObjectLink userProfile: Profilebuild() {Column() {Text(`姓名: ${this.userProfile.name}`)Text(`年龄: ${this.userProfile.age}`)}}
}
4.6.5 注意事项
- @Observed 只能用于类声明
- 不能用于装饰类的属性
- 嵌套对象如需响应式,需要分别使用 @Observed 装饰器
- 配合 @ObjectLink 使用来实现组件间的数据同步
- 观察对象的属性修改会自动触发相关组件的更新
对比
让我详细对比这些修饰符的区别:
1. 使用场景对比
修饰符 | 使用对象 | 数据流向 | 使用场景 |
---|---|---|---|
@State | 基本类型/对象/数组 | 组件内部 | 管理组件内部状态 |
@Prop | 基本类型/对象/数组 | 父 -> 子 | 父组件向子组件传递数据 |
@Link | 基本类型/对象/数组 | 双向 | 父子组件双向数据同步 |
@Observed/@ObjectLink | 类实例 | 多组件共享 | 复杂对象的响应式管理 |
2. 具体示例对比
// @State:组件内部状态管理
@Component
struct StateExample {@State count: number = 0 // 组件内部状态build() {Column() {Text(`Count: ${this.count}`)Button('Add').onClick(() => this.count++)}}
}// @Prop:单向数据流
@Component
struct PropExample {@Prop title: string // 从父组件接收数据build() {Text(this.title)}
}// @Link:双向数据绑定
@Component
struct LinkExample {@Link message: string // 与父组件双向绑定build() {TextInput({ text: this.message }).onChange((value: string) => {this.message = value // 修改会影响父组件})}
}// @Observed:对象响应式
@Observed
class User {name: stringage: numberconstructor(name: string, age: number) {this.name = namethis.age = age}
}@Component
struct ObservedExample {@State user: User = new User('tata', 25)build() {Column() {UserCard({ userInfo: this.user }) // 传递给子组件}}
}@Component
struct UserCard {@ObjectLink userInfo: User // 引用观察对象build() {Column() {Text(`Name: ${this.userInfo.name}`)Button('Age++').onClick(() => {this.userInfo.age++ // 直接修改对象属性})}}
}
3. 特点对比
@State
- 用于组件内部状态管理
- 值变化会触发组件重新渲染
- 可以管理任何类型的数据
@State count: number = 0
@State user: Object = {}
@State list: string[] = []
@Prop
- 实现单向数据流
- 子组件不能直接修改父组件数据
- 父组件数据变化会更新子组件
// 父组件
@State title: string = 'Hello'
// 子组件使用
@Prop title: string
@Link
- 实现双向数据绑定
- 子组件可以直接修改数据
- 数据变化双向同步
// 父组件
@State message: string = ''
// 子组件使用
@Link message: string
@Observed/@ObjectLink
- 用于复杂对象的响应式管理
- 自动跟踪对象属性变化
- 支持多组件共享和同步
@Observed
class Model {// 整个类的属性都会被观察
}@ObjectLink model: Model // 引用观察对象
4. 数据同步方式对比
// @State:组件内直接修改
@State value: number = 0
this.value = 1 // 直接赋值// @Prop:不能直接修改
@Prop title: string
this.title = 'new' // ❌ 错误// @Link:双向同步
@Link count: number
this.count++ // ✅ 会同步到父组件// @Observed:对象属性修改
@ObjectLink user: User
this.user.name = 'new name' // ✅ 自动触发更新
相关文章:
鸿蒙修饰符
文章目录 一、引言1.1 什么是修饰符1.2 修饰符在鸿蒙开发中的重要性1.3 修饰符的作用机制 二、UI装饰类修饰符2.1 Styles修饰符2.1.1 基本概念和使用场景2.1.2 使用示例2.1.3 最佳实践 2.2 Extend修饰符2.2.1 基本概念2.2.2 使用示例2.2.3 Extend vs Styles 对比2.2.4 使用建议…...
【Linux】Linux2.6内核进程调度队列与调度原理
目录 一、进程管理中的部分概念二、寄存器三、进程切换四、Linux2.6内核进程调度队列与调度原理结尾 一、进程管理中的部分概念 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务&#…...
MacOS使用VSCode编写C++程序如何配置clang编译环境
前言 这段时间在练习写C和Python,用vscode这个开发工具,调试的时候遇到一些麻烦,浪费了很多时间,因此整理了这个文档。将详细的细节描述清楚,避免与我遇到同样问题的人踩坑。 1.开发环境的配置 vscode的开发环境配置…...
【Spark源码分析】规则框架- `analysis`分析阶段使用的规则
analysis分析阶段使用的规则 规则批策略规则说明SubstitutionfixedPointOptimizeUpdateFields该规则优化了 UpdateFields 表达式链,因此看起来更像优化规则。但是,在处理深嵌套模式时,UpdateFields 表达式树可能会非常复杂,导致分…...
Windows和Ubuntu系统下cmake和opencv的安装和使用
以下是在Windows和Ubuntu系统下分别安装CMake并使用C配置OpenCV实现读取图片并显示功能的详细步骤: Windows系统 1. 安装CMake 访问CMake官方网站(https://cmake.org/download/)。根据你的Windows系统版本(32位或64位ÿ…...
详解 Qt QtPDF之QPdfPageNavigator 页面跳转
文章目录 前言头文件: 自 Qt 6.4 起继承自: 属性backAvailable : const boolcurrentLocation : const QPointFcurrentPage : const intcurrentZoom : const qrealforwardAvailable : const bool 公共函数QPdfPageNavigator(QObject *parent)virtual ~QPd…...
设计模式之单例
单例可以说是设计模式中最简单的一种模式。但任何一种设计模式都是普遍经验的总结,都有值得思考的地方。所以单例也并不简单,下面让我们慢慢了解它。 单例顾名思义这个类只有一个实例。要做到这点,需要做到以下几点: (…...
笔记软件:我来、思源笔记、Obsidian、OneNote
最近wolai的会员到期了,促使我更新了一下笔记软件。 首先,wolai作为一个笔记软件,我觉得有很多做得不错的方面(否则我也不会为它付费2年了),各种功能集成得很全(公式识别这个功能我写论文的时候…...
前端入门指南:前端模块有哪些格式?分别什么情况使用
前言 在当今的前端开发中,模块化是提升代码组织性和可维护性的关键手段。随着前端技术的发展,出现了多种模块化方案,每种方案都有其独特的优势和适用场景。本文将详细探讨常见的前端模块格式,包括全局变量、IIFE、CommonJS、AMD、…...
Vue3 常用指令解析:v-bind、v-if、v-for、v-show、v-model
Vue 是一个非常强大的前端框架,提供了许多常用指令来简化模板的使用。Vue 指令以 v- 开头,用于对 DOM 元素和组件的行为进行控制。本文将介绍 Vue 中常见的五个指令:v-bind、v-if、v-for、v-show 和 v-model,并通过实例代码来演示…...
如何查看ubuntu服务器的ssh服务是否可用
你可以通过以下几种方法检查 Ubuntu 服务器上的 SSH 服务是否可用: 1. 使用 systemctl 检查 SSH 服务状态 首先,检查 SSH 服务是否正在运行: sudo systemctl status ssh如果 SSH 服务正在运行,你会看到类似以下的输出ÿ…...
redis面试复习
1.redis是单线程还是多线程 无论什么版本工作线程就是是一个,6.x高版本出现了IO多线程 单线程满足redis的串行原子,只不过IO多线程后,把输入/输出放到更多的线程里区并行,好处: 1.执行的时间更短,更快&a…...
【人工智能基础04】线性模型
文章目录 一. 基本知识1. 线性回归1.1. 基本形式1.2. 线性回归 2. 优化方法:梯度下降法2.1. 梯度下降法的直观意义2.2. 随机梯度下降法 3. 分类问题3.1. 二分类:逻辑回归-sigmoid函数3.2. 多分类问题--softmax函数 4. 岭回归与套索回归4.1. 基础概念什么…...
使用YOLO系列txt目标检测标签的滑窗切割:批量处理图像和标签的实用工具
使用YOLO系列txt目标检测标签的滑窗切割:批量处理图像和标签的实用工具 使用YOLO的TXT目标检测标签的滑窗切割:批量处理图像和标签的实用工具背景1. 代码概述2. 滑窗切割算法原理滑窗切割步骤:示例: 3. **代码实现**1. **加载标签…...
《装甲车内气体检测“神器”:上海松柏 K-5S 电化学传感器模组详解》
《装甲车内气体检测“神器”:上海松柏 K-5S 电化学传感器模组详解》 一、引言二、K-5S 电化学传感器模组概述(一)产品简介(二)产品特点(三)产品适用场景 三、电化学传感器原理及优点(一…...
【笔记】文明、现代化与价值投资
文章目录 价值投资与理性思考资管行业特点及对从业人员的道德底线要求价值投资长期来看,各项资产的走势投资与投机 对文明的认知对文明的计量方式狩猎文明或1.0文明农业畜牧文明或2.0文明农业文明的天花板及三次冲顶农业文明中的思想革命和制度创新 科技文明或3.0文…...
排序学习整理(1)
1.排序的概念及运用 1.1概念 排序:所谓排序,就是使⼀串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作,以便更容易查找、组织或分析数据。 1.2运用 购物筛选排序 院校排名 1.3常见排序算法 2.实…...
提升分布式系统响应速度:分布式系统远程调用性能提升之道
目录 一、远程调用直接案例分析 二、并行调用 (一)核心思想 (二)并行调用的实现方式 1. 基本思路 2. 代码示例 3. 关键点说明 4.线程池配置建议 三、数据异构 (一)场景重提 (二&…...
通过MinIO+h2non/imaginary 搭建自己的阿里云OSS
安装MinIO Docker部署MinIO对象存储服务 图片访问地址:http://192.168.153.138:9000/public/su7_1.jpg 安装h2non/imaginary Docker部署h2non/imaginary 处理图片地址:http://192.168.153.138:7000/resize?urlhttp://192.168.153.138:9000/public/su…...
.NET 9 AOT的突破 - 支持老旧Win7与XP环境
引言 随着技术的不断进步,微软的.NET 框架在每次迭代中都带来了令人惊喜的新特性。在.NET 9 版本中,一个特别引人注目的亮点是 AOT( Ahead-of-Time)支持,它允许开发人员将应用程序在编译阶段就优化为能够在老旧的 Win…...
iOS与Windows间传文件
想用数据线从 windows 手提电脑传文件入 iPhone,有点迂回。 参考 [1],要在 windows 装 Apple Devices。装完、打开、插线之后会检测到手机,界面: 点左侧栏「文件」,不是就直接可以传,而是要通过某个应用传…...
ospf协议(动态路由协议)
ospf基本概念 定义 OSPF 是典型的链路状态路由协议,是目前业内使用非常广泛的 IGP 协议之一。 目前针对 IPv4 协议使用的是 OSPF Version 2 ( RFC2328 );针对 IPv6 协议使用 OSPF Version 3 ( RFC2740 )。…...
直击高频编程考点:聚焦新版综合编程能力考查汇总
目录 一、业务性编程和广度能力考查 (一)基本定义 (二)必要性分析 二、高频考查样题(编程扩展问法) 考题1: 用java 代码实现一个死锁用例,说说怎么解决死锁问题?(高…...
爬虫框架快速入门——Scrapy
适用人群:零基础、对网络爬虫有兴趣但不知道从何开始的小白。 什么是 Scrapy? Scrapy 是一个基于 Python 的网络爬虫框架,它能帮助你快速爬取网站上的数据,并将数据保存到文件或数据库中。 特点: 高效:支…...
Springfox、Swagger 和 Springdoc
Springfox、Swagger 和 Springdoc 是用于在 Spring Boot 项目中生成 API 文档的工具,但它们之间有显著的区别和演进关系: 1. Swagger 简介 Swagger 是一个开源项目,旨在为 RESTful APIs 提供交互式文档。最早由 SmartBear 开发,…...
Css、less和Sass(SCSS)的区别详解
文章目录 Css、less和Sass(SCSS)的区别详解一、引言二、CSS 简介1.1、CSS 示例 三、Less 简介2.1、Less 特性2.2、Less 示例 四、Sass(SCSS)简介3.1、Sass 特性3.2、SCSS 示例 五、总结 Css、less和Sass(SCSSÿ…...
新能源汽车充电基础设施短板问题多,如何实现高效、综合、智能化管理?
随着城市经济的发展,人民生活水平的提升,新能源汽车保有量快速增长,而日益增长的新能源汽车需求与充电基础设施建设不平衡的矛盾日益突出。由于停车泊位充电基础设施总量不足、布局待优化、利用效率低、建设运营存在短板问题等原因࿰…...
DBA面试题-1
面临失业,整理一下面试题,找下家继续搬砖 主要参考:https://www.csdn.net/?spm1001.2101.3001.4476 略有修改 一、mysql有哪些数据类型 1, 整形 tinyint,smallint,medumint,int,bigint;分别占用1字节、2字节、3字节…...
LAN,WAN,VLAN,WLAN,VPN了解笔记
局域网LAN---公司的内部网络就是局域网LAN。 提供有线连接的接口允许局域网内的设备(如台式电脑、网络打印机、网络存储设备等)通过以太网线连接到路由器并与其他局域网设备进行通信实现设备之间的数据传输和资源共享一种私有的网络相对其他网络传输速度…...
1.2 算法和算法评价
1.2.1 算法的基本概念 算法:对特定问题求解步骤的一种描述,它是指令的有限序列,其中的每条指令表示一个或多个操作。 算法的五个重要特性 “好”的算法的五个目标 1.2.2 算法效率的度量 一、时间复杂度 算法的时间复杂度是指一个算法每行…...
各大常见编程语言应用领域
不同编程语言因其特性和设计目标而适用于不同的应用领域。以下是一些常见编程语言及其主要应用领域: 1. Python 数据科学与人工智能:Python 在数据分析、机器学习、深度学习等领域广泛使用,因其丰富的库(如 NumPy、Pandas、Tens…...
【FFT】数据点数是否一定为2的n次方?不补零会如何处理?
一般来说,FFT的数据点个数为以2为基数的整数次方(采用以2为基的FFT算法,可以提升运算性能),但是并没有要求FFT的数据点个数一定为2的n次方。 因此针对数据点数不是以2为基数的整数次方,有两种处理方法&…...
shell脚本小练习#003:查找并拷贝目录
实例1: # 从当前执行脚本的路径位置开始向上搜索一个名为sourceProject目录名 # 并将这个文件目录的路径名称打印出来#!/bin/bashfunction find_dir() {local current_dir$PWDwhile [[ $current_dir ! "/" ]]; doif [[ -d "${current_dir}/sourcePr…...
frp内网穿透
目录 1,准备公网服务器 2,下载安装frp服务端 3,服务端安装 2)编辑服务端配置文件fprs.toml 3)配置启动服务 4)启动服务 5 )设置开机启动服务 6)查看服务启动状态 3,…...
Android电视项目焦点跨层级流转
1. 背景 在智家电视项目中,主要操作方式不是触摸,而是遥控器,通过Focus进行移动,确定点击进行的交互,所以在电视项目中焦点、选中、确定、返回这几个交互比较重要。由于电视屏比较大,在一些复杂页面中会存…...
时频转换 | Matlab基于S变换S-transform一维数据转二维图像方法
目录 基本介绍程序设计参考资料获取方式基本介绍 时频转换 | Matlab基于S变换S-transform一维数据转二维图像方法 程序设计 clear clc % close all load x.mat % 导入数据 x =...
转载 为nautilus安装rabbitvcs
# 添加 rabbitvcs 的 ppa 源 sudo add-apt-repository ppa:rabbitvcs/ppa sudo apt update # 安装 rabbitvcs sudo apt install rabbitvcs-cli rabbitvcs-core rabbitvcs-gedit rabbitvcs-nautilus # 注销后重新登录,右键即可使用 # 解决 RabbitVCS 无法自动保存…...
OpenCV 模板匹配全解析:从单模板到多模板的实战指南
简介:本文深入探讨 OpenCV 中的模板匹配技术。详细介绍构建输入图像与模板图像的步骤,包括读取、截取、滤波与存储等操作。剖析 cv2.matchTemplate 语法及其参数含义,阐述不同匹配方法下结果值的意义。同时讲解 cv2.minMaxLoc 语法࿰…...
手机控制载货汽车一键启动无钥匙进入广泛应用
移动管家载货汽车一键启动无钥匙进入手机控车系统, 该系统广泛应用于物流运输、工程作业等货车场景,为车主提供了高效、便捷的启动和熄火解决方案,体现了科技进步对物流行业的积极影响 核心功能:简化启动流程,提…...
Springboot——SseEmitter流式输出
文章目录 前言SseEmitter 简介测试demo注意点异常一 ResponseBodyEmitter is already set complete 前言 最近做AI类的开发,看到各大AI模型的输出方式都是采取的一种EventStream的方式实现。 不是通常的等接口处理完成后,一次性返回。 而是片段式的处理…...
【人工智能数学基础篇】线性代数基础学习:深入解读矩阵及其运算
矩阵及其运算:人工智能入门数学基础的深入解读 引言 线性代数是人工智能(AI)和机器学习的数学基础,而矩阵作为其核心概念之一,承担着数据表示、变换和运算的重任。矩阵不仅在数据科学中广泛应用,更是神经…...
idea 自动导包,并且禁止自动导 *(java.io.*)
自动导包配置 进入 idea 设置,可以按下图所示寻找位置,也可以直接输入 auto import 快速定位到配置。 Add unambiguous imports on the fly:自动帮我们优化导入的包Optimize imports on the fly:自动去掉一些没有用到的包 禁止导…...
奇怪的编码2
1.当铺密码 当铺密码的标志是“田由中人工大王夫井羊” 口 0 田 0 由 1 中 2 人 3 工 4 大 5 王 6 夫 7 井 8 羊 9 解密脚本: s 田由中人工大王夫井羊 codeinput("请输入当铺密码:") code code.split(" ") w for i in code:k…...
AI服务器从HBM到CXL的技术变革
AI服务器从HBM到CXL变革 本文探讨了AI产业的新范式,特别是服务器变革。传统服务器价格通常在1万美金以内,而搭载8张H100算力卡的DGX H100AI服务器价值高达40万美金(约300万人民币)。这一变化将对AI产业产生深远影响。 自然语言和图形处理依赖大量存储器…...
将自定义 AWS S3 快照存储库连接到 Elastic Cloud
作者:来自 Elastic Annie Hansen, Stef Nestor 在本博客中,我们将介绍如何通过 Elasticsearch 的快照将我们已提交的集群数据备份到 AWS S3 存储桶中。在 Elastic Cloud(企业版)中,Elastic 在其 found-snapshots 存储…...
Java 多线程编程核心要点全解析:深度探秘关键方法与同步机制
1.Thread 类中的start() 和 run() 方法有什么区别? 在Java编程语言中,Thread 类的 start() 和 run() 方法有重要的区别: start() 方法: 当你调用 start() 方法时,它会启动一个新的线程,并且这个新线程会…...
个人博客接入github issue风格的评论,utteranc,gitment
在做个人博客的时候,如果你需要评论功能,但是又不想构建用户体系和评论模块,那么可以直接使用github的issue提供的接口,对应的开源项目有utteranc和gitment,尤其是前者。 它们的原理是一样的:在博客文章下…...
搞个项目之-esp32-cam ov2640模组搭建图像视频项目
开发版的介绍: 1、开发板使用的是:ESP32-CAM 2、摄像头模组:OV2640 3、烧录底座:ESP32-CAM开发板烧录座 4、mirco usb线,四线30cm 5、开发版的原理图像 项目前期的准备工作 一、安装arduino arduino官网地址地址…...
【FPGA开发】Vivado自定义封装IP核,绑定总线
支持单个文件的封装、整个工程的封装,这里用单个文件举例。 在文件工程目录下,自建一个文件夹,里面放上需要封装的verilog文件。 选择第三个,指定路径封装,找到文件所在目录 取个名,选择封装IP的路径 会…...
Leetcode51:N 皇后
题目描述: 按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上,并且使皇后彼此之间不能相互攻击。 给你一个整数 n ,返回所有不同的 n 皇后问…...