ArkTS 中 @State、@Prop 和 @Link 的区别总结
ArkTS 中 @State、@Prop 和 @Link 的区别总结
📌 文档说明
本文档详细对比 ArkTS 中三个核心状态管理装饰器:@State、@Prop 和 @Link 的区别、使用场景和注意事项。适合用于:
- 理解 ArkTS 组件间通信机制
- 面试准备和技术交流
- 选择合适的状态管理方式
🎯 一、快速对比表
| 特性 | @State | @Prop | @Link |
|---|---|---|---|
| 作用域 | 组件内部 | 父 → 子 | 父 ↔ 子 |
| 数据流 | 私有状态 | 单向(只读) | 双向(可读可写) |
| 可修改 | ✅ 可以 | ❌ 不可以 | ✅ 可以 |
| 影响范围 | 当前组件 | 仅子组件 | 父子同步 |
| 传递方式 | - | 直接传值 | 使用 $ 传引用 |
| 使用场景 | 组件私有状态 | 展示型组件 | 表单、交互组件 |
| 初始化 | 必须赋初值 | 从父组件接收 | 从父组件接收 |
| 响应式 | ✅ 响应式 | ✅ 响应式 | ✅ 响应式 |
💡 二、核心概念
记忆口诀
@State → 我的数据我做主(私有状态)
@Prop → 只能看不能改(单向传递)
@Link → 大家一起用(双向绑定)
数据流向图
┌─────────────┐
│ @State │ 组件内部独享
│ (私有状态) │ ↓ 修改
│ │ ↓ 触发 UI 更新
└─────────────┘┌─────────────┐
│ 父组件 │
│ @State data │
│ │
│ ↓ (传值)
│
│ 子组件 │
│ @Prop data │ ← 只读,不能修改
│ │
└─────────────┘┌─────────────┐
│ 父组件 │
│ @State data │
│ ↕ (传引用 $)
│
│ 子组件 │
│ @Link data │ ← 可读可写,父子同步
│ │
└─────────────┘
📖 三、详细讲解
3.1 @State - 组件私有状态
定义
@State 装饰的变量是组件的私有状态,只在当前组件内部使用,修改会触发组件 UI 更新。
特点
- ✅ 组件私有:不能从外部传入
- ✅ 响应式更新:修改自动触发 UI 重新渲染
- ✅ 必须初始化:声明时必须赋初值
- ⚠️ 作用域限制:只影响当前组件
基本用法
@Entry
@Component
struct CounterDemo {@State count: number = 0 // ✅ 必须赋初值@State message: string = 'Hello'@State isVisible: boolean = truebuild() {Column({ space: 10 }) {Text(`计数: ${this.count}`)Text(this.message)Button('增加').onClick(() => {this.count++ // ✅ 可以修改this.message = 'World'})Button('切换显示').onClick(() => {this.isVisible = !this.isVisible})if (this.isVisible) {Text('显示内容')}}}
}
使用场景
适合的场景:
- ✅ 组件内部的计数器、索引
- ✅ 开关状态(visible、loading、checked)
- ✅ 表单输入值(暂存在组件内)
- ✅ 列表数据(组件独有的数据)
- ✅ 临时状态(弹窗、Toast)
代码示例:
@Entry
@Component
struct TodoList {@State todos: string[] = [] // 待办列表@State inputText: string = '' // 输入框内容@State loading: boolean = false // 加载状态build() {Column() {// 输入框TextInput({ text: this.inputText }).onChange((value: string) => {this.inputText = value})// 添加按钮Button('添加').onClick(() => {if (this.inputText) {this.todos.push(this.inputText)this.inputText = ''}})// 列表if (this.loading) {LoadingProgress()} else {List() {ForEach(this.todos, (todo: string) => {ListItem() {Text(todo)}})}}}}
}
3.2 @Prop - 父向子单向传递
定义
@Prop 实现父组件向子组件的单向数据传递,子组件只能读取不能修改,父组件数据变化时子组件自动更新。
特点
- ✅ 单向数据流:父 → 子
- ❌ 只读属性:子组件不能修改
- ✅ 自动更新:父组件变化,子组件自动更新
- ✅ 传值方式:直接传值,不需要
$ - ⚠️ 修改报错:尝试修改会编译错误
基本用法
// ========== 子组件 ==========
@Component
struct UserCard {@Prop username: string // 从父组件接收@Prop age: number@Prop avatar: stringbuild() {Column({ space: 10 }) {Image(this.avatar).width(80).height(80).borderRadius(40)Text(this.username).fontSize(18).fontWeight(FontWeight.Bold)Text(`年龄: ${this.age}`).fontSize(14).fontColor(Color.Gray)Button('尝试修改').onClick(() => {// ❌ 错误:不能修改 @Prop// this.username = 'New Name' // 编译错误!console.log('子组件不能修改 @Prop')})}.padding(20).backgroundColor(Color.White).borderRadius(12)}
}// ========== 父组件 ==========
@Entry
@Component
struct UserProfile {@State userName: string = 'Tom'@State userAge: number = 20@State userAvatar: string = ''build() {Column({ space: 20 }) {Text('用户资料').fontSize(24).fontWeight(FontWeight.Bold)// 传递给子组件(直接传值)UserCard({username: this.userName, // ✅ 直接传值age: this.userAge,avatar: this.userAvatar})Button('修改用户信息').onClick(() => {this.userName = 'Jerry' // ✅ 父组件修改this.userAge = 25// 子组件自动更新})}.width('100%').padding(20)}
}
使用场景
适合的场景:
- ✅ 展示型组件(卡片、列表项、头像)
- ✅ 只读数据(商品信息、用户资料、文章详情)
- ✅ 静态配置(主题色、字体大小、样式参数)
- ✅ 纯 UI 组件(按钮、标签、徽章)
完整示例:商品卡片
// 商品数据接口
interface Product {id: stringname: stringprice: numberimage: stringstock: number
}// ========== 子组件:商品卡片 ==========
@Component
struct ProductCard {@Prop productName: string@Prop price: number@Prop imageUrl: string@Prop stock: numberbuild() {Column({ space: 10 }) {// 商品图片Image(this.imageUrl).width('100%').height(200).borderRadius(8).backgroundColor('#F5F5F5')// 商品名称Text(this.productName).fontSize(16).fontWeight(FontWeight.Bold).maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })// 价格和库存Row() {Text(`¥${this.price}`).fontSize(20).fontColor('#FF4D4F').fontWeight(FontWeight.Bold)Spacer()Text(`库存: ${this.stock}`).fontSize(14).fontColor(Color.Gray)}.width('100%')// 购买按钮Button('立即购买').width('100%').backgroundColor('#FF4D4F')}.width('100%').padding(15).backgroundColor(Color.White).borderRadius(12)}
}// ========== 父组件:商品列表 ==========
@Entry
@Component
struct ProductList {@State products: Product[] = [{ id: '1', name: 'iPhone 15 Pro', price: 7999, stock: 100, image: '' },{ id: '2', name: 'iPad Pro', price: 6799, stock: 50, image: '' },{ id: '3', name: 'MacBook Pro', price: 12999, stock: 30, image: '' }]build() {Scroll() {Column({ space: 15 }) {Text('商品列表').fontSize(24).fontWeight(FontWeight.Bold)// 使用 ForEach 渲染商品列表ForEach(this.products, (product: Product) => {ProductCard({productName: product.name,price: product.price,imageUrl: product.image,stock: product.stock})}, (product: Product) => product.id)}.width('100%').padding(15)}}
}
3.3 @Link - 父子双向同步
定义
@Link 实现父子组件的双向数据绑定,子组件可以修改数据,修改会同步到父组件。
特点
- ✅ 双向绑定:父 ↔ 子,数据同步
- ✅ 子组件可写:子组件可以修改数据
- ✅ 父子同步:任一方修改,另一方自动更新
- ⚠️ 传引用:必须使用
$符号传递引用 - ⚠️ 不能初始化:不能在子组件中赋初值
基本用法
// ========== 子组件 ==========
@Component
struct Counter {@Link count: number // ✅ 双向绑定build() {Column({ space: 10 }) {Text('子组件').fontSize(18).fontWeight(FontWeight.Bold)Text(`计数: ${this.count}`).fontSize(24)Button('子组件增加').onClick(() => {this.count++ // ✅ 可以修改,父组件会同步})Button('子组件减少').onClick(() => {this.count--})}.padding(20).backgroundColor('#E3F2FD').borderRadius(12)}
}// ========== 父组件 ==========
@Entry
@Component
struct ParentPage {@State parentCount: number = 0 // 父组件状态build() {Column({ space: 20 }) {Text('父组件').fontSize(20).fontWeight(FontWeight.Bold)Text(`父组件看到的计数: ${this.parentCount}`).fontSize(16)// 使用 $ 传递引用Counter({ count: $parentCount }) // ⚠️ 注意 $ 符号Button('父组件增加').onClick(() => {this.parentCount += 10 // ✅ 父组件修改,子组件同步})Button('父组件重置').onClick(() => {this.parentCount = 0})}.width('100%').padding(20)}
}
使用场景
适合的场景:
- ✅ 表单输入组件(TextInput、DatePicker)
- ✅ 开关切换(Toggle、Checkbox、Radio)
- ✅ 可编辑列表(增删改查)
- ✅ 双向绑定控件(Slider、Rating)
- ✅ 需要父子协作的组件
完整示例:表单组件
// ========== 子组件:输入框 ==========
@Component
struct FormInput {@Link value: string // 双向绑定private label: string = ''private placeholder: string = ''build() {Column({ space: 5 }) {Text(this.label).fontSize(14).fontWeight(FontWeight.Medium)TextInput({ text: this.value, placeholder: this.placeholder }).onChange((text: string) => {this.value = text // ✅ 子组件修改,父组件同步}).width('100%').height(40)}.alignItems(HorizontalAlign.Start).width('100%')}
}// ========== 子组件:开关 ==========
@Component
struct FormSwitch {@Link checked: boolean // 双向绑定private label: string = ''build() {Row() {Text(this.label).fontSize(14).layoutWeight(1)Toggle({ type: ToggleType.Switch, isOn: this.checked }).onChange((isOn: boolean) => {this.checked = isOn // ✅ 子组件修改,父组件同步})}.width('100%').padding(10)}
}// ========== 父组件:注册表单 ==========
@Entry
@Component
struct RegistrationForm {@State username: string = ''@State email: string = ''@State password: string = ''@State agreeTerms: boolean = false// 提交表单handleSubmit() {if (!this.username || !this.email || !this.password) {AlertDialog.show({message: '请填写完整信息',confirm: { value: '确定' }})return}if (!this.agreeTerms) {AlertDialog.show({message: '请同意服务条款',confirm: { value: '确定' }})return}console.log('提交数据:', {username: this.username,email: this.email,password: this.password})AlertDialog.show({message: '注册成功!',confirm: { value: '确定' }})}build() {Scroll() {Column({ space: 20 }) {Text('用户注册').fontSize(28).fontWeight(FontWeight.Bold)// 使用 @Link 组件FormInput({value: $username, // ⚠️ 使用 $ 传引用label: '用户名',placeholder: '请输入用户名'})FormInput({value: $email,label: '邮箱',placeholder: '请输入邮箱'})FormInput({value: $password,label: '密码',placeholder: '请输入密码'})FormSwitch({checked: $agreeTerms,label: '我已阅读并同意服务条款'})// 显示表单数据(父组件可以看到子组件的修改)Column({ space: 5 }) {Text('表单数据预览:').fontSize(14).fontColor(Color.Gray)Text(`用户名: ${this.username}`)Text(`邮箱: ${this.email}`)Text(`密码: ${'*'.repeat(this.password.length)}`)Text(`同意条款: ${this.agreeTerms ? '是' : '否'}`)}.alignItems(HorizontalAlign.Start).width('100%').padding(10).backgroundColor('#F5F5F5').borderRadius(8)Button('注册').width('100%').height(44).fontSize(16).backgroundColor('#007DFF').onClick(() => this.handleSubmit())}.width('100%').padding(20)}}
}
🔄 四、三者对比示例
让我们通过一个完整的示例来看三者的区别:
// ========== 子组件定义 ==========// 1. 使用 @Prop(只读)
@Component
struct DisplayCard {@Prop title: string@Prop count: numberbuild() {Column({ space: 10 }) {Text(this.title).fontSize(16).fontWeight(FontWeight.Bold)Text(`${this.count}`).fontSize(24).fontColor('#007DFF')Button('子组件尝试修改').onClick(() => {// ❌ 不能修改 @Prop// this.count++ // 编译错误console.log('不能修改 @Prop')})}.width('100%').padding(15).backgroundColor('#F0F0F0').borderRadius(8)}
}// 2. 使用 @Link(可读可写)
@Component
struct EditableCard {@Link count: number // 双向绑定build() {Column({ space: 10 }) {Text('可编辑卡片').fontSize(16).fontWeight(FontWeight.Bold)Text(`${this.count}`).fontSize(24).fontColor('#FF4D4F')Button('子组件增加').onClick(() => {this.count++ // ✅ 可以修改,父组件同步})Button('子组件减少').onClick(() => {this.count--})}.width('100%').padding(15).backgroundColor('#E3F2FD').borderRadius(8)}
}// ========== 父组件 ==========@Entry
@Component
struct ComparisonDemo {@State privateCount: number = 0 // ① 组件私有状态@State propCount: number = 0 // ② 用于 @Prop 的状态@State linkCount: number = 0 // ③ 用于 @Link 的共享状态build() {Scroll() {Column({ space: 30 }) {Text('@State、@Prop、@Link 对比').fontSize(24).fontWeight(FontWeight.Bold)Divider()// ========== @State 示例 ==========Column({ space: 10 }) {Text('1️⃣ @State - 组件私有状态').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#FF6B6B')Text(`私有计数: ${this.privateCount}`).fontSize(16)Button('修改私有状态').onClick(() => {this.privateCount++ // ✅ 只影响当前组件})Text('特点:组件内部独享,修改只影响当前组件').fontSize(12).fontColor(Color.Gray)}.alignItems(HorizontalAlign.Start).width('100%').padding(15).backgroundColor('#FFE5E5').borderRadius(12)Divider()// ========== @Prop 示例 ==========Column({ space: 10 }) {Text('2️⃣ @Prop - 单向传递(父 → 子)').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#4ECDC4')Text(`父组件的值: ${this.propCount}`).fontSize(16)// 子组件(@Prop)DisplayCard({title: '只读卡片',count: this.propCount // ✅ 直接传值})Button('父组件修改').onClick(() => {this.propCount++ // 父组件修改,子组件自动更新})Text('特点:子组件只读,父组件修改会同步').fontSize(12).fontColor(Color.Gray)}.alignItems(HorizontalAlign.Start).width('100%').padding(15).backgroundColor('#E0F7F6').borderRadius(12)Divider()// ========== @Link 示例 ==========Column({ space: 10 }) {Text('3️⃣ @Link - 双向绑定(父 ↔ 子)').fontSize(18).fontWeight(FontWeight.Bold).fontColor('#95E1D3')Text(`父组件看到的值: ${this.linkCount}`).fontSize(16)// 子组件(@Link)EditableCard({count: $linkCount // ⚠️ 使用 $ 传引用})Button('父组件修改').onClick(() => {this.linkCount += 10 // 父子同步})Button('父组件重置').onClick(() => {this.linkCount = 0})Text('特点:子组件可修改,父子双向同步').fontSize(12).fontColor(Color.Gray)}.alignItems(HorizontalAlign.Start).width('100%').padding(15).backgroundColor('#E8F8F5').borderRadius(12)Divider()// ========== 总结 ==========Column({ space: 10 }) {Text('📝 总结').fontSize(18).fontWeight(FontWeight.Bold)Text('• @State: 我的数据我做主(私有状态)').fontSize(14)Text('• @Prop: 只能看不能改(单向传递)').fontSize(14)Text('• @Link: 大家一起用(双向绑定)').fontSize(14)}.alignItems(HorizontalAlign.Start).width('100%').padding(15).backgroundColor('#FFF9E6').borderRadius(12)}.width('100%').padding(20)}}
}
⚠️ 五、常见错误与注意事项
5.1 错误 1:@Prop 尝试修改
@Component
struct Child {@Prop value: stringbuild() {Button('修改').onClick(() => {// ❌ 编译错误!@Prop 是只读的this.value = 'new value'})}
}
错误信息:
Property 'value' is read-only
正确做法:
如果需要修改,应该使用 @Link 或通过回调函数通知父组件:
@Component
struct Child {@Prop value: stringprivate onChange?: (newValue: string) => void // 回调函数build() {Button('修改').onClick(() => {// ✅ 通过回调通知父组件if (this.onChange) {this.onChange('new value')}})}
}@Entry
@Component
struct Parent {@State parentValue: string = 'old'build() {Child({value: this.parentValue,onChange: (newValue: string) => {this.parentValue = newValue // 父组件修改}})}
}
5.2 错误 2:@Link 忘记使用 $
@Entry
@Component
struct Parent {@State count: number = 0build() {Column() {// ❌ 错误:直接传值Child({ count: this.count })// ✅ 正确:使用 $ 传引用Child({ count: $count })}}
}
错误现象:
- 子组件修改不会同步到父组件
- 父组件修改也不会同步到子组件
- 失去了双向绑定的效果
记忆技巧:
// @Prop:直接传值(单向)
Child({ value: this.data });// @Link:使用 $ 传引用(双向)
Child({ value: $data }); // $ = 引用
5.3 错误 3:混淆使用场景
// ❌ 错误:展示型组件不需要 @Link
@Component
struct ProductCard {@Link productName: string // 只是展示,不需要双向绑定@Link price: number
}// ✅ 正确:展示型组件用 @Prop
@Component
struct ProductCard {@Prop productName: string@Prop price: number
}// ❌ 错误:表单组件用 @Prop
@Component
struct InputField {@Prop value: string // 用户输入无法同步到父组件
}// ✅ 正确:表单组件用 @Link
@Component
struct InputField {@Link value: string // 用户输入可以同步到父组件
}
5.4 错误 4:@State 初始化问题
@Component
struct Demo {// ❌ 错误:@State 必须初始化@State count: number// ✅ 正确:必须赋初值@State count: number = 0
}
5.5 错误 5:@Link 不能初始化
@Component
struct Child {// ❌ 错误:@Link 不能赋初值@Link count: number = 0// ✅ 正确:从父组件接收@Link count: number
}
📊 六、选择指南
6.1 决策树
需要状态管理?├─ 是否需要从父组件接收?│ ├─ 否 → 使用 @State│ │ (组件私有状态)│ ││ └─ 是 → 子组件是否需要修改?│ ├─ 否 → 使用 @Prop│ │ (只读,展示型组件)│ ││ └─ 是 → 使用 @Link│ (可写,表单组件)│└─ 否 → 使用普通变量
6.2 使用场景对照表
| 场景 | 装饰器 | 说明 |
|---|---|---|
| 计数器(组件内部) | @State | 组件私有状态 |
| Loading 状态 | @State | 组件控制 |
| 弹窗显示/隐藏 | @State | 组件内部控制 |
| 商品卡片展示 | @Prop | 只读,展示商品信息 |
| 用户头像组件 | @Prop | 只读,展示用户信息 |
| 文章列表项 | @Prop | 只读,展示文章信息 |
| TextInput 输入框 | @Link | 需要同步输入值 |
| Checkbox 复选框 | @Link | 需要同步选中状态 |
| Toggle 开关 | @Link | 需要同步开关状态 |
| 可编辑列表 | @Link | 需要同步编辑结果 |
| Slider 滑块 | @Link | 需要同步滑块值 |
🎤 七、面试回答模板
7.1 基础版本(30 秒)
"@State、@Prop 和 @Link 是 ArkTS 中三种不同的状态管理装饰器:
- @State 是组件的私有状态,只在当前组件内使用,修改会触发 UI 更新
- @Prop 实现父向子单向传递,子组件只能读取不能修改,父组件变化时子组件自动更新
- @Link 实现父子双向绑定,子组件修改会同步到父组件,需要用
$符号传递引用简单来说:@State 是自己用,@Prop 是看别人的,@Link 是和别人共享。"
7.2 进阶版本(1-2 分钟)
"从数据流向来看:
@State 是组件的内部状态,完全由组件自己控制,比如一个计数器的 count 值。修改它只会影响当前组件的 UI。
@Prop 实现单向数据流:父 → 子。它像一个只读属性,子组件接收父组件传来的值,可以使用但不能修改。如果父组件的值变了,子组件会自动更新。比如商品卡片组件接收商品信息,只用来展示。传递时直接传值:
Child({ value: this.data })。@Link 实现双向绑定:父 ↔ 子。子组件可以修改这个值,修改后会同步到父组件。传递时需要用
$符号传引用而不是传值:Child({ value: $data })。比如表单输入组件,子组件的输入要同步给父组件。使用场景上:
- 组件内部的临时状态用 @State
- 展示型组件用 @Prop
- 表单、可编辑组件用 @Link"
7.3 深度版本(追问)
如果问:为什么 @Link 需要 $ 符号?
"$ 符号表示传递的是引用而不是值。
在 JavaScript 中,基本类型(number、string、boolean)是值传递,对象是引用传递。但在 ArkTS 中,为了明确表示’这是一个需要双向绑定的引用’,使用
$符号来标记。当你使用
$data时,传递的是 data 的引用,子组件和父组件指向同一个数据源。子组件修改时,实际上是修改了共享的数据,所以父组件也能看到变化。如果不用
$,传递的就是值的拷贝,子组件修改只是修改自己的拷贝,不会影响父组件。"
如果问:@Prop 为什么是只读的?
"这是为了保持单向数据流的设计原则。
单向数据流的好处是:
- 数据流向清晰:数据总是从父组件流向子组件,便于追踪
- 避免混乱:如果子组件能随意修改,数据来源就不明确了
- 便于调试:出问题时只需要检查父组件,不用担心子组件偷偷修改
如果确实需要子组件修改数据,有两种方式:
- 使用 @Link 实现双向绑定
- 通过回调函数通知父组件修改,保持单向数据流
这种设计在 React、Vue 等主流框架中也是一样的。"
🎯 八、核心要点总结
8.1 记忆口诀
@State → 我的数据我做主(私有状态)
@Prop → 只能看不能改(单向传递)
@Link → 大家一起用(双向绑定)
8.2 传递方式
// @State:不需要传递
@State data: string = 'value'// @Prop:直接传值
Child({ data: this.data })// @Link:使用 $ 传引用
Child({ data: $data }) // 注意 $ 符号
8.3 使用决策
第一步:问自己"数据从哪来?"
- 组件内部产生 →
@State - 父组件传入 → 继续第二步
第二步:问自己"需要修改吗?"
- 不需要修改(只展示) →
@Prop - 需要修改(可编辑) →
@Link
8.4 对比总结
| 装饰器 | 数据流 | 可修改 | 传递方式 | 典型场景 |
|---|---|---|---|---|
| @State | 组件内部 | ✅ 是 | - | 计数器、状态 |
| @Prop | 父 → 子 | ❌ 否 | 直接传值 | 商品卡片、头像 |
| @Link | 父 ↔ 子 | ✅ 是 | $ 传引用 | 表单、开关 |
📚 九、最佳实践
9.1 优先使用 @State + @Prop
原则:优先使用单向数据流(@State + @Prop),只在必要时使用 @Link。
原因:
- 单向数据流更清晰,易于理解和维护
- 避免复杂的双向依赖关系
- 便于调试和测试
// ✅ 推荐:单向数据流
@Entry
@Component
struct Parent {@State data: string = 'value'handleChange(newValue: string) {this.data = newValue}build() {Child({value: this.data,onChange: (val) => this.handleChange(val)})}
}// ⚠️ 谨慎使用:双向绑定
@Entry
@Component
struct Parent {@State data: string = 'value'build() {Child({ value: $data })}
}
9.2 组件封装建议
展示型组件:使用 @Prop
@Component
export struct ProductCard {@Prop name: string@Prop price: number@Prop image: stringbuild() {// 只展示,不修改}
}
容器型组件:使用 @State
@Entry
@Component
struct ProductList {@State products: Product[] = []build() {List() {ForEach(this.products, (product: Product) => {ProductCard({name: product.name,price: product.price,image: product.image})})}}
}
表单组件:使用 @Link
@Component
export struct CustomInput {@Link value: stringprivate label: string = ''build() {Column() {Text(this.label)TextInput({ text: this.value }).onChange((text) => {this.value = text})}}
}
9.3 命名规范
// @State:描述性名称
@State isLoading: boolean = false
@State userList: User[] = []
@State currentIndex: number = 0// @Prop:与数据含义一致
@Prop productName: string
@Prop userName: string
@Prop itemCount: number// @Link:强调共享性
@Link selectedItems: string[]
@Link formData: FormData
@Link sharedCount: number
✅ 检查清单
在使用装饰器前,问自己这些问题:
@State 检查清单
- 这个状态只在当前组件使用吗?
- 不需要从父组件接收吗?
- 已经赋初值了吗?
@Prop 检查清单
- 这个数据从父组件接收吗?
- 子组件只需要展示,不需要修改吗?
- 父组件变化时,子组件需要更新吗?
- 传递时直接传值了吗(没有用 $)?
@Link 检查清单
- 这个数据需要父子同步吗?
- 子组件需要修改数据吗?
- 修改需要同步到父组件吗?
- 传递时使用 $ 符号了吗?
🎬 总结
一句话总结:
@State 管理组件内部状态,@Prop 实现父向子单向传递,@Link 实现父子双向绑定。三者的选择取决于数据的来源和使用方式。
核心原则:
- 能用 @State 就用 @State(组件私有)
- 优先使用 @Prop(单向数据流)
- 必要时才用 @Link(双向绑定)
记住:正确使用这三个装饰器,能让你的代码更清晰、更易维护!
祝你开发顺利! 🚀
