ArkTS 是 TypeScript 的扩展语言,在 TS 基础上增加了声明式 UI 语法。
@Entry
@Component
struct Index {
build() {
Column() {
Text('Hello HarmonyOS')
}
}
}不支持 TS 的部分特性:
不支持 any / unknown 类型
不支持 as 类型断言(用 @Type 代替)
不支持枚举的运行时特性(仅支持数字枚举)
不支持 eval() / new Function()
不支持 with 语句
不支持 Object.entries() / Object.keys() / Object.values()(部分场景受限)
ArkTS 新增特性:
@Component
/ @Entry / @Builder / @Extend / @Styles 装饰器
$
双美元符号语法用于双向绑定
@State
/ @Prop / @Link / @Provide / @Consume / @Watch / @ObservedV2 / @Trace 状态装饰器
@Require
参数必填装饰器
@Local
本地状态装饰器
@Param
组件参数装饰器
@Event
组件事件装饰器
@Monitor
属性变化监听装饰器
@Computed
计算属性装饰器
最基础的状态装饰器,变量变化会触发 UI 重新渲染。
@Entry
@Component
struct CounterPage {
@State count: number = 0
@State message: string = '点击计数'
build() {
Column({ space: 20 }) {
Text(this.message).fontSize(24)
Text(`${this.count}`).fontSize(48).fontWeight(FontWeight.Bold)
Row({ space: 12 }) {
Button('-1').onClick(() => { this.count-- })
Button('+1').onClick(() => { this.count++ })
Button('重置').onClick(() => { this.count = 0 })
}
}
}
}支持的类型:string / number / boolean / Object / Array / Map / Set
嵌套对象注意事项:@State 只能观察到第一层属性变化。嵌套属性变化需要使用 @ObservedV2 + @Trace。
父组件 → 子组件的单向数据传递。子组件可以本地修改,但不会同步回父组件。
@Component
struct ChildComponent {
@Prop title: string = ''
@Prop itemCount: number = 0
build() {
Text(`${this.title}: ${this.itemCount}件`)
.fontSize(16)
.onClick(() => { this.itemCount++ }) // 本地修改不影响父组件
}
}
@Entry
@Component
struct ParentComponent {
@State items: number = 5
build() {
Column() {
ChildComponent({ title: '商品', itemCount: this.items })
Text(`父组件值: ${this.items}`) // 仍然是5
}
}
}子组件修改会同步回父组件。必须使用 $ 语法传递引用。
@Component
struct SliderItem {
@Link value: number
build() {
Row() {
Slider({ value: this.value, min: 0, max: 100 })
.onChange((val) => { this.value = val })
Text(`${Math.round(this.value)}%`)
}
}
}
@Entry
@Component
struct MainPage {
@State progress: number = 30
build() {
Column() {
SliderItem({ value: $progress }) // $ 语法传递引用
Text(`当前进度: ${this.progress}%`)
}
}
}祖先组件 @Provide,后代组件 @Consume,无需逐层传递。
@Entry
@Component
struct GrandParent {
@Provide('theme') themeColor: string = '#007DFF'
@Provide('user') userInfo: UserInfo = { name: '张三', level: 'VIP' }
build() {
Column() {
Button('切换主题').onClick(() => {
this.themeColor = this.themeColor === '#007DFF' ? '#FF4400' : '#007DFF'
})
ParentComponent()
}
}
}
@Component
struct ParentComponent {
build() { ChildComponent() }
}
@Component
struct ChildComponent {
@Consume('theme') themeColor: string
@Consume('user') userInfo: UserInfo
build() {
Text(`主题色: ${this.themeColor}, 用户: ${this.userInfo.name}`)
.fontColor(this.themeColor)
}
}监听状态变量变化,执行回调逻辑。
@Entry
@Component
struct SearchPage {
@State @Watch('onSearchChange') keyword: string = ''
@State resultList: string[] = []
onSearchChange(newValue: string) {
if (newValue.length > 0) {
this.doSearch(newValue)
} else {
this.resultList = []
}
}
build() {
Column() {
TextInput({ placeholder: '搜索', text: this.keyword })
.onChange((val) => { this.keyword = val })
List() {
ForEach(this.resultList, (item) => {
ListItem() { Text(item) }
})
}
}
}
}解决 @State 无法观测嵌套属性变化的问题。
@ObservedV2
class Order {
@Trace id: string = ''
@Trace items: OrderItem[] = []
@Trace status: string = 'pending'
}
@ObservedV2
class OrderItem {
@Trace name: string = ''
@Trace price: number = 0
@Trace quantity: number = 1
}
@Entry
@Component
struct OrderPage {
@State order: Order = new Order()
build() {
Column() {
// 修改嵌套属性会触发UI刷新
Button('修改数量').onClick(() => {
this.order.items[0].quantity += 1 // @Trace 确保观测到变化
})
}
}
}复用 UI 结构,类似函数组件。
@Entry
@Component
struct ProductList {
@State products: Product[] = []
@Builder
ProductCard(product: Product) {
Column({ space: 8 }) {
Image(product.image).width(80).height(80)
Text(product.name).fontSize(14).fontWeight(FontWeight.Bold)
Text(`¥${product.price}`).fontSize(12).fontColor('#FF4400')
}
.padding(12)
.borderRadius(8)
.backgroundColor(Color.White)
}
@Builder
EmptyState() {
Column() {
Image($r('app.media.empty')).width(120)
Text('暂无数据').fontSize(16).fontColor('#999999')
}
}
build() {
if (this.products.length > 0) {
List() {
ForEach(this.products, (item) => {
ListItem() { this.ProductCard(item) }
})
}
} else {
this.EmptyState()
}
}
}复用样式属性组合。
@Styles function commonButtonStyle() {
.width(200)
.height(40)
.borderRadius(20)
.fontSize(16)
.fontColor(Color.White)
}
@Styles function primaryBg() {
.backgroundColor('#007DFF')
}
@Styles function dangerBg() {
.backgroundColor('#FF4400')
}
@Entry
@Component
struct ButtonPage {
build() {
Column({ space: 12 }) {
Button('确认').commonButtonStyle().primaryBg()
Button('删除').commonButtonStyle().dangerBg()
}
}
}对特定组件类型扩展样式。
@Extend(Button)
function fancyButton(text: string, bgColor: ResourceColor) {
.type(ButtonType.Capsule)
.width(200)
.height(40)
.backgroundColor(bgColor)
.fontColor(Color.White)
.fontSize(16)
}
// 使用
Button('提交').fancyButton('提交', '#007DFF')@Entry
@Component
struct FormPage {
@State username: string = ''
@State age: number = 18
@State isAgree: boolean = false
build() {
Column() {
TextInput({ text: $username, placeholder: '姓名' })
Slider({ value: $age, min: 0, max: 100 })
Toggle({ type: ToggleType.Checkbox, isOn: $isAgree })
Text(`姓名: ${this.username}, 年龄: ${this.age}, 同意: ${this.isAgree}`)
}
}
}build() {
Column() {
if (this.isLoading) {
LoadingProgress().color('#007DFF')
} else if (this.dataList.length === 0) {
this.EmptyState()
} else {
this.DataContent()
}
}
}ForEach(
this.dataList,
(item: DataItem) => {
ListItem() { this.ItemBuilder(item) }
},
(item: DataItem) => item.id.toString() // keyGenerator,必须唯一
)// 数据源必须实现 IDataSource 接口
class MyDataSource implements IDataSource {
private dataList: DataItem[] = []
private listeners: DataChangeListener[] = []
totalCount(): number { return this.dataList.length }
getData(index: number): DataItem { return this.dataList[index] }
addData(item: DataItem) {
this.dataList.push(item)
this.listeners.forEach(l => l.onDataAdd(this.dataList.length - 1))
}
registerDataChangeListener(listener: DataChangeListener): void {
this.listeners.push(listener)
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const index = this.listeners.indexOf(listener)
if (index >= 0) this.listeners.splice(index, 1)
}
}
// 使用
LazyForEach(
this.dataSource,
(item: DataItem) => {
ListItem() { this.ItemBuilder(item) }
},
(item: DataItem) => item.id.toString()
)