3. ArkTS语言基础:TypeScript快速回顾、ArkTS语法特性、声明式UI与状态管理、@State与@Prop装饰器
好,咱们直接进入正题。ArkTS 是鸿蒙应用开发的核心语言,它基于 TypeScript,但做了不少针对 UI 开发的优化。说白了,你如果会 TypeScript,上手 ArkTS 会非常快。但如果你之前只用过 JavaScript,也别慌,咱们一步步来。
3.1 TypeScript 快速回顾:为什么我们需要它?
我个人习惯把 TypeScript 看作是「带手铐的 JavaScript」。为什么这么说?因为它强制你定义类型。刚开始你可能觉得麻烦,但我在项目中遇到过无数次因为类型错误导致的线上 bug,从那以后我就成了 TypeScript 的忠实拥趸。
咱们快速过几个核心点:
- 类型注解:变量后面加冒号指定类型,比如
let name: string = '鸿蒙' - 接口(Interface):定义对象的结构,比如
interface User { name: string; age: number } - 联合类型:一个变量可以是多种类型,比如
let id: string | number - 泛型:让你的代码更灵活,比如
function identity<T>(arg: T): T { return arg }
any 占位,等逻辑跑通了再回头补类型。但别养成习惯,any 用多了就失去 TypeScript 的意义了。
3.2 ArkTS 语法特性:它和 TypeScript 有什么不同?
ArkTS 保留了 TypeScript 的大部分语法,但做了减法。嗯,这里要注意,它去掉了 JavaScript 的一些动态特性,比如 any 类型和 var 声明。为什么?为了性能。鸿蒙的方舟编译器需要静态类型信息来做优化。
几个关键差异:
- 强制类型安全:所有变量必须有明确的类型,不能随意改变
- 装饰器支持:这是 ArkTS 的重头戏,后面会详细讲
- UI 描述语法:用
@Component和@Entry来标记组件 - 状态管理:通过装饰器实现响应式更新
// 一个简单的 ArkTS 组件示例
@Component
struct HelloComponent {
@State message: string = 'Hello, HarmonyOS!'
build() {
Column() {
Text(this.message)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Button('点击更新')
.onClick(() => {
this.message = '你点击了按钮!'
})
}
.padding(20)
}
}
你看,结构很清晰。@Component 标记这是一个组件,@State 标记这是一个状态变量,build() 方法里写 UI 描述。我个人觉得这种声明式写法比传统的命令式要直观得多。
3.3 声明式 UI 与状态管理:核心思想
声明式 UI 是什么?说白了,你只需要描述「UI 应该长什么样」,而不需要关心「怎么去更新它」。状态变了,UI 自动跟着变。你想想看,这比手动操作 DOM 省心多少?
在 ArkTS 里,状态管理的核心就是「数据驱动视图」。我刚开始接触这个概念时,总觉得有点玄乎,直到自己写了个计数器才恍然大悟。
举个例子,假设你有一个用户头像组件:
@Component
struct AvatarComponent {
@State avatarUrl: string = 'default.png'
build() {
Image(this.avatarUrl)
.width(100)
.height(100)
.borderRadius(50)
}
}
当 avatarUrl 改变时,图片会自动刷新。你不用写任何更新 UI 的代码。这就是声明式的魅力。
3.4 @State 装饰器:组件的内部状态
@State 是 ArkTS 中最常用的装饰器之一。它标记的变量会成为组件的「响应式状态」。一旦这个变量发生变化,组件会重新渲染。
使用 @State 时要注意几点:
- 只能用在组件内部,不能跨组件共享
- 初始化时必须赋值
- 修改时必须通过赋值操作(
=),不能直接修改对象的属性
@State 数组的某个元素,比如 this.list[0] = 'new value',结果 UI 没更新。后来才意识到,必须用新的数组替换旧数组,比如 this.list = [...this.list]。记住,不可变性是关键!
// 正确的做法
@State items: string[] = ['苹果', '香蕉', '橘子']
// 更新时
this.items = this.items.map((item, index) => {
return index === 0 ? '草莓' : item
})
3.5 @Prop 装饰器:父子组件的数据传递
@Prop 用于从父组件接收数据。它和 @State 的区别在于:@Prop 的数据由父组件提供,子组件不能修改它(至少不能直接修改)。
我习惯把 @Prop 看作是「只读的输入参数」。父组件传什么,子组件就用什么。
// 父组件
@Component
struct ParentComponent {
@State count: number = 0
build() {
Column() {
ChildComponent({ value: this.count })
Button('增加')
.onClick(() => {
this.count++
})
}
}
}
// 子组件
@Component
struct ChildComponent {
@Prop value: number
build() {
Text(`当前值:${this.value}`)
.fontSize(18)
}
}
你看,父组件通过 { value: this.count } 把数据传给子组件。子组件用 @Prop value: number 接收。当父组件的 count 变化时,子组件会自动更新。
@State 配合回调函数。父组件传一个修改方法给子组件,子组件调用这个方法。这样数据流还是单向的,容易追踪。
3.6 实战小例子:一个可编辑的列表项
咱们把今天学的知识串起来,写一个简单的可编辑列表:
@Component
struct EditableListItem {
@Prop initialText: string
@State isEditing: boolean = false
@State currentText: string = ''
build() {
Row() {
if (this.isEditing) {
TextInput({ placeholder: '输入内容', text: this.currentText })
.onChange((value: string) => {
this.currentText = value
})
Button('保存')
.onClick(() => {
this.isEditing = false
// 这里可以触发回调通知父组件
})
} else {
Text(this.initialText)
.fontSize(16)
Button('编辑')
.onClick(() => {
this.currentText = this.initialText
this.isEditing = true
})
}
}
.padding(10)
}
}
这个例子展示了 @Prop 和 @State 的配合使用。initialText 从父组件传入,isEditing 和 currentText 是组件内部状态。点击编辑按钮时,切换编辑模式,输入框绑定 currentText,实现双向数据绑定。
好了,这一章的内容就到这里。记住,状态管理是 ArkTS 的核心,@State 和 @Prop 是你最常用的两个工具。多写几个小例子,慢慢就找到感觉了。