未验证 提交 ba673c62 编写于 作者: O openharmony_ci 提交者: Gitee

!10887 指南整改优化1

Merge pull request !10887 from luoying_ace/ly1020
......@@ -4,9 +4,9 @@ ArkTS通过装饰器@Component和@Entry装饰struct关键字声明的数据结
## 基本概念
- struct:组件可以基于struct实现,不能有继承关系,对于struct的实例化,可以省略new。
- struct:自定义组件可以基于struct实现,不能有继承关系,对于struct的实例化,可以省略new。
- 装饰器:装饰器给被装饰的对象赋予某一种能力,其不仅可以装饰类或结构体,还可以装饰类的属性。多个装饰器可以叠加到目标元素,定义在同一行上或者多行上,推荐定义在多行上
- 装饰器:装饰器给被装饰的对象赋予某一种能力,其不仅可以装饰类或结构体,还可以装饰类的属性。多个装饰器可以叠加到目标元素上,定义在同一行中或者分开多行,推荐分开多行定义
```ts
@Entry
......@@ -15,7 +15,7 @@ ArkTS通过装饰器@Component和@Entry装饰struct关键字声明的数据结
}
```
- build函数:自定义组件必须定义build函数,并且自定义组件禁止自定义构造函数。build函数满足Builder构造器接口定义,用于定义组件的声明式UI描述。
- build函数:自定义组件必须定义build函数,并且禁止自定义构造函数。build函数满足Builder构造器接口定义,用于定义组件的声明式UI描述。
```ts
interface Builder {
......@@ -23,21 +23,21 @@ ArkTS通过装饰器@Component和@Entry装饰struct关键字声明的数据结
}
```
- @Component:装饰struct,结构体在装饰后具有基于组件的能力,需要实现build方法来更新UI。
- @Component:装饰struct,结构体在装饰后具有基于组件的能力,需要实现build方法来创建UI。
- @Entry: 装饰struct,组件被装饰后作为页面的入口,页面加载时将被渲染显示。
- @Preview:装饰struct, 用@Preview装饰的自定义组件可以在DevEco Studio的预览器上进行实时预览,加载页面时,将创建并呈现@Preview装饰的自定义组件。
- @Preview:装饰struct, 用@Preview装饰的自定义组件可以在DevEco Studio的预览器上进行实时预览,加载页面时,将创建并显示@Preview装饰的自定义组件。
> **说明:** 在单个源文件中,最多可以使用10个@Preview装饰自定义组件,更多说明请参考[查看ArkTS组件预览效果](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-previewing-app-service-0000001218760596#section146052489820)。
- 链式调用:以 "." 链式调用的方式配置UI结构及其属性、事件等。
- 链式调用:以 "." 链式调用的方式配置UI组件的属性方法、事件方法等。
## UI描述规范
### 无参数构造配置
组件的接口定义不包含必选构造参数,组件后面的“()”中不需要配置任何内容。例如,Divider组件不包含构造参数:
如果组件的接口定义中不包含必选构造参数,组件后面的“()”中不需要配置任何内容。例如,Divider组件不包含构造参数:
```ts
Column() {
......@@ -49,27 +49,27 @@ Column() {
### 必选参数构造配置
如果组件的接口定义中包含必选构造参数,则在组件后面的“()”中必须配置参数,参数可以使用常量进行赋值。
如果组件的接口定义中包含必选构造参数,则在组件后面的“()”中必须配置相应参数,参数可以使用常量进行赋值。
例如:
- Image组件的必选参数src:
```ts
Image('http://xyz/a.jpg')
Image('https://xyz/test.jpg')
```
- Text组件的必选参数content:
```ts
Text('123')
Text('test')
```
变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求。例如,传递变量或表达式来构造Image和Text组件的参数:
变量或表达式也可以用于参数赋值,其中表达式返回的结果类型必须满足参数类型要求,变量的定义详见[页面级变量的状态管理](arkts-state-mgmt-page-level.md)[应用级变量的状态管理](arkts-state-mgmt-application-level.md)。例如,设置变量或表达式来构造Image和Text组件的参数:
```ts
Image(this.imagePath)
Image('http://' + this.imageUrl)
Image('https://' + this.imageUrl)
Text(`count: ${this.count}`)
```
......@@ -80,14 +80,14 @@ Text(`count: ${this.count}`)
- 配置Text组件的字体大小属性:
```ts
Text('123')
Text('test')
.fontSize(12)
```
- 使用"."操作进行链式调用并同时配置组件的多个属性,如下所示:
- 使用"."运算符进行链式调用并同时配置组件的多个属性,如下所示:
```ts
Image('a.jpg')
Image('test.jpg')
.alt('error.jpg')
.width(100)
.height(100)
......@@ -98,12 +98,12 @@ Text(`count: ${this.count}`)
```ts
Text('hello')
.fontSize(this.size)
Image('a.jpg')
Image('test.jpg')
.width(this.count % 2 === 0 ? 100 : 200)
.height(this.offset + 100)
```
- 对于系统内置组件,框架还为其属性预定义了一些[枚举类型](../reference/arkui-ts/ts-appendix-enums.md)供开发人员调用,枚举类型必须满足参数类型要求,枚举值可以作为参数传递。可以按以下方式配置Text组件的颜色和字体属性:
- 对于系统内置组件,框架还为其属性预定义了一些[枚举类型](../reference/arkui-ts/ts-appendix-enums.md)供开发人员调用,枚举类型可以作为参数传递,且必须满足参数类型要求。例如,可以按以下方式配置Text组件的颜色和字体属性:
```ts
Text('hello')
......@@ -114,7 +114,7 @@ Text(`count: ${this.count}`)
### 事件配置
通过事件方法可以配置组件支持的事件。
通过事件方法可以配置组件支持的事件,事件方法紧随组件,并用"."运算符连接
- 使用lambda表达式配置组件的事件方法:
......@@ -138,18 +138,18 @@ Text(`count: ${this.count}`)
```ts
myClickHandler(): void {
this.counter += 2
}
...
Button('add counter')
.onClick(this.myClickHandler)
```
### 子组件配置
对于支持子组件配置的组件,例如容器组件,在"{ ... }"里为组件添加子组件的UI描述。Column、Row、Stack、Button、Grid和List组件都是容器组件。
对于支持子组件配置的组件,例如容器组件,在"{ ... }"里为组件添加子组件的UI描述。Column、Row、Stack、Grid、List等组件都是容器组件。
- 以下是简单的Column示例:
......@@ -164,32 +164,40 @@ Text(`count: ${this.count}`)
}
```
- 可以嵌套多个子组件
- 容器组件之间也可以互相嵌套,实现相对复杂的多级嵌套效果
```ts
Column() {
Column() {
Button() {
Text('+ 1')
}.type(ButtonType.Capsule)
.onClick(() => console.log ('+1 clicked!'))
Image('1.jpg')
}
Divider()
Column() {
Button() {
Text('+ 2')
}.type(ButtonType.Capsule)
.onClick(() => console.log ('+2 clicked!'))
Image('2.jpg')
}
Divider()
Column() {
Button() {
Text('+ 3')
}.type(ButtonType.Capsule)
.onClick(() => console.log('+3 clicked!'))
Image('3.jpg')
}
}.alignItems(HorizontalAlign.Center)
Row() {
Image('test1.jpg')
.width(100)
.height(100)
Button('click +1')
.onClick(() => {
console.info('+1 clicked!')
})
}
Divider()
Row() {
Image('test2.jpg')
.width(100)
.height(100)
Button('click +2')
.onClick(() => {
console.info('+2 clicked!')
})
}
Divider()
Row() {
Image('test3.jpg')
.width(100)
.height(100)
Button('click +3')
.onClick(() => {
console.info('+3 clicked!')
})
}
}
```
\ No newline at end of file
# 动态构建UI元素
前面章节介绍的是如何创建一个组件内部UI结构固定的自定义组件,为了满足开发者自定义组件内部UI结构的需求,ArkTS同时提供了动态构建UI元素的能力。
[基本UI描述](arkts-basic-ui-description.md)介绍的是如何创建一个内部UI结构固定的自定义组件,为了满足开发者自定义组件内部UI结构的需求,ArkTS同时提供了动态构建UI元素的能力。
## @Builder
可通过@Builder装饰器进行描述,该装饰器可以修饰一个函数,此函数可以在build函数之外声明,并在build函数中或其他@Builder修饰的函数中使用,在一个自定义组件内快速生成多个布局内容。使用方式如下面示例所示。
可通过@Builder装饰器进行描述,该装饰器可以修饰一个函数,此函数可以在build函数之外声明,并在build函数中或其他@Builder修饰的函数中使用,从而实现在一个自定义组件内快速生成多个布局内容。使用方式如下面示例所示。
```ts
// xxx.ets
@Component
struct CompB {
@State CompValue: string = '';
@State CompValue: string = ''
aboutToAppear() {
console.info('CompB aboutToAppear.');
console.info('CompB aboutToAppear.')
}
aboutToDisappear() {
console.info('CompB aboutToDisappear.');
console.info('CompB aboutToDisappear.')
}
build() {
......@@ -33,22 +30,24 @@ struct CompB {
@Entry
@Component
struct CompA {
size1: number = 100;
@State CompValue1: string = "Hello,CompValue1";
@State CompValue2: string = "Hello,CompValue2";
@State CompValue3: string = "Hello,CompValue3";
size1: number = 100
@State CompValue1: string = "Hello,CompValue1"
@State CompValue2: string = "Hello,CompValue2"
@State CompValue3: string = "Hello,CompValue3"
// @Builder装饰的函数内使用自定义组件
// @Builder装饰的函数CompC内使用自定义组件CompB
@Builder CompC(value: string) {
CompB({ CompValue: value });
CompB({ CompValue: value })
}
@Builder SquareText(label: string) {
Text(label)
.fontSize(18)
.width(1 * this.size1)
.height(1 * this.size1)
}
// @Builder装饰的函数RowOfSquareTexts内使用@Builder装饰的函数SquareText
@Builder RowOfSquareTexts(label1: string, label2: string) {
Row() {
this.SquareText(label1)
......@@ -63,7 +62,6 @@ struct CompA {
Row() {
this.SquareText("A")
this.SquareText("B")
// or as long as tsc is used
}
.width(2 * this.size1)
.height(1 * this.size1)
......@@ -71,9 +69,9 @@ struct CompA {
this.RowOfSquareTexts("C", "D")
Column() {
// 使用三次@Builder装饰的自定义组件
this.CompC(this.CompValue1);
this.CompC(this.CompValue2);
this.CompC(this.CompValue3);
this.CompC(this.CompValue1)
this.CompC(this.CompValue2)
this.CompC(this.CompValue3)
}
.width(2 * this.size1)
.height(2 * this.size1)
......@@ -85,32 +83,33 @@ struct CompA {
```
## @BuilderParam<sup>8+<sup>
@BuilderParam装饰器用于修饰自定义组件内函数类型的属性(例如:`@BuilderParam content: () => any;`),并且在初始化自定义组件时被@BuilderParam修饰的属性必须赋值。
@BuilderParam装饰器用于修饰自定义组件内函数类型的属性(例如:`@BuilderParam noParam: () => void`),并且在初始化自定义组件时被@BuilderParam修饰的属性必须赋值。
### 引入动机
当开发者创建自定义组件,想对该组件添加特定功能时(如:仅对自定义组件添加一个点击跳转操作)。若直接在组件内嵌入事件方法,将会导致所有初始化该组件的地方均增加了该功能。为解决此问题,引入了@BuilderParam装饰器,此装饰器修饰的属性值可为@Builder修饰的方法,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的的功能。
当开发者创建自定义组件,并想对该组件添加特定功能时(例如在自定义组件中添加一个点击跳转操作)。若直接在组件内嵌入事件方法,将会导致所有引入该自定义组件的地方均增加了该功能。为解决此问题,引入了@BuilderParam装饰器,此装饰器修饰的属性值可为@Builder装饰的函数,开发者可在初始化自定义组件时对此属性进行赋值,为自定义组件增加特定的功能。
### 参数初始化组件
通过参数初始化组件时,将@Builder装饰的方法赋值给@BuilderParam修饰的属性,并在自定义组件内调用content属性值。对@BuilderParam修饰的属性进行赋值时不带参数(如:`content: this.specificParam`),则此属性的类型需定义成无返回值的函数(如:`@BuilderParam content: () => void`)。若带参数(如:`callContent: this.specificParam1("111")`),则此属性的类型需定义成any(如:`@BuilderParam callContent: any;`)。
通过参数初始化组件时,将@Builder装饰的函数赋值给@BuilderParam修饰的属性,并在自定义组件内调用该属性值。若@BuilderParam修饰的属性在进行赋值时不带参数(如:`noParam: this.specificNoParam`),则此属性的类型需定义为无返回值的函数(如:`@BuilderParam noParam: () => void`);若带参数(如:`withParam: this.SpecificWithParam('WithParamA')`),则此属性的类型需定义成any(如:`@BuilderParam withParam: any`)。
```ts
// xxx.ets
@Component
struct CustomContainer {
header: string = "";
@BuilderParam noParam: () => void;
@BuilderParam withParam: any;
footer: string = "";
header: string = ''
@BuilderParam noParam: () => void
@BuilderParam withParam: any
footer: string = ''
build() {
Column() {
Text(this.header)
.fontSize(50)
.fontSize(30)
this.noParam()
this.withParam()
Text(this.footer)
.fontSize(50)
.fontSize(30)
}
}
}
......@@ -120,22 +119,32 @@ struct CustomContainer {
struct CustomContainerUser {
@Builder specificNoParam() {
Column() {
Text("noParam").fontSize(50)
Text('noParam').fontSize(30)
}
}
@Builder SpecificWithParam(label: string) {
Column() {
Text(label).fontSize(50)
Text(label).fontSize(30)
}
}
build() {
Column() {
CustomContainer({
header: "Header",
header: 'HeaderA',
noParam: this.specificNoParam,
withParam: this.SpecificWithParam('WithParamA'),
footer: 'FooterA'
})
Divider()
.strokeWidth(3)
.margin(10)
CustomContainer({
header: 'HeaderB',
noParam: this.specificNoParam,
withParam: this.SpecificWithParam("WithParam"),
footer: "Footer",
withParam: this.SpecificWithParam('WithParamB'),
footer: 'FooterB'
})
}
}
......@@ -144,45 +153,50 @@ struct CustomContainerUser {
### 尾随闭包初始化组件
在自定义组件中使用@BuilderParam修饰的属性接收尾随闭包(在初始化自定义组件时,组件名称紧跟一个大括号“{}”形成尾随闭包场景(`CustomComponent(){}`)。开发者可把尾随闭包看做一个容器,向其填充内容,如在闭包内增加组件(`{Column(){Text("content")}`),闭包内语法规范与build函数一致。此场景下自定义组件内有且仅有一个使用@BuilderParam修饰的属性。
在自定义组件中使用@BuilderParam修饰的属性时也可通过尾随闭包进行初始化(在初始化自定义组件时,组件后紧跟一个大括号“{}”形成尾随闭包场景(`CustomContainer(){}`)。开发者可把尾随闭包看做一个容器,向其中填充内容,如在闭包内增加组件(`{Column(){...}`),闭包内语法规范与build函数一致。此场景下自定义组件内有且仅有一个使用@BuilderParam修饰的属性。
示例:在闭包内增加Column组件并添加点击事件,在新增的Column组件内调用@Builder修饰的specificParam方法,点击Column组件后该改变自定义组件中header的属性值为“changeHeader”。并且在初始化自定义组件时会把尾随闭包的内容赋值给使用@BuilderParam修饰的closer属性。
示例:在闭包内添加Column组件并设置点击事件,在Column组件内调用@Builder修饰的specificParam函数,点击Column组件后将自定义组件CustomContainer中header的属性值由“header”改变为“changeHeader”。在初始化自定义组件CustomContainer时,尾随闭包的内容会被赋值给@BuilderParam修饰的closer属性。
```ts
// xxx.ets
@Component
struct CustomContainer {
header: string = "";
@BuilderParam closer: () => void;
header: string = ''
@BuilderParam closer: () => void
build() {
Column() {
Text(this.header)
.fontSize(50)
.fontSize(30)
this.closer()
}
}
}
@Builder function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
.fontSize(50)
.fontSize(30)
Text(label2)
.fontSize(50)
.fontSize(30)
}
}
@Entry
@Component
struct CustomContainerUser {
@State text: string = "header"
@State text: string = 'header'
build() {
Column() {
CustomContainer({
header: this.text,
}){
Column(){
specificParam("111", "22")
}.onClick(()=>{
this.text = "changeHeader"
}) {
Column() {
specificParam('testA', 'testB')
}.backgroundColor(Color.Yellow)
.onClick(() => {
this.text = 'changeHeader'
})
}
}
......@@ -192,34 +206,41 @@ struct CustomContainerUser {
## @Styles
ArkTS为了避免开发者对重复样式的设置,通过@Styles装饰器可以将多条样式设置提炼成一个方法,直接在组件声明的位置使用。@Styles装饰器将新的属性函数添加到基本组件上,如Text、Column、Button等。当前@Styles仅支持通用属性。通过@Styles装饰器可以快速定义并复用组件的自定义样式
ArkTS为了避免开发者对重复样式的设置,通过@Styles装饰器可以将多个样式设置提炼成一个方法,直接在组件声明时调用,通过@Styles装饰器可以快速定义并复用自定义样式。当前@Styles仅支持通用属性
@Styles可以定义在组件内或组件外,在组件外定义时需在方法前添加function关键字,组件内定义时不需要添加function关键字。
@Styles可以定义在组件内或组件外,在组件外定义时需在方法名前面添加function关键字,组件内定义时则不需要添加function关键字。
```ts
// xxx.ets
@Styles function globalFancy() {
.backgroundColor(Color.Red)
@Styles function globalFancy () {
.width(150)
.height(100)
.backgroundColor(Color.Pink)
}
@Entry
@Component
struct FancyUse {
@Styles componentFancy() {
.backgroundColor(Color.Blue)
.width(100)
.height(200)
.backgroundColor(Color.Yellow)
}
build() {
Column({ space: 10 }) {
Text("Fancy")
Text('FancyA')
.globalFancy()
.width(100)
.height(100)
.fontSize(30)
Text("Fancy")
Text('FancyB')
.globalFancy()
.fontSize(20)
Text('FancyC')
.componentFancy()
.width(100)
.height(100)
.fontSize(30)
Text('FancyD')
.componentFancy()
.fontSize(20)
}
}
}
......@@ -227,35 +248,37 @@ struct FancyUse {
@Styles还可以在[StateStyles](../reference/arkui-ts/ts-universal-attributes-polymorphic-style.md)属性内部使用,在组件处于不同的状态时赋予相应的属性。
在StateStyles内可以直接调用组件外定义的Styles,但需要通过this关键字调用组件内定义的Styles
在StateStyles内可以直接调用组件外定义的@Styles方法,但需要通过this关键字调用组件内定义的@Styles方法
```ts
// xxx.ets
@Styles function globalFancy() {
.width(100)
.height(100)
@Styles function globalFancy () {
.width(120)
.height(120)
.backgroundColor(Color.Green)
}
@Entry
@Component
struct FancyUse {
@Styles componentFancy() {
.width(50)
.height(50)
.width(80)
.height(80)
.backgroundColor(Color.Red)
}
build() {
Row({ space: 10 }) {
Button() {
Text("Fancy")
}
.stateStyles({
normal: {
.width(80)
.height(80)
},
disabled: this.componentFancy,
pressed: globalFancy
})
Button('Fancy')
.stateStyles({
normal: {
.width(100)
.height(100)
.backgroundColor(Color.Blue)
},
disabled: this.componentFancy,
pressed: globalFancy
})
}
}
}
......@@ -263,14 +286,15 @@ struct FancyUse {
## @Extend
@Extend装饰器将新的属性函数添加到内置组件上,如Text、Column、Button等。通过@Extend装饰器可以快速的扩展原生组件。@Extend装饰器不能用在自定义组件struct定义框内。
@Extend装饰器将新的属性方法添加到Text、Column、Button等内置组件上,通过@Extend装饰器可以快速地扩展原生组件。@Extend不能定义在自定义组件struct内。
```ts
// xxx.ets
@Extend(Text) function fancy(fontSize: number) {
@Extend(Text) function fancy (fontSize: number) {
.fontColor(Color.Red)
.fontSize(fontSize)
.fontStyle(FontStyle.Italic)
.fontWeight(600)
}
@Entry
......@@ -282,62 +306,66 @@ struct FancyUse {
.fancy(16)
Text("Fancy")
.fancy(24)
Text("Fancy")
.fancy(32)
}
}
}
```
> **说明:**
>
> - @Extend装饰器不能用在自定义组件struct定义框内。
> - @Extend装饰器内仅支持属性函数语句
> - @Extend装饰器不能定义在自定义组件struct内。
> - @Extend装饰器内仅支持属性方法设置
## @CustomDialog
@CustomDialog装饰器用于装饰自定义弹窗,使得弹窗可以动态设置样式。
@CustomDialog装饰器用于装饰自定义弹窗组件,使得弹窗可以动态设置内容及样式。
```ts
// custom-dialog-demo.ets
// xxx.ets
@CustomDialog
struct DialogExample {
controller: CustomDialogController;
action: () => void;
build() {
Row() {
Button ("Close CustomDialog")
.onClick(() => {
this.controller.close();
this.action();
})
}.padding(20)
}
controller: CustomDialogController
action: () => void
build() {
Row() {
Button('Close CustomDialog')
.onClick(() => {
this.controller.close()
this.action()
})
}.padding(20)
}
}
@Entry
@Component
struct CustomDialogUser {
dialogController : CustomDialogController = new CustomDialogController({
builder: DialogExample({action: this.onAccept}),
cancel: this.existApp,
autoCancel: true
});
onAccept() {
console.log("onAccept");
}
existApp() {
console.log("Cancel dialog!");
}
dialogController: CustomDialogController = new CustomDialogController({
builder: DialogExample({ action: this.onAccept }),
cancel: this.existApp,
autoCancel: true
});
onAccept() {
console.info('onAccept');
}
build() {
Column() {
Button("Click to open Dialog")
.onClick(() => {
this.dialogController.open()
})
}
existApp() {
console.info('Cancel dialog!');
}
build() {
Column() {
Button('Click to open Dialog')
.onClick(() => {
this.dialogController.open()
})
}
}
}
```
......
......@@ -4,27 +4,27 @@ ArkTS是OpenHarmony优选的主力应用开发语言。ArkTS基于TypeScript(
- ArkTS继承了TS的所有特性。
- 当前,ArkTS在TS基础上主要扩展了[声明式UI](arkts-basic-ui-description.md)能力,让开发者以更简洁、更自然的方式开发高性能应用。
- 当前,ArkTS在TS的基础上主要扩展了[声明式UI](arkts-basic-ui-description.md)能力,让开发者能够以更简洁、更自然的方式开发高性能应用。
当前扩展的声明式UI包括如下特性。
当前扩展的声明式UI能力包括如下特性。
- [基本UI描述](arkts-basic-ui-description.md):ArkTS定义了各种装饰器、自定义组件、UI描述机制,再配合UI开发框架中的UI内置组件、事件方法、属性方法等共同构成了UI开发的主体。
- [状态管理](arkts-state-mgmt-page-level.md):ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,也可以是全局范围内的传递,还可以是 跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。
- [动态构建UI元素](arkts-dynamic-ui-elememt-building.md):ArkTS提供了动态构建UI元素的能力,不仅可以自定义组件内部的UI结构,还可复用组件样式,扩展原生组件。
- [渲染控制](arkts-rendering-control.md):ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的部分内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
- [基本UI描述](arkts-basic-ui-description.md):ArkTS定义了各种装饰器、自定义组件、UI描述机制,再配合UI开发框架中的内置组件及其相关的事件方法、属性方法等共同构成了UI开发的主体。
- [状态管理](arkts-state-mgmt-page-level.md):ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,也可以是应用全局范围内的传递,还可以是跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。
- [动态构建UI元素](arkts-dynamic-ui-elememt-building.md):ArkTS提供了动态构建UI元素的能力,不仅可以自定义组件内部的UI结构,还可复用组件样式,扩展原生组件。
- [渲染控制](arkts-rendering-control.md):ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
- [使用限制与扩展](arkts-restrictions-and-extensions.md):ArkTS在使用过程中存在限制与约束,同时也扩展了双向绑定等能力。
- 未来,ArkTS会结合应用开发/运行的需求持续演进,逐步提供并行和并发能力增强、类型系统增强、分布式开发范式等更多特性。
- 未来,ArkTS会结合应用开发/运行的需求持续演进,逐步提供并行和并发能力增强、系统类型增强、分布式开发范式等更多特性。
下面我们以一个具体的示例来说明ArkTS的基本组成。如下图所示的代码示例,UI界面会两段文本和一个按钮,当开发者点击按钮时,文本内容会从'Hello World'变为 'Hello ArkUI'。
下面我们以一个具体的示例来说明ArkTS的基本组成。如下图所示的代码示例,UI界面包含两段文本、一条分割线和一个按钮,当开发者点击按钮时,文本内容会从'Hello World'变为 'Hello ArkUI'。
![arkts-get-started](figures/arkts-get-started.png)
这个示例中所包含的ArkTS声明式开发范式的基本组成说明如下:
- 装饰器: 用于装饰类、结构、方法以及变量,赋予其特殊的含义,如上述示例中@Entry、@Component和@State都是装饰器。 具体而言,@Component表示这是个自定义组件;@Entry则表示这是个入口组件;@State表示组件中的状态变量,这个状态变换会引起UI变更
- 装饰器: 用于装饰类、结构、方法以及变量,赋予其特殊的含义,如上述示例中@Entry、@Component和@State都是装饰器。 具体而言,@Component表示这是个自定义组件;@Entry则表示这是个入口组件;@State表示这是组件中的状态变量,这个变量变化会触发UI刷新
- 自定义组件:可复用的UI单元,可组合其他组件,如上述被@Component装饰的struct Hello。
- UI描述:声明式的方法来描述UI的结构,例如build()方法中的代码块。
- 内置组件:ArkTS中默认内置的基本组件和布局组件,开发者可以直接调用,如Column、Text、Divider、Button等。
- 内置组件:ArkTS中默认内置的基础组件、容器组件、媒体组件、绘制组件、画布组件等各种组件,开发者可以直接调用,如示例中的Column、Text、Divider、Button等。
- 属性方法:用于组件属性的配置,如fontSize()、width()、height()、color()等,可通过链式调用的方式设置多项属性。
- 事件方法:用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在Button后面的onClick()
- 事件方法:用于添加组件对事件的响应逻辑,如跟随在Button后面的onClick(),同样可以通过链式调用的方式设置多个事件响应逻辑
# 渲染控制
ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的部分内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。
## 条件渲染
......@@ -9,61 +9,61 @@ ArkTS也提供了渲染控制的能力。条件渲染可根据应用的不同状
> **说明:**
>
> - if条件语句可以使用状态变量。
> - if/else条件语句可以使用状态变量。
>
> - 使用if可以使子组件的渲染依赖条件语句。
> - 使用if/else可以使子组件的渲染依赖条件语句。
>
> - 必须在容器组件内使用。
>
> - 某些容器组件限制子组件的类型或数量。将if放置在这些组件内时,这些限制将应用于if和else语句内创建的组件。例如,Grid组件的子组件仅支持GridItem组件,在Grid组件内使用if时,则if条件语句内仅允许使用GridItem组件。
> - 某些容器组件限制子组件的类型或数量,将if/else用于这些组件内时,这些限制将同样应用于if/else语句内创建的组件。例如,Grid容器组件的子组件仅支持GridItem组件,在Grid内使用if/else时,则if/else语句内也仅允许使用GridItem组件。
```ts
Column() {
if (this.count < 0) {
Text('count is negative')
} else if (this.count % 2 === 0) {
Divider()
Text('even')
} else {
Divider()
Text('odd')
}
if (this.count < 0) {
Text('count is negative').fontSize(14)
} else if (this.count % 2 === 0) {
Text('count is even').fontSize(14)
} else {
Text('count is odd').fontSize(14)
}
}
```
## 循环渲染
通过循环渲染(ForEach组件)来迭代数组,并为每个数组项创建相应的组件,可减少代码复杂度。
通过循环渲染(ForEach)从数组中获取数据,并为每个数据项创建相应的组件,可减少代码复杂度。
```
ForEach(
arr: any[],
itemGenerator: (item: any, index?: number) => void,
keyGenerator?: (item: any, index?: number) => string
)
ForEach(
arr: any[],
itemGenerator: (item: any, index?: number) => void,
keyGenerator?: (item: any, index?: number) => string
)
```
**参数:**
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ------------- | ------------------------------------- | ---- | ------------------------------------------------------------ |
| arr | any[] | 是 | 必须是数组,允许空数组,空数组场景下不会创建子组件。同时允许设置返回值为数组类型的函数,例如arr.slice(1, 3),设置的函数不得改变包括数组本身在内的任何状态变量,如Array.splice、Array.sort或Array.reverse这些改变原数组的函数。 |
| itemGenerator | (item: any, index?: number) => void | 是 | 生成子组件的lambda函数,为给定数组项生成一个或多个子组件,单个组件和子组件列表必须括在大括号“{....}”中。 |
| keyGenerator | (item: any, index?: number) => string | 否 | 匿名参数,用于给定数组项生成唯一且稳定的键值。当子项在数组中的位置更改时,子项的键值不得更改,当数组中的子项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则ForEach中的所有节点都将重建。 |
| arr | any[] | 是 | 必须是数组,允许设置为空数组,空数组场景下将不会创建子组件。同时允许设置返回值为数组类型的函数,例如arr.slice(1, 3),设置的函数不得改变包括数组本身在内的任何状态变量,如Array.splice、Array.sort或Array.reverse这些改变原数组的函数。 |
| itemGenerator | (item: any, index?: number) => void | 是 | 生成子组件的lambda函数,为数组中的每一个数据项创建一个或多个子组件,单个子组件或子组件列表必须包括在大括号“{...}”中。 |
| keyGenerator | (item: any, index?: number) => string | 否 | 匿名函数,用于给数组中的每一个数据项生成唯一且固定的键值。当数据项在数组中的位置更改时,其键值不得更改,当数组中的数据项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则ForEach中的所有节点都将重建。 |
> **说明:**
>
> - ForEach组件必须在容器组件内使用;
> - ForEach必须在容器组件内使用。
>
> - 生成的子组件允许在ForEach的父容器组件中,允许子组件生成器函数中包含if/else条件渲染,同时也允许ForEach包含在if/else条件渲染语句中;
> - 生成的子组件应当是允许包含在ForEach父容器组件中的子组件。
>
> - 子项生成器函数的调用顺序不一定和数组中的数据项相同,在开发过程中不要假设子项生成器和键值生成器函数是否执行以及执行顺序。如下示例可能无法正常工作:
> - 允许子组件生成器函数中包含if/else条件渲染,同时也允许ForEach包含在if/else条件渲染语句中。
>
> ```
> - itemGenerator函数的调用顺序不一定和数组中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:
>
> ```ts
> ForEach(anArray.map((item1, index1) => { return { i: index1 + 1, data: item1 }; }),
> item => Text(`${item.i}. item.data.label`),
> item => item.data.id.toString())
> item => Text(`${item.i}. item.data.label`),
> item => item.data.id.toString())
> ```
## 示例
......@@ -76,20 +76,16 @@ struct MyComponent {
@State arr: number[] = [10, 20, 30]
build() {
Column() {
Button() {
Text('Reverse Array')
}.onClick(() => {
this.arr.reverse()
})
ForEach(this.arr,
(item: number) => {
Text(`item value: ${item}`)
Divider()
},
(item: number) => item.toString()
)
Column({ space: 5 }) {
Button('Reverse Array')
.onClick(() => {
this.arr.reverse()
})
ForEach(this.arr, (item: number) => {
Text(`item value: ${item}`).fontSize(18)
Divider().strokeWidth(2)
}, (item: number) => item.toString())
}
}
}
......@@ -97,28 +93,28 @@ struct MyComponent {
## 数据懒加载
通过数据懒加载(LazyForEach组件)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
通过数据懒加载(LazyForEach)从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。
```ts
LazyForEach(
dataSource: IDataSource,
itemGenerator: (item: any) => void,
keyGenerator?: (item: any) => string
dataSource: IDataSource,
itemGenerator: (item: any) => void,
keyGenerator?: (item: any) => string
): void
interface IDataSource {
totalCount(): number;
getData(index: number): any;
registerDataChangeListener(listener: DataChangeListener): void;
unregisterDataChangeListener(listener: DataChangeListener): void;
totalCount(): number;
getData(index: number): any;
registerDataChangeListener(listener: DataChangeListener): void;
unregisterDataChangeListener(listener: DataChangeListener): void;
}
interface DataChangeListener {
onDataReloaded(): void;
onDataAdd(index: number): void;
onDataMove(from: number, to: number): void;
onDataDelete(index: number): void;
onDataChange(index: number): void;
onDataReloaded(): void;
onDataAdd(index: number): void;
onDataMove(from: number, to: number): void;
onDataDelete(index: number): void;
onDataChange(index: number): void;
}
```
......@@ -127,19 +123,19 @@ interface DataChangeListener {
| 参数名 | 参数类型 | 必填 | 参数描述 |
| ------------- | --------------------- | ---- | ------------------------------------------------------------ |
| dataSource | IDataSource | 是 | 实现IDataSource接口的对象,需要开发者实现相关接口。 |
| itemGenerator | (item: any) => void | 是 | 生成子组件的lambda函数,为给定数组项生成一个或多个子组件,单个组件和子组件列表必须括在大括号“{....}”中。 |
| keyGenerator | (item: any) => string | 否 | 匿名函数,用于键值生成,为给定数组项生成唯一且稳定的键值。当子项在数组中的位置更改时,子项的键值不得更改,当数组中的子项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。 |
| itemGenerator | (item: any) => void | 是 | 生成子组件的lambda函数,为数组中的每一个数据项创建一个或多个子组件,单个子组件或子组件列表必须包括在大括号“{...}”中。 |
| keyGenerator | (item: any) => string | 否 | 匿名函数,用于给数组中的每一个数据项生成唯一且固定的键值。当数据项在数组中的位置更改时,其键值不得更改,当数组中的数据项被新项替换时,被替换项的键值和新项的键值必须不同。键值生成器的功能是可选的,但是,为了使开发框架能够更好地识别数组更改,提高性能,建议提供。如将数组反向时,如果没有提供键值生成器,则LazyForEach中的所有节点都将重建。 |
表2 IDataSource类型说明
### IDataSource类型说明
| 名称 | 描述 |
| ------------------------------------------------------------ | ---------------------- |
| totalCount(): number | 获取数据总数。 |
| getData(index: number): any | 获取索引对应的数据。 |
| registerDataChangeListener(listener:DataChangeListener): void | 注册改变数据的控制器。 |
| unregisterDataChangeListener(listener:DataChangeListener): void | 注销改变数据的控制器。 |
| getData(index: number): any | 获取索引值index对应的数据。 |
| registerDataChangeListener(listener:DataChangeListener): void | 注册数据改变的监听器。 |
| unregisterDataChangeListener(listener:DataChangeListener): void | 注销数据改变的监听器。 |
表3 DataChangeListener类型说明
### DataChangeListener类型说明
| 名称 | 描述 |
| -------------------------------------------------------- | -------------------------------------- |
......@@ -156,115 +152,127 @@ interface DataChangeListener {
## 示例
```ts
// xxx.ets
class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = []
private listeners: DataChangeListener[] = []
public totalCount(): number {
return 0
}
public getData(index: number): any {
return undefined
}
public totalCount(): number {
return 0
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener')
this.listeners.push(listener)
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener')
this.listeners.splice(pos, 1)
}
}
public getData(index: number): any {
return undefined
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded()
})
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener')
this.listeners.push(listener)
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index)
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index)
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index)
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to)
})
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener')
this.listeners.splice(pos, 1)
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded()
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index)
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index)
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index)
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to)
})
}
}
class MyDataSource extends BasicDataSource {
private dataArray: string[] = ['/path/image0', '/path/image1', '/path/image2', '/path/image3']
// 初始化数据列表
private dataArray: string[] = ['/path/image0', '/path/image1', '/path/image2', '/path/image3']
public totalCount(): number {
return this.dataArray.length
}
public getData(index: number): any {
return this.dataArray[index]
}
public totalCount(): number {
return this.dataArray.length
}
public addData(index: number, data: string): void {
this.dataArray.splice(index, 0, data)
this.notifyDataAdd(index)
}
public pushData(data: string): void {
this.dataArray.push(data)
this.notifyDataAdd(this.dataArray.length - 1)
}
public getData(index: number): any {
return this.dataArray[index]
}
public addData(index: number, data: string): void {
this.dataArray.splice(index, 0, data)
this.notifyDataAdd(index)
}
public pushData(data: string): void {
this.dataArray.push(data)
this.notifyDataAdd(this.dataArray.length - 1)
}
}
@Entry
@Component
struct MyComponent {
private data: MyDataSource = new MyDataSource()
build() {
List({space: 3}) {
LazyForEach(this.data, (item: string) => {
ListItem() {
Row() {
Image(item).width("30%").height(50)
Text(item).fontSize(20).margin({left:10})
}.margin({left: 10, right: 10})
}
.onClick(()=>{
this.data.pushData('/path/image' + this.data.totalCount())
})
}, item => item)
private data: MyDataSource = new MyDataSource()
build() {
List({ space: 3 }) {
LazyForEach(this.data, (item: string) => {
ListItem() {
Row() {
Image(item).width(50).height(50)
Text(item).fontSize(20).margin({ left: 10 })
}.margin({ left: 10, right: 10 })
}
.onClick(() => {
// 每点击一次列表项,数据增加一项
this.data.pushData('/path/image' + this.data.totalCount())
})
}, item => item)
}
}
}
```
> **说明:**
>
> - 数据懒加载必须在容器组件内使用,且仅有List、Grid以及Swiper组件支持数据的懒加载(即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次加载所有的数据;
> - LazyForEach必须在容器组件内使用,目前仅有List、Grid以及Swiper组件支持数据懒加载(即只加载可视部分以及其前后少量数据用于缓冲),其他组件仍然是一次性加载所有的数据。
>
> - LazyForEach在每次迭代中,必须且只允许创建一个子组件;
> - LazyForEach在每次迭代中,必须创建且只允许创建一个子组件。
>
> - 生成的子组件必须允许在LazyForEach的父容器组件中;
> - 生成的子组件必须是允许包含在LazyForEach父容器组件中的子组件。
>
> - 允许LazyForEach包含在if/else条件渲染语句中,不允许LazyForEach中出现if/else条件渲染语句;
> - 允许LazyForEach包含在if/else条件渲染语句中,但不允许LazyForEach中出现if/else条件渲染语句。
>
> - 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,仅itemGenerator中的UI描述的组件内使用了状态变量时,才会触发组件刷新;
> - 为了高性能渲染,通过DataChangeListener对象的onDataChange方法来更新UI时,仅当itemGenerator中创建的子组件内使用了状态变量时,才会触发组件刷新。
>
> - 子项生成器函数的调用顺序不一定和数据源中的数据项相同,在开发过程中不要假设子项生成器和键值生成器函数是否执行以及执行顺序。如下示例可能无法正常工作:
> - itemGenerator函数的调用顺序不一定和数据源中的数据项相同,在开发过程中不要假设itemGenerator和keyGenerator函数是否执行及其执行顺序。例如,以下示例可能无法正常工作:
>
> ```ts
> LazyForEach(dataSource,
> item => Text(`${item.i}. item.data.label`)),
> item => item.data.id.toString())
> item => Text(`${item.i}. item.data.label`)),
> item => item.data.id.toString())
> ```
\ No newline at end of file
......@@ -4,48 +4,47 @@
ArkTS语言的使用在生成器函数中存在一定的限制:
- 表达式仅允许在字符串(${expression})、if条件、ForEach的参数和组件的参数中使用;
- 任何表达式都不能导致任何应用程序状态变量(@State、@Link、@Prop)的改变,否则会导致未定义和潜在不稳定的框架行为;
- 表达式仅允许在字符串(${expression})、if/else条件语句、ForEach的参数以及组件的参数中使用。
- 任何表达式都不能导致应用程序中状态变量(@State、@Link、@Prop)的改变,否则会造成未定义和潜在不稳定的框架行为。
- 生成器函数内部不能有局部变量。
上述限制都不适用于事件处理函数(例如onClick)的匿函数实现。
上述限制都不适用于事件方法(如onClick)的匿名函数实现。
## 变量的双向绑定
ArkTS支持通过$$双向绑定变量,通常应用于状态值频繁改变的变量。
- 当前$$支持基础类型变量,以及@State、@Link和@Prop装饰的变量。
- 当前$$仅支持[bindPopup](../reference/arkui-ts/ts-universal-attributes-popup.md)属性的show参数和@State变量之间的渲染,Radio组件的checked属性
- $$绑定的变量变更时,仅渲染当前组件,提高渲染速度。
- 当前$$仅支持[bindPopup](../reference/arkui-ts/ts-universal-attributes-popup.md)属性方法的show参数,[Radio](../reference/arkui-ts/ts-basic-components-radio.md)组件的checked属性,[Refresh](../reference/arkui-ts/ts-container-refresh.md)组件的refreshing参数
- $$绑定的变量变化时,仅渲染刷新当前组件,提高渲染速度。
```ts
// xxx.ets
@Entry
@Component
struct bindPopup {
@State customPopup: boolean = false
build() {
column() {
button() {
Text('Popup')
}
.onClick(() => {
this.customPopup = !this.customPopup
})
.bindPopup(
$$this.customPopup, {
mesage: "showPopup"
}
)
}
}
}
struct bindPopupPage {
@State customPopup: boolean = false
build() {
Column() {
Button('Popup')
.margin(20)
.onClick(() => {
this.customPopup = !this.customPopup
})
.bindPopup($$this.customPopup, {
message: "showPopup"
})
}
}
}
```
## 状态变量多种数据类型声明使用限制
## 状态变量数据类型声明使用限制
@State、@Provide、 @Link和@Consume四种状态变量的多种数据类型只能同时由简单数据类型或引用数据类型其中一种构成。
@State、@Provide、 @Link和@Consume四种状态变量的数据类型声明只能由简单数据类型或引用数据类型的其中一种构成。
示例:
......@@ -53,14 +52,14 @@ struct bindPopup {
// xxx.ets
@Entry
@Component
struct Index {
struct IndexPage {
//错误写法: @State message: string | Resource = 'Hello World'
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(`${ this.message }`)
Text(`${this.message}`)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
......
# 应用级变量的状态管理
在前面的章节中,已经讲述了如何管理页面级变量的状态,本章将说明如何管理应用级变量的状态,具体接口请参考[应用级变量的状态管理接口](../reference/arkui-ts/ts-state-management.md)
在前面的章节中,已经讲述了如何管理页面级变量的状态,本章将说明如何管理应用级变量的状态,具体接口说明请参考[应用级变量的状态管理接口](../reference/arkui-ts/ts-state-management.md)
## AppStorage
## [AppStorage](../reference/arkui-ts/ts-state-management.md#appstorage)
AppStorage是应用程序中的单例对象,由UI框架在应用程序启动时创建,在应用程序退出时销毁,为应用程序范围内的可变状态属性提供中央存储。
AppStorage包含整个应用程序中需要访问的所有状态属性,只要应用程序保持运行,AppStorage就会保存所有属性及属性值,属性值可以通过唯一的键值进行访问。
组件可以通过装饰器将应用程序状态数据与AppStorage进行同步,应用业务逻辑的实现也可以通过接口访问AppStorage。
AppStorage的选择状态属性可以与不同的数据源或数据接收器同步,这些数据源和接收器可以是设备上的本地或远程,并具有不同的功能,如数据持久性。这样的数据源和接收器可以独立于UI在业务逻辑中实现。
默认情况下,AppStorage中的属性是可变的,AppStorage还可使用不可变(只读)属性。
......@@ -22,54 +20,59 @@ AppStorage的选择状态属性可以与不同的数据源或数据接收器同
### @StorageProp装饰器
组件通过使用@StorageProp(key)装饰的状态变量,将与AppStorage建立单向数据绑定,key标识AppStorage中的属性键值。当创建包含@StoageProp的状态变量的组件时,该状态变量的值将使用AppStorage中的值进行初始化。AppStorage中的属性值的更改会导致绑定的UI组件进行状态更新。
组件通过使用@StorageProp(key)装饰的状态变量,与AppStorage建立单向数据绑定,key标识AppStorage中的属性键值。当创建包含@StorageProp的状态变量的组件时,该状态变量的值将使用AppStorage中的值进行初始化。AppStorage中属性值的更改会导致绑定该状态变量的UI组件进行状态更新。
### 示例
每次用户单击Count按钮时,this.varA变量值都会增加,此变量与AppStorage中的varA同步。每次用户单击当前语言按钮时,修改AppStorage中的languageCode,此修改会同步给this.lang变量。
每次用户单击Count按钮时,this.varA变量值都会增加1,此变量与AppStorage中的varA同步。每次用户单击language按钮时,修改AppStorage中的languageCode,此修改会同步给this.languageCode变量。
```ts
// xxx.ets
@Entry
@Component
struct ComponentA {
@StorageLink('varA') varA: number = 2
@StorageProp('languageCode') lang: string = 'en'
@StorageProp('languageCode') languageCode: string = 'en'
private label: string = 'count'
aboutToAppear() {
this.label = (this.lang === 'zh') ? '' : 'Count'
this.label = (this.languageCode === 'zh') ? '数量' : 'Count'
}
build() {
Column(){
Column() {
Row({ space: 20 }) {
Button(`${this.label}: ${this.varA}`)
.onClick(() => {
AppStorage.Set<number>('varA', AppStorage.Get<number>('varA') + 1)
})
Button(`lang: ${this.lang}`)
Button(`language: ${this.languageCode}`)
.onClick(() => {
if (this.lang === 'zh') {
if (AppStorage.Get<string>('languageCode') === 'zh') {
AppStorage.Set<string>('languageCode', 'en')
} else {
AppStorage.Set<string>('languageCode', 'zh')
}
this.label = (this.lang === 'zh') ? '' : 'Count'
this.label = (this.languageCode === 'zh') ? '数量' : 'Count'
})
}
.margin({ bottom: 50 })
Row(){
Button(`更改@StorageLink修饰的变量:${this.varA}`).fontSize(10)
Row() {
Button(`更改@StorageLink修饰的变量:${this.varA}`).height(40).fontSize(14)
.onClick(() => {
this.varA++
})
}.margin({ bottom: 50 })
Row(){
Button(`更改@StorageProp修饰的变量:${this.lang}`).fontSize(10)
Row() {
Button(`更改@StorageProp修饰的变量:${this.languageCode}`).height(40).fontSize(14)
.onClick(() => {
this.lang = 'test'
if (this.languageCode === 'zh') {
this.languageCode = 'en'
} else {
this.languageCode = 'zh'
}
})
}
}
......@@ -77,17 +80,18 @@ struct ComponentA {
}
```
![appstorage](figures/appstorage.gif)
## LocalStorage
## [LocalStorage](../reference/arkui-ts/ts-state-management.md#localstorage9)
> **说明:** 该组件从API version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
> **说明:**
>
> 该组件从API version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
LocalStorage是应用程序中的存储单元,生命周期跟随其关联的Ability。LocalStorage为应用程序范围内的可变状态属性和非可变状态属性提供存储,可变状态属性和非可变状态属性是构建应用程序UI的一部分,如一个Ability的UI。
应用层:一个应用程序可以创建多个LocalStorage实例,应用程序的每一个Ability对应一个LocalStorage实例。
Ability: 一个应用程序可以拥有多个Ability,一个Ability中的所有子组件最多可以分配一个LocalStorage实例。并且,Ability中的所有子组件都将继承对此LocalStorage实例存储对象的访问权。
Ability:一个应用程序可以拥有多个Ability,一个Ability中的所有子组件最多可以分配一个LocalStorage实例。并且,Ability中的所有子组件都将继承对此LocalStorage实例存储对象的访问权。
一个组件最多可以访问一个LocalStorage实例,一个LocalStorage对象可以分配给多个组件。
......@@ -101,150 +105,168 @@ Ability: 一个应用程序可以拥有多个Ability,一个Ability中的所
> **说明:** 创建LocalStorage实例时如未定义初始值,可以使用组件内@LocalStorageLink和@LocalStorageProp的初始值。如果定义时给定了初始值,那么不会再使用@LocalStorageLink和@LocalStorageProp的初始值。
### 示例1(在一个Ability创建的LocalStorage)
### 示例1(在一个Ability中创建LocalStorage)
LocalStorage通过loadContent接口加载,接口详见[loadContent](../reference/apis/js-apis-window.md#loadcontent9-1)
LocalStorage通过loadContent接口加载,接口说明详见[loadContent](../reference/apis/js-apis-window.md#loadcontent9-1)
```
```ts
// MainAbility.ts
import Ability from '@ohos.application.Ability'
export default class MainAbility extends Ability {
storage : LocalStorage
onCreate() {
this.storage = new LocalStorage();
this.storage.setOrCreate("storageSimpleProp",121);
console.log("[Demo MainAbility onCreate]");
}
onDestroy() {
console.log("[Demo MainAbility onDestroy]")
}
onWindowStageCreate(windowStage) {
// storage作为参数传递给loadContent接口。
windowStage.loadContent("pages/index",this.storage)
}
onWindowStageDestroy() {
console.log("[Demo] MainAbility onWindoeStageDestroy")
}
onForeground() {
console.log("[Demo] MainAbility onForeground")
}
onBackground() {
console.log("[Demo] MainAbility onBackground")
}
export default class MainAbility extends Ability {
storage: LocalStorage
onCreate() {
this.storage = new LocalStorage()
this.storage.setOrCreate('storageSimpleProp', 121)
console.info('[Demo MainAbility onCreate]')
}
onDestroy() {
console.info('[Demo MainAbility onDestroy]')
}
onWindowStageCreate(windowStage) {
// storage作为参数传递给loadContent接口
windowStage.loadContent('pages/index', this.storage)
}
onWindowStageDestroy() {
console.info('[Demo] MainAbility onWindowStageDestroy')
}
onForeground() {
console.info('[Demo] MainAbility onForeground')
}
onBackground() {
console.info('[Demo] MainAbility onBackground')
}
}
```
@Component组件获取数据
```
```ts
// index.ets
let storage = LocalStorage.GetShared()
@Entry(storage)
@Component
struct LocalStorageComponent {
@LocalStorageLink("storageSimpleProp") simpleVarName: number = 0
@LocalStorageLink('storageSimpleProp') simpleVarName: number = 0
build() {
Column(){
Button(`LocalStorageLink: ${ this.simpleVarName.toString() }`)
.onClick(()=>{
this.simpleVarName +=1;
Column() {
Button(`LocalStorageLink: ${this.simpleVarName.toString()}`)
.margin(20)
.onClick(() => {
this.simpleVarName += 1
})
Text(JSON.stringify(this.simpleVarName))
.fontSize(50)
LocalStorageComponentProp();
}
.height(500)
LocalStorageComponentProp()
}.width('100%')
}
}
@Component
struct LocalStorageComponentProp {
@LocalStorageProp("storageSimpleProp") simpleVarName: number = 0
@LocalStorageProp('storageSimpleProp') simpleVarName: number = 0
build() {
Column() {
Button(`LocalStorageProp: ${ this.simpleVarName.toString() }`)
Button(`LocalStorageProp: ${this.simpleVarName.toString()}`)
.margin(20)
.onClick(() => {
this.simpleVarName += 1;
this.simpleVarName += 1
})
Text(JSON.stringify(this.simpleVarName))
.fontSize(50)
}
.height(500)
}.width('100%')
}
}
```
### 示例2(在Entry页面定义LocalStorage)
```
let storage = new LocalStorage({"PropA":47});
```ts
// xxx.ets
let storage = new LocalStorage({ "PropA": 47 })
@Entry(storage)
@Component
struct ComA {
@LocalStorageLink("PropA") storLink: number = 1;
build() {
Column() {
Text(`Parent from LocalStorage ${ this.storLink }`)
.onClick(()=>this.storLink+=1)
Child()
}
@Component
struct ComA {
@LocalStorageLink("PropA") storageLink: number = 1
build() {
Column() {
Text(`Parent from LocalStorage ${this.storageLink}`)
.fontSize(18)
.margin(20)
.onClick(() => this.storageLink += 1)
Child()
}
}
}
@Component
struct Child{
@LocalStorageLink("PropA") storLink: number = 1;
build() {
Text(`Parent from LocalStorage ${ this.storLink }`)
.onClick(()=>this.storLink+=1)
struct Child {
@LocalStorageLink("PropA") storageLink: number = 1
build() {
Text(`Child from LocalStorage ${this.storageLink}`)
.fontSize(18)
.margin(20)
.onClick(() => this.storageLink += 1)
}
}
```
## PersistentStorage
## [PersistentStorage](../reference/arkui-ts/ts-state-management.md#persistentstorage)
PersistentStorage提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
PersistentStorage提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
> **说明:**
>
> - PersistProp接口使用时,需要保证输入对应的key应当在AppStorage存在。
> - DeleteProp接口使用时,只能对本次启动已经link过的数据生效。
> - PersistentStorage的PersistProp接口使用时,需要保证输入对应的key在AppStorage中存在。
> - PersistentStorage的DeleteProp接口使用时,只能对本次应用启动时已经link过的数据生效。
```ts
// xxx.ets
PersistentStorage.PersistProp("highScore", "0");
PersistentStorage.PersistProp('highScore', '0')
@Entry
@Component
struct PersistentComponent {
@StorageLink('highScore') highScore: string = '0'
@State currentScore: number = 0
build() {
Column() {
if (this.currentScore === Number(this.highScore)) {
Text(`new highScore : ${this.highScore}`)
}
Button() {
Text(`goal!, currentScore : ${this.currentScore}`)
.fontSize(10)
}.onClick(() => {
this.currentScore++
if (this.currentScore > Number(this.highScore)) {
this.highScore = this.currentScore.toString()
}
})
}
}
@StorageLink('highScore') highScore: string = '0'
@State currentScore: number = 0
build() {
Column() {
if (this.currentScore === Number(this.highScore)) {
Text(`new highScore : ${this.highScore}`).fontSize(18)
}
Button(`goal!, currentScore : ${this.currentScore}`)
.margin(20)
.onClick(() => {
this.currentScore++
if (this.currentScore > Number(this.highScore)) {
this.highScore = this.currentScore.toString()
}
})
}.width('100%')
}
}
```
![PersistentStorage](figures/PersistentStorage.gif)
## [Environment](../reference/arkui-ts/ts-state-management.md#environment)
## Environment
Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态数据,这些数据描述了应用程序运行的设备环境,包括系统语言、深浅色模式等等。Environment及其属性是不可变的,所有数据类型均为简单类型。如下示例展示了从Environment获取系统是否开启无障碍屏幕朗读:
Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态属性,这些属性描述了应用程序运行的设备环境。Environment及其属性是不可变的,所有属性值类型均为简单类型。如下示例展示了从Environment获取语音环境:
```
Environment.EnvProp("accessibilityEnabled", "default");
var enable = AppStorage.Get("accessibilityEnabled");
```ts
Environment.EnvProp('accessibilityEnabled', 'default')
var enable = AppStorage.Get('accessibilityEnabled')
```
accessibilityEnabled是Environment提供默认系统变量识别符。首先需要将对应系统属性绑定到AppStorage中,再通过AppStorage中的方法或者装饰器访问对应系统的属性数据。
accessibilityEnabled是Environment提供的系统默认变量识别符。首先需要将对应系统属性绑定到AppStorage上,再通过AppStorage中的方法或者装饰器访问对应的系统属性数据。
\ No newline at end of file
# 基本概念
ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之前,也可以时全局范围内的传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。
ArkTS提供了多维度的状态管理机制,在ArkUI开发框架中,和UI相关联的数据,不仅可以在组件内使用,还可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,也可以是应用全局范围内的传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活地利用这些能力来实现数据和UI的联动。
![](figures/CoreSpec_figures_state-mgmt-overview.png)
......@@ -11,20 +11,20 @@ ArkTS提供了多维度的状态管理机制,在UI开发框架中,和UI相
| 装饰器 | 装饰内容 | 说明 |
| ----------- | ------------------------- | ------------------------------------------------------------ |
| @State | 基本数据类型,类,数组 | 修饰的状态数据被修改时会触发组件的build方法进行UI界面更新。 |
| @Prop | 基本数据类型 | 修改后的状态数据用于在父组件和子组件之间建立单向数据依赖关系。修改父组件关联数据时,更新当前组件的UI。 |
| @Prop | 基本数据类型 | 修改后的状态数据用于在父组件和子组件之间建立单向数据依赖关系。修改父组件关联数据时,当前组件会重新渲染。 |
| @Link | 基本数据类型,类,数组 | 父子组件之间的双向数据绑定,父组件的内部状态数据作为数据源,任何一方所做的修改都会反映给另一方。 |
| @Observed | 类 | @Observed应用于类,表示该类中的数据变更被UI页面管理。 |
| @ObjectLink | 被@Observed所装饰类的对象 | 装饰的状态数据被修改时,在父组件或者其他兄弟组件内与它关联的状态数据所在的组件都会更新UI。 |
| @ObjectLink | 被@Observed所装饰类的对象 | @ObjectLink装饰的状态数据被修改时,在父组件或者其他兄弟组件内与它关联的状态数据所在的组件都会重新渲染。 |
| @Consume | 基本数据类型,类,数组 | @Consume装饰的变量在感知到@Provide装饰的变量更新后,会触发当前自定义组件的重新渲染。 |
| @Provide | 基本数据类型,类,数组 | @Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。 |
| @Provide | 基本数据类型,类,数组 | @Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面重新渲染。 |
## 应用级变量的状态管理
AppStorage是整个UI应用程序状态的中心“数据库”,UI框架会针对应用程序创建单例AppStorage对象,并提供相应的装饰器和接口供应用程序使用。
AppStorage是整个应用程序状态的中心“数据库”,UI框架会针对应用程序创建单例AppStorage对象,并提供相应的装饰器和接口供应用程序使用。
- @StorageLink:@StorageLink(name)的原理类似于@Consume(name),不同的是,该给定名称的链接对象是从AppStorage中获得的,在UI组件和AppStorage之间建立双向绑定同步数据。
- @StorageProp:@StorageProp(name)将UI组件属性与AppStorage进行单向同步,AppStorage中值的更改会更新组件中的属性,但UI组件无法更改AppStorage中的属性值
- AppStorage还提供用于业务逻辑实现的API,用于添加、读取、修改和删除应用程序的状态属性,此API所做的更改会导致修改的状态数据同步到UI组件上进行UI更新。
- PersistentStorage类提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
- Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态属性,这些属性描述了应用程序运行的设备环境。
- @StorageProp:@StorageProp(name)将UI组件数据与AppStorage进行单向同步,AppStorage中值的更改会更新UI组件中的数据,但UI组件无法更改AppStorage中的数据
- AppStorage还提供了用于业务逻辑实现的API,用于添加、读取、修改和删除应用程序的状态数据,此API所做的更改会导致修改的状态数据同步到UI组件上进行UI更新。
- PersistentStorage提供了一些静态方法用来管理应用持久化数据,可以将特定标记的持久化数据链接到AppStorage中,并由AppStorage接口访问对应持久化数据,或者通过@StorageLink装饰器来访问对应key的变量。
- Environment是框架在应用程序启动时创建的单例对象,它为AppStorage提供了一系列应用程序需要的环境状态数据,这些数据描述了应用程序运行的设备环境。
......@@ -8,182 +8,191 @@
@State状态数据具有以下特征:
- 支持多种类型:允许class、number、boolean、string强类型的按值和按引用类型。允许这些强类型构成的数组,即Array&lt;class&gt;、Array&lt;string&gt;、Array&lt;boolean&gt;、Array&lt;number&gt;。不允许object和any。
- 支持多种类型数据:支持class、number、boolean、string强类型数据的值类型和引用类型,以及这些强类型构成的数组,即Array&lt;class&gt;、Array&lt;string&gt;、Array&lt;boolean&gt;、Array&lt;number&gt;。不支持object和any。
- 支持多实例:组件不同实例的内部状态数据独立。
- 内部私有:标记为@State的属性是私有变量,只能在组件内访问。
- 需要本地初始化:必须为所有@State变量分配初始值,将变量保持未初始化可能导致框架行为未定义
- 创建自定义组件时支持通过状态变量名设置初始值:在创建组件实例时,可以通过变量名显式指定@State状态属性的初始值。
- 需要本地初始化:必须为所有@State变量分配初始值,变量未初始化可能导致未定义的框架异常行为
- 创建自定义组件时支持通过状态变量名设置初始值:在创建组件实例时,可以通过变量名显式指定@State状态变量的初始值。
**示例:**
在下面的示例中:
- 用户定义的组件MyComponent定义了@State状态变量count和title。如果count或title的值发生变化,则执行MyComponent的build方法来重新渲染组件;
- EntryComponent中有多个MyComponent组件实例,第一个MyComponent内部状态的更改不会影响第二个MyComponent;
- 创建MyComponent实例时通过变量名给组件内的变量进行初始化,如:
```ts
MyComponent({title: {value: 'Hello, World 2'}, count: 7})
MyComponent({ title: { value: 'Hello World 2' }, count: 7 })
```
```ts
// xxx.ets
class Model {
value: string
constructor(value: string) {
this.value = value
}
value: string
constructor(value: string) {
this.value = value
}
}
@Entry
@Component
struct EntryComponent {
build() {
Column() {
MyComponent({count: 1, increaseBy: 2}) // MyComponent1 in this document
MyComponent({title: {value: 'Hello, World 2'}, count: 7}) //MyComponent2 in this document
}
build() {
Column() {
MyComponent({ count: 1, increaseBy: 2 }) // 第1个MyComponent实例
MyComponent({ title: { value: 'Hello World 2' }, count: 7 }) // 第2个MyComponent实例
}
}
}
@Component
struct MyComponent {
@State title: Model = {value: 'Hello World'}
@State count: number = 0
private toggle: string = 'Hello World'
private increaseBy: number = 1
build() {
Column() {
Text(`${this.title.value}`).fontSize(30)
Button() {
Text(`Click to change title`).fontSize(20).fontColor(Color.White)
}.onClick(() => {
this.title.value = (this.toggle == this.title.value) ? 'Hello World' : 'Hello UI'
}) // Modify the internal state of MyComponent using the anonymous method.
Button() {
Text(`Click to increase count=${this.count}`).fontSize(20).fontColor(Color.White)
}.onClick(() => {
this.count += this.increaseBy
}) // Modify the internal state of MyComponent using the anonymous method.
}
@State title: Model = { value: 'Hello World' }
@State count: number = 0
private toggle: string = 'Hello World'
private increaseBy: number = 1
build() {
Column() {
Text(`${this.title.value}`).fontSize(30)
Button('Click to change title')
.margin(20)
.onClick(() => {
// 修改内部状态变量title
this.title.value = (this.toggle == this.title.value) ? 'Hello World' : 'Hello ArkUI'
})
Button(`Click to increase count=${this.count}`)
.margin(20)
.onClick(() => {
// 修改内部状态变量count
this.count += this.increaseBy
})
}
}
}
```
![@state1](figures/@state.png)
## @Prop
@Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但更改不会通知给父组件,即@Prop属于单向数据绑定。
@Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但变量的更改不会通知给父组件,即@Prop属于单向数据绑定。
@Prop状态数据具有以下特征:
- 支持简单类型:仅支持number、string、boolean简单类型;
- 私有:仅组件内访问;
- 支持简单类型:仅支持number、string、boolean等简单数据类型;
- 私有:仅支持组件内访问;
- 支持多个实例:一个组件中可以定义多个标有@Prop的属性;
- 创建自定义组件时将值传递给@Prop变量进行初始化:在创建组件的新实例时,必须初始化所有@Prop变量,不支持在组件内部进行初始化。
**示例:**
在下面的示例中,当按“+1”或“-1”按钮时,父组件状态发生变化,重新执行build方法,此时将创建一个新的CountDownComponent组件。父组件的countDownStartValue状态属性被用于初始化子组件的@Prop变量,当按下子组件的“Try again”按钮时,其@Prop变量count将被更改,CountDownComponent重新渲染。但是count值的更改不会影响父组件的countDownStartValue值。
在下面的示例中,当按“+1”或“-1”按钮时,父组件状态发生变化,重新执行build方法,此时将创建一个新的CountDownComponent组件实例。父组件的countDownStartValue状态变量被用于初始化子组件的@Prop变量,当按下子组件的“count - costOfOneAttempt”按钮时,其@Prop变量count将被更改,CountDownComponent重新渲染,但是count值的更改不会影响父组件的countDownStartValue值。
```ts
// xxx.ets
@Entry
@Component
struct ParentComponent {
@State countDownStartValue: number = 10 // 10 Nuggets default start value in a Game
build() {
Column() {
Text(`Grant ${this.countDownStartValue} nuggets to play.`)
Button() {
Text('+1 - Nuggets in New Game')
}.onClick(() => {
this.countDownStartValue += 1
})
Button() {
Text('-1 - Nuggets in New Game')
}.onClick(() => {
this.countDownStartValue -= 1
})
// 创建子组件时,必须在构造函数参数中提供其@Prop变量的初始值,同时初始化常规变量CostOfOneAttump(非Prop)
CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2})
}
@State countDownStartValue: number = 10 // 初始化countDownStartValue
build() {
Column() {
Text(`Grant ${this.countDownStartValue} nuggets to play.`).fontSize(18)
Button('+1 - Nuggets in New Game')
.margin(15)
.onClick(() => {
this.countDownStartValue += 1
})
Button('-1 - Nuggets in New Game')
.margin(15)
.onClick(() => {
this.countDownStartValue -= 1
})
// 创建子组件时,必须在构造函数参数中提供其@Prop变量count的初始值,同时初始化常规变量costOfOneAttempt(非Prop变量)
CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
}
}
}
@Component
struct CountDownComponent {
@Prop count: number
private costOfOneAttempt: number
build() {
Column() {
if (this.count > 0) {
Text(`You have ${this.count} Nuggets left`)
} else {
Text('Game over!')
}
Button() {
Text('Try again')
}.onClick(() => {
this.count -= this.costOfOneAttempt
})
}
@Prop count: number
private costOfOneAttempt: number
build() {
Column() {
if (this.count > 0) {
Text(`You have ${this.count} Nuggets left`).fontSize(18)
} else {
Text('Game over!').fontSize(18)
}
Button('count - costOfOneAttempt')
.margin(15)
.onClick(() => {
this.count -= this.costOfOneAttempt
})
}
}
}
```
> **说明:** 创建新组件实例时,必须初始化其所有@Prop变量。
## @Link
@Link装饰的变量可以和父组件的@State变量建立双向数据绑定:
- 支持多种类型:@Link变量的值与@State变量的类型相同,即class、number、string、boolean或这些类型的数组;
- 私有:仅组件内访问;
- 单个数据源:初始化@Link变量的父组件的变量必须是@State变量;
- 双向通信:子组件对@Link变量的更改将同步修改父组件的@State变量;
- 支持多种类型:@Link支持的数据类型与@State相同,即class、number、string、boolean或这些类型的数组;
- 私有:仅支持组件内访问;
- 单个数据源:父组件中用于初始化子组件@Link变量的必须是@State变量;
- 双向通信:子组件对@Link变量的更改将同步修改父组件的@State变量;
- 创建自定义组件时需要将变量的引用传递给@Link变量,在创建组件的新实例时,必须使用命名参数初始化所有@Link变量。@Link变量可以使用@State变量或@Link变量的引用进行初始化,@State变量可以通过`'$'`操作符创建引用。
> 说明: @Link变量不能在组件内部进行初始化
> **说明:** @Link变量不能在组件内部进行初始化。
**简单类型示例:**
@Link语义是从`'$'`操作符引出,即`$isPlaying``this.isPlaying`内部状态的双向数据绑定。当单击PlayButton时,PlayButton的Image组件和Text组件将同时进行刷新。
@Link语义是从`'$'`操作符引出,即`$isPlaying``this.isPlaying`内部状态的双向数据绑定。当单击子组件PlayButton中的按钮时,@Link变量更改,PlayButton与父组件中的Text和Button将同时进行刷新,同样地,当点击父组件中的Button修改`this.isPlaying`时,子组件PlayButton与父组件中的Text和Button也将同时刷新。
```ts
// xxx.ets
@Entry
@Component
struct Player {
@State isPlaying: boolean = false
build() {
Column() {
PlayButton({buttonPlaying: $isPlaying})
Text(`Player is ${this.isPlaying? '':'not'} playing`)
}
@State isPlaying: boolean = false
build() {
Column() {
PlayButton({ buttonPlaying: $isPlaying })
Text(`Player is ${this.isPlaying ? '' : 'not'} playing`).fontSize(18)
Button('Parent:' + this.isPlaying)
.margin(15)
.onClick(() => {
this.isPlaying = !this.isPlaying
})
}
}
}
@Component
struct PlayButton {
@Link buttonPlaying: boolean
build() {
Column() {
Button() {
Image(this.buttonPlaying? 'play.png' : 'pause.png')
}.onClick(() => {
this.buttonPlaying = !this.buttonPlaying
})
}
@Link buttonPlaying: boolean
build() {
Column() {
Button(this.buttonPlaying ? 'pause' : 'play')
.margin(20)
.onClick(() => {
this.buttonPlaying = !this.buttonPlaying
})
}
}
}
```
......@@ -194,82 +203,97 @@ struct PlayButton {
@Entry
@Component
struct Parent {
@State arr: number[] = [1, 2, 3]
build() {
Column() {
Child({items: $arr})
ForEach(this.arr,
item => Text(`${item}`),
item => item.toString())
}
@State arr: number[] = [1, 2, 3]
build() {
Column() {
Child({ items: $arr })
Button('Parent Button: splice')
.margin(10)
.onClick(() => {
this.arr.splice(0, 1, 60)
})
ForEach(this.arr, item => {
Text(item.toString()).fontSize(18).margin(10)
}, item => item.toString())
}
}
}
@Component
struct Child {
@Link items: number[]
build() {
Column() {
Button() {
Text('Button1: push')
}.onClick(() => {
this.items.push(100)
})
Button() {
Text('Button2: replace whole item')
}.onClick(() => {
this.items = [100, 200, 300]
})
}
@Link items: number[]
build() {
Column() {
Button('Child Button1: push')
.margin(15)
.onClick(() => {
this.items.push(100)
})
Button('Child Button2: replace whole item')
.margin(15)
.onClick(() => {
this.items = [100, 200, 300]
})
}
}
}
```
## @Link、@State和@Prop结合使用示例
**@Link、@State和@Prop结合使用示例:**
下面示例中,ParentView包含ChildA和ChildB两个子组件,ParentView的状态变量counter分别初始化ChildA和ChildB
下面示例中,ParentView包含ChildA和ChildB两个子组件,ParentView的状态变量counter分别用于初始化ChildA的@Prop变量和ChildB的@Link变量
- ChildB使用@Link建立双向状态绑定。当ChildB修改counterRef状态变量值时,该更改将同步到ParentView和ChildA共享;
- ChildA使用@Prop建立从ParentView到自身的单向状态绑定。当ChildA修改状态时,ChildA将重新渲染,但该更改不会传达给ParentView和ChildB。
- ChildB使用@Link建立双向数据绑定,当ChildB修改counterRef状态变量值时,该更改将同步到ParentView和ChildA共享;
- ChildA使用@Prop建立从ParentView到自身的单向数据绑定,当ChildA修改counterVal状态变量值时,ChildA将重新渲染,但该更改不会传达给ParentView和ChildB。
```ts
// xxx.ets
@Entry
@Component
struct ParentView {
@State counter: number = 0
build() {
Column() {
ChildA({counterVal: this.counter}) // pass by value
ChildB({counterRef: $counter}) // $ creates a Reference that can be bound to counterRef
}
@State counter: number = 0
build() {
Column() {
ChildA({ counterVal: this.counter })
ChildB({ counterRef: $counter })
}
}
}
@Component
struct ChildA {
@Prop counterVal: number
build() {
Button() {
Text(`ChildA: (${this.counterVal}) + 1`)
}.onClick(() => {this.counterVal+= 1})
}
@Prop counterVal: number
build() {
Button(`ChildA: (${this.counterVal}) + 1`)
.margin(15)
.onClick(() => {
this.counterVal += 1
})
}
}
@Component
struct ChildB {
@Link counterRef: number
build() {
Button() {
Text(`ChildB: (${this.counterRef}) + 1`)
}.onClick(() => {this.counterRef+= 1})
}
@Link counterRef: number
build() {
Button(`ChildB: (${this.counterRef}) + 1`)
.margin(15)
.onClick(() => {
this.counterRef += 1
})
}
}
```
## Observed和ObjectLink数据管理
## @Observed和ObjectLink数据管理
当开发者需要在子组件中针对父组件的一个变量(parent_a)设置双向同步时,开发者可以在父组件中使用\@State装饰变量(parent_a),并在子组件中使用@Link装饰相应的变量(child_a)。这样的话,不仅可以实现父组件与单个子组件之间的数据同步,也可以实现父组件与多个子组件之间的数据同步。如下图所示,可以看到,父子组件针对ClassA类型的变量设置了双向同步,那么当子组件1中变量的属性c的值变化时,会通知父组件同步变化,而当父组件中属性c的值变化时,会通知所有子组件同步变化。
当开发者需要在子组件中针对父组件的一个变量(parent_a)设置双向同步时,开发者可以在父组件中使用@State装饰变量(parent_a),并在子组件中使用@Link装饰对应的变量(child_a)。这样不仅可以实现父组件与单个子组件之间的数据同步,也可以实现父组件与多个子组件之间的数据同步。如下图所示,可以看到,父子组件针对ClassA类型的变量设置了双向同步,那么当子组件1中变量对应的属性c的值变化时,会通知父组件同步变化,而当父组件中属性c的值变化时,会通知所有子组件同步变化。
![zh-cn_image_0000001251090821](figures/zh-cn_image_0000001251090821.png)
......@@ -279,17 +303,17 @@ struct ChildB {
### 设置要求
- @Observed 用于类,@ObjectLink 用于变量。
- @Observed用于类,@ObjectLink用于变量。
- @ObjectLink装饰的变量类型必须为类(class type)。
- 类要被\@Observed装饰器所装饰。
- 类要被@Observed装饰器所装饰。
- 不支持简单类型参数,可以使用@Prop进行单向同步。
- @ObjectLink装饰的变量是不可变的(immutable)
- 属性的改动是被允许的,当改动发生时,如果同一个对象被多个@ObjectLink变量所引用,那么所有拥有这些变量的自定义组件都会被通知重新渲染。
- @ObjectLink装饰的变量是不可变的。
- 属性的改动是被允许的,当改动发生时,如果同一个对象被多个@ObjectLink变量所引用,那么所有拥有这些变量的自定义组件都会被通知进行重新渲染。
- @ObjectLink装饰的变量不可设置默认值。
- 必须让父组件中有一个由@State、@Link、@StorageLink、@Provide或@Consume所装饰变量参与的TS表达式进行初始化。
- 必须让父组件中有一个由@State、@Link、@StorageLink、@Provide或@Consume装饰的变量所参与的TS表达式进行初始化。
- @ObjectLink装饰的变量是私有变量,只能在组件内访问。
......@@ -299,30 +323,31 @@ struct ChildB {
```ts
// xxx.ets
// 父组件ViewB中的类对象ClassA与子组件ViewA保持数据同步时,可以使用@ObjectLink和@Observed,绑定该数据对象的父组件和其他子组件同步更新
var nextID: number = 0;
var nextID: number = 0
@Observed
class ClassA {
public name : string;
public c: number;
public id : number;
public name: string
public c: number
public id: number
constructor(c: number, name: string = 'OK') {
this.name = name;
this.c = c;
this.id = nextID++;
this.name = name
this.c = c
this.id = nextID++
}
}
@Component
struct ViewA {
label : string = "ViewA1";
@ObjectLink a: ClassA;
label: string = 'ViewA1'
@ObjectLink a: ClassA
build() {
Row() {
Button(`ViewA [${this.label}] this.a.c= ${this.a.c} +1`)
.onClick(() => {
this.a.c += 1;
this.a.c += 1
})
}.margin({ top: 10 })
}
......@@ -331,59 +356,57 @@ struct ViewA {
@Entry
@Component
struct ViewB {
@State arrA : ClassA[] = [ new ClassA(0), new ClassA(0) ];
@State arrA: ClassA[] = [new ClassA(0), new ClassA(0)]
build() {
Column() {
ForEach (this.arrA, (item) => {
ViewA({label: `#${item.id}`, a: item})
},
(item) => item.id.toString()
)
ViewA({label: `ViewA this.arrA[first]`, a: this.arrA[0]})
ViewA({label: `ViewA this.arrA[last]`, a: this.arrA[this.arrA.length-1]})
ForEach(this.arrA, (item) => {
ViewA({ label: `#${item.id}`, a: item })
}, (item) => item.id.toString())
ViewA({ label: `this.arrA[first]`, a: this.arrA[0] })
ViewA({ label: `this.arrA[last]`, a: this.arrA[this.arrA.length - 1] })
Button(`ViewB: reset array`)
.margin({ top: 10 })
.onClick(() => {
this.arrA = [ new ClassA(0), new ClassA(0) ];
this.arrA = [new ClassA(0), new ClassA(0)]
})
Button(`ViewB: push`)
.margin({ top: 10 })
.margin({ top: 10 })
.onClick(() => {
this.arrA.push(new ClassA(0))
this.arrA.push(new ClassA(0))
})
Button(`ViewB: shift`)
.margin({ top: 10 })
.margin({ top: 10 })
.onClick(() => {
this.arrA.shift()
this.arrA.shift()
})
}
}.width('100%')
}
}
```
![Observed](figures/Observed.gif)
## @Consume和@Provide
Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。Consume在感知到Provide数据的更新后,会触发当前view的重新渲染。
@Provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。@Consume在感知到@Provide数据的更新后,会触发当前自定义组件的重新渲染。
> **说明:** 使用@Provide 和@Consume时避免循环引用导致死循环。
> **说明:** 使用@Provide和@Consume时应避免循环引用导致死循环。
表1 @Provide
### @Provide
| 名称 | 说明 |
| -------------- | ------------------------------------------------------------ |
| 装饰器参数 | 是一个string类型的常量,用于给装饰的变量起别名。如果规定别名,则提供对应别名的数据更新。如果没有,则使用变量名作为别名。推荐使用@Provide("alias")这种形式。 |
| 装饰器参数 | 是一个string类型的常量,用于给装饰的变量起别名。如果规定别名,则提供对应别名的数据更新。如果没有,则使用变量名作为别名。推荐使用@Provide('alias')这种形式。 |
| 同步机制 | @Provide的变量类似@State,可以修改对应变量进行页面重新渲染。也可以修改@Consume装饰的变量,反向修改@State变量。 |
| 初始值 | 必须制定初始值。 |
| 初始值 | 必须设置初始值。 |
| 页面重渲染场景 | 触发页面渲染的修改: <br/>- 基础类型(boolean,string,number)变量的改变; <br/>- @Observed class类型变量及其属性的修改; <br/>- 添加,删除,更新数组中的元素。 |
表2 @Consume
### @Consume
| 类型 | 说明 |
| ------ | ---------------- |
| 初始值 | 不可设置默认值。 |
| 初始值 | 不可设置默认初始值。 |
### 示例
......@@ -392,18 +415,16 @@ Provide作为数据的提供方,可以更新其子孙节点的数据,并触
@Entry
@Component
struct CompA {
@Provide("reviewVote") reviewVotes : number = 0;
@Provide("reviewVote") reviewVotes: number = 0;
build() {
Column() {
CompB()
Button() {
Text(`${this.reviewVotes}`)
.fontSize(30)
}
.onClick(() => {
this.reviewVotes += 1;
})
Button(`CompA: ${this.reviewVotes}`)
.margin(10)
.onClick(() => {
this.reviewVotes += 1;
})
}
}
}
......@@ -419,17 +440,16 @@ struct CompB {
@Component
struct CompC {
@Consume("reviewVote") reviewVotes : number;
@Consume("reviewVote") reviewVotes: number
build() {
Column() {
Button() {
Text(`${this.reviewVotes}`)
.fontSize(30)
}
.onClick(() => {
this.reviewVotes += 1;
})
}.margin({ left:10, top: 10 })
Button(`CompC: ${this.reviewVotes}`)
.margin(10)
.onClick(() => {
this.reviewVotes += 1
})
}.width('100%')
}
}
```
......@@ -438,41 +458,53 @@ struct CompC {
@Watch用于监听状态变量的变化,语法结构为:
```
```ts
@State @Watch("onChanged") count : number = 0
```
如上给状态变量增加一个@Watch装饰器,通过@Watch注册一个回调方法onChanged, 当状态变量count被改变时, 触发onChanged回调。
如上所示,给状态变量增加一个@Watch装饰器,通过@Watch注册一个回调方法onChanged, 当状态变量count被改变时, 触发onChanged回调。
装饰器@State、@Prop、@Link、@ObjectLink、@Provide、@Consume、@StorageProp以及@StorageLink装饰的变量可以监听其变化。
装饰器@State、@Prop、@Link、@ObjectLink、@Provide、@Consume、@StorageProp以及@StorageLink所装饰的变量均可以通过@Watch监听其变化。
```
```ts
// xxx.ets
@Entry
@Component
struct CompA {
@State @Watch("onBasketUpdated") shopBasket : Array<number> = [ 7, 12, 47, 3 ];
@State totalPurchase : number = 0;
updateTotal() : number {
let sum = 0;
this.shopBasket.forEach((i) => { sum += i; });
// 计算新的购物篮总价值,如果超过100RMB,则适用折扣
this.totalPurchase = (sum < 100) ? sum : 0.9 * sum;
return this.totalPurchase;
}
@State @Watch('onBasketUpdated') shopBasket: Array<number> = [7, 12, 47, 3]
@State totalPurchase: number = 0
@State addPurchase: number = 0
// @Watch cb
onBasketUpdated(propName: string) : void {
this.updateTotal();
}
aboutToAppear() {
this.updateTotal()
}
updateTotal(): number {
let sum = 0;
this.shopBasket.forEach((i) => {
sum += i
})
// 计算新的购物篮总价值,如果超过100,则适用折扣
this.totalPurchase = (sum < 100) ? sum : 0.9 * sum
return this.totalPurchase
}
build() {
Column() {
Button("add to basket").onClick(() => { this.shopBasket.push(Math.round(100 * Math.random())) })
Text(`${this.totalPurchase}`)
.fontSize(30)
}
// shopBasket更改时触发该方法
onBasketUpdated(propName: string): void {
this.updateTotal()
}
build() {
Column() {
Button('add to basket ' + this.addPurchase)
.margin(15)
.onClick(() => {
this.addPurchase = Math.round(100 * Math.random())
this.shopBasket.push(this.addPurchase)
})
Text(`${this.totalPurchase}`)
.fontSize(30)
}
}
}
```
\ No newline at end of file
......@@ -15,7 +15,7 @@ Link(propName: string): any
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | :--------------------- |
| -------- | ------ | --- | --------------------- |
| propName | string | 是 | 要双向绑定的属性名称。 |
**返回值:**
......@@ -25,7 +25,7 @@ Link(propName: string): any
| @Link | 在具有给定键的数据,则返回到此属性的双向数据绑定,该双向绑定意味着变量或者组件对数据的更改将同步到AppStorage,通过AppStorage对数据的修改将同步到变量或者组件。 |
```ts
let simple = AppStorage.Link('simpleProp');
let simple = AppStorage.Link('simpleProp')
```
### SetAndLink
......@@ -37,7 +37,7 @@ SetAndLink\<T>(propName: string, defaultValue: T): SubscribedAbstractProperty\<T
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| ------------ | ------ | :--- | -------------------- |
| ------------ | ------ | --- | -------------------- |
| propName | string | 是 | 要进行创建的key值。 |
| defaultValue | T | 是 | 要进行设置的默认值。 |
......@@ -48,7 +48,7 @@ SetAndLink\<T>(propName: string, defaultValue: T): SubscribedAbstractProperty\<T
| @Link | 与Link接口类似,如果当前的key保存于LocalStorage,返回该key值对应的value值。如果该key值未被创建,则创建一个对应的defaultValue的Link返回。 |
```ts
let simple = AppStorage.SetAndLink('simpleProp',121);
let simple = AppStorage.SetAndLink('simpleProp', 121)
```
### Prop
......@@ -60,7 +60,7 @@ Prop(propName: string): any
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--: | ------------------- |
| -------- | ------ | --- | ------------------- |
| propName | string | 是 | 要进行创建的key值。 |
**返回值:**
......@@ -70,7 +70,7 @@ Prop(propName: string): any
| @Prop | 如果存在具有给定键的属性,则返回此属性的单向数据绑定。该单向绑定意味着只能通过AppStorage将属性的更改同步到变量或者组件。该方法返回的变量为不可变变量,适用于可变和不可变的状态属性,如果具有此键的属性不存在则返回undefined。 |
```ts
let simple = AppStorage.Prop('simpleProp');
let simple = AppStorage.Prop('simpleProp')
```
### SetAndProp
......@@ -82,7 +82,7 @@ SetAndProp\<S>(propName: string, defaultValue: S): SubscribedAbstractProperty\<S
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| ------------ | ------ | :--: | --------------------------- |
| ------------ | ------ | --- | --------------------------- |
| propName | string | 是 | 要保存的的键值对中的key值。 |
| defaultValue | S | 是 | 创建的默认值。 |
......@@ -93,7 +93,7 @@ SetAndProp\<S>(propName: string, defaultValue: S): SubscribedAbstractProperty\<S
| @Prop | 如果当前的key保存与LocalStorage,返回该key值对应的value值。如果该key值未被创建,则创建一个对应的defaultValue的Prop返回。 |
```ts
let simple = AppStorage.SetAndProp('simpleProp',121);
let simple = AppStorage.SetAndProp('simpleProp', 121)
```
### Has
......@@ -105,7 +105,7 @@ Has(propName: string): boolean
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | -------------- |
| -------- | ------ | --- | -------------- |
| propName | string | 是 | 属性的属性值。 |
**返回值:**
......@@ -115,7 +115,7 @@ Has(propName: string): boolean
| boolean | 返回属性的属性值是否存在。 |
```ts
let simple = AppStorage.Has('simpleProp');
let simple = AppStorage.Has('simpleProp')
```
### Get
......@@ -137,7 +137,7 @@ Get\<T>(propName: string): T | undefined
| boolean或undefined | 返回属性的属性值是否存在。 |
```ts
let simple = AppStorage.Get('simpleProp');
let simple = AppStorage.Get('simpleProp')
```
### Set
......@@ -149,7 +149,7 @@ Set<T>(propName: string, newValue: T): boolean
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | ----------------- |
| -------- | ------ | --- | ----------------- |
| propName | string | 是 | 要设置的key值。 |
| newValue | T | 是 | 要设置的value值。 |
......@@ -160,7 +160,7 @@ Set<T>(propName: string, newValue: T): boolean
| boolean | 如果存在key值,设置value值并返回true,否则返回false。 |
```ts
let simple = AppStorage.Set('simpleProp');
let simple = AppStorage.Set('simpleProp')
```
### SetOrCreate
......@@ -172,7 +172,7 @@ SetOrCreate<T>(propName: string, newValue: T): void
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | ------------------------- |
| -------- | ------ | --- | ------------------------- |
| propName | string | 是 | 要更新或者创建的key值。 |
| newValue | T | 是 | 要更新或者创建的value值。 |
......@@ -183,7 +183,7 @@ SetOrCreate<T>(propName: string, newValue: T): void
| boolean | 如果已存在与给定键名字相同的属性,更新其值且返回true。如果不存在具有给定名称的属性,在LocalStorage中创建具有给定默认值的新属性,默认值必须是T类型。不允许undefined 或 null 返回true。 |
```ts
let simple = AppStorage.SetOrCreate('simpleProp',121);
let simple = AppStorage.SetOrCreate('simpleProp', 121)
```
### Delete
......@@ -195,7 +195,7 @@ Delete(propName: string): boolean
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | --------------------- |
| -------- | ------ | --- | --------------------- |
| propName | string | 是 | 要删除的属性的key值。 |
**返回值:**
......@@ -205,7 +205,7 @@ Delete(propName: string): boolean
| boolean | 删除key指定的键值对,如果存在且删除成功返回true,不存在或删除失败返回false。 |
```ts
let simple = AppStorage.Delete('simpleProp');
let simple = AppStorage.Delete('simpleProp')
```
### keys
......@@ -221,7 +221,7 @@ keys(): IterableIterator\<string>
| array\<string> | 返回包含所有键的字符串数组。 |
```ts
let simple = AppStorage.Keys();
let simple = AppStorage.Keys()
```
### staticClear
......@@ -237,7 +237,7 @@ staticClear(): boolean
| boolean | 删除所有的属性,如果当前有状态变量依旧引用此属性,返回false。 |
```ts
let simple = AppStorage.staticClear();
let simple = AppStorage.staticClear()
```
### IsMutable
......@@ -259,7 +259,7 @@ IsMutable(propName: string): boolean
| boolean | 返回此属性是否存在并且是否可以改变。 |
```ts
let simple = AppStorage.IsMutable();
let simple = AppStorage.IsMutable()
```
### Size
......@@ -275,7 +275,7 @@ Size(): number
| number | 返回键值对的数量。 |
```ts
let simple = AppStorage.Size();
let simple = AppStorage.Size()
```
## LocalStorage<sup>9+</sup>
......@@ -293,7 +293,7 @@ constructor(initializingProperties?: Object)
| initializingProperties | Object | 否 | object.keys(obj)返回的所有对象属性及其值都将添加到LocalStorage。 |
```ts
this.storage = new LocalStorage();
this.storage = new LocalStorage()
```
### GetShared<sup>9+</sup>
......@@ -311,7 +311,7 @@ static GetShared(): LocalStorage
| [LocalStorage](#localstorage) | 返回LocalStorage对象。 |
```ts
let storage = LocalStorage.GetShared();
let storage = LocalStorage.GetShared()
```
### has<sup>9+</sup>
......@@ -323,7 +323,7 @@ has(propName: string): boolean
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | -------------- |
| -------- | ------ | --- | -------------- |
| propName | string | 是 | 属性的属性值。 |
**返回值:**
......@@ -333,8 +333,8 @@ has(propName: string): boolean
| boolean | 返回属性的属性值是否存在。 |
```ts
this.storage = new LocalStorage();
this.storage.has("storageSimpleProp");
this.storage = new LocalStorage()
this.storage.has('storageSimpleProp')
```
### get<sup>9+</sup>
......@@ -346,7 +346,7 @@ get\<T>(propName: string): T
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | ------------------- |
| -------- | ------ | --- | ------------------- |
| propName | string | 是 | 要获取对应的key值。 |
**返回值:**
......@@ -356,8 +356,8 @@ get\<T>(propName: string): T
| T \| undefined | 当keyvalue存在时,返回keyvalue值。不存在返回undefined。 |
```ts
this.storage = new LocalStorage();
let simpleValue = this.storage.get("storageSimpleProp");
this.storage = new LocalStorage()
let simpleValue = this.storage.get('storageSimpleProp')
```
### set<sup>9+</sup>
......@@ -369,7 +369,7 @@ set\<T>(propName: string, newValue: T): boolean
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | ----------------- |
| -------- | ------ | --- | ----------------- |
| propName | string | 是 | 要设置的key值。 |
| newValue | T | 是 | 要设置的value值。 |
......@@ -380,8 +380,8 @@ set\<T>(propName: string, newValue: T): boolean
| boolean | 如果存在key值,设置value值并返回true,否则返回false。 |
```ts
this.storage = new LocalStorage();
this.storage.set("storageSimpleProp",121);
this.storage = new LocalStorage()
this.storage.set('storageSimpleProp', 121)
```
### setOrCreate<sup>9+</sup>
......@@ -404,8 +404,8 @@ setOrCreate\<T>(propName: string, newValue: T): boolean
| boolean | 如果已存在与给定键名字相同的属性,更新其值且返回true。如果不存在具有给定名称的属性,在LocalStorage中创建具有给定默认值的新属性,默认值必须是T类型,不允许undefined 或 null 。 |
```ts
this.storage = new LocalStorage();
this.storage.setOrCreate("storageSimpleProp",121);
this.storage = new LocalStorage()
this.storage.setOrCreate('storageSimpleProp', 121)
```
### link<sup>9+</sup>
......@@ -417,7 +417,7 @@ link\<T>(propName: string): T
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | ---------------------- |
| -------- | ------ | --- | ---------------------- |
| propName | string | 是 | 要双向绑定的属性名称。 |
**返回值:**
......@@ -427,8 +427,8 @@ link\<T>(propName: string): T
| T | 如果存在具有给定键的属性,返回到此属性的双向绑定,该双向绑定意味着变量或者组件对数据的更改将同步到LocalStorage,然后通过LocalStorage实例同步到任何变量或组件。如果不存在给定键的属性,返回undefined。 |
```ts
this.storage = new LocalStorage();
let localStorage = this.storage.link("storageSimpleProp");
this.storage = new LocalStorage()
let localStorage = this.storage.link('storageSimpleProp')
```
### setAndLink<sup>9+</sup>
......@@ -440,7 +440,7 @@ setAndLink\<T>(propName: string, defaultValue: T): T
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| ------------ | ------ | :--- | -------------------- |
| ------------ | ------ | --- | -------------------- |
| propName | string | 是 | 要进行创建的key值。 |
| defaultValue | T | 是 | 要进行设置的默认值。 |
......@@ -451,8 +451,8 @@ setAndLink\<T>(propName: string, defaultValue: T): T
| @Link | 与Link接口类似,如果当前的key保存于LocalStorage,返回该key值对应的value值。如果该key值未被创建,则创建一个对应的defaultValue的Link返回。 |
```ts
this.storage = new LocalStorage();
let localStorage = this.storage.setAndLink("storageSimpleProp",121);
this.storage = new LocalStorage()
let localStorage = this.storage.setAndLink('storageSimpleProp', 121)
```
### prop<sup>9+</sup>
......@@ -464,7 +464,7 @@ prop\<T>(propName: string): T
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| -------- | ------ | :--- | ----------------------- |
| -------- | ------ | --- | ----------------------- |
| propName | string | 是 | 要单向数据绑定的key值。 |
**返回值:**
......@@ -474,8 +474,8 @@ prop\<T>(propName: string): T
| @Prop | 如果存在具有给定键的属性,返回此属性的单向数据绑定。该单向绑定意味着只能通过LocalStorage将属性的更改同步到变量或组件。该方法返回的变量为不可变变量,适用于可变和不可变的状态变量。如果此键的属性不存在则返回undefined。 |
```ts
this.storage = new LocalStorage();
let localStorage = this.storage.prop("storageSimpleProp");
this.storage = new LocalStorage()
let localStorage = this.storage.prop('storageSimpleProp')
```
### setAndProp<sup>9+</sup>
......@@ -487,7 +487,7 @@ setAndProp\<T>(propName: string, defaultValue: T): T
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| ------------ | ------ | :--- | ------------------------- |
| ------------ | ------ | --- | ------------------------- |
| propName | string | 是 | 要保存的键值对中的key值。 |
| defaultValue | T | 是 | 创建的默认值。 |
......@@ -498,8 +498,8 @@ setAndProp\<T>(propName: string, defaultValue: T): T
| @Prop | 如果当前的key保存与LocalStorage,返回该key值对应的value值。如果该key值未被创建,则创建一个对应的defaultValue的Prop返回。 |
```ts
this.storage = new LocalStorage();
let localStorage = this.storage.setAndProp("storageSimpleProp",121);
this.storage = new LocalStorage()
let localStorage = this.storage.setAndProp('storageSimpleProp', 121)
```
### delete<sup>9+</sup>
......@@ -521,8 +521,8 @@ delete(propName: string): boolean
| boolean | 删除key指定的键值对,如果存在且删除成功返回true,不存在或删除失败返回false。 |
```ts
this.storage = new LocalStorage();
this.storage.delete("storageSimpleProp");
this.storage = new LocalStorage()
this.storage.delete('storageSimpleProp')
```
### keys<sup>9+</sup>
......@@ -538,8 +538,8 @@ keys(): IterableIterator\<string>
| array\<string> | 返回包含所有键的字符串数组。 |
```ts
this.storage = new LocalStorage();
let simple = this.storage.keys();
this.storage = new LocalStorage()
let simple = this.storage.keys()
```
### size<sup>9+</sup>
......@@ -555,8 +555,8 @@ size(): number
| number | 返回键值对的数量。 |
```ts
this.storage = new LocalStorage();
let simple = this.storage.size();
this.storage = new LocalStorage()
let simple = this.storage.size()
```
### Clear<sup>9+</sup>
......@@ -572,11 +572,11 @@ clear(): boolean
| boolean | 删除所有的属性,如果当前有状态变量依旧引用此属性,返回false。 |
```ts
this.storage = new LocalStorage();
let simple = this.storage.clear();
this.storage = new LocalStorage()
let simple = this.storage.clear()
```
## persistentstorage
## PersistentStorage
### constructor
......@@ -587,29 +587,29 @@ constructor(appStorage: AppStorage, storage: Storage)
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| ---------- | ---------- | :--- | -------------------------------- |
| ---------- | ---------- | --- | -------------------------------- |
| appStorage | AppStorage | 是 | 保存所有属性及属性值的单例对象。 |
| storage | Storage | 是 | Storage实例对象。 |
```ts
this.persistentstorage = new PersistentStorage(AppStorage,Storage);
this.persistentstorage = new PersistentStorage(AppStorage,Storage)
```
### PersistProp
PersistProp(key:string,defaultValue:T):void
PersistProp(key:string,defaultValue:T): void
关联命名的属性再AppStorage变为持久化数据。
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| ------------ | ------ | :--- | ----------------------- |
| ------------ | ------ | --- | ----------------------- |
| key | string | 是 | 要关联的属性的key值。 |
| defaultValue | T | 是 | 要关联的属性的value值。 |
```ts
PersistentStorage.PersistProp("highScore", "0");
PersistentStorage.PersistProp('highScore', '0')
```
### DeleteProp
......@@ -621,11 +621,11 @@ DeleteProp(key: string): void
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| ------ | ------ | :--- | --------------------- |
| ------ | ------ | --- | --------------------- |
| key | string | 是 | 要取消的属性的key值。 |
```ts
PersistentStorage.DeleteProp("highScore");
PersistentStorage.DeleteProp('highScore')
```
### PersistProps
......@@ -637,11 +637,11 @@ PersistProps(properties: {key: string, defaultValue: any}[]): void;
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 |
| ------ | ---------------------------------- | :--: | ------------------ |
| ------ | ---------------------------------- | -- | ------------------ |
| key | {key: string, defaultValue: any}[] | 是 | 要关联的属性数组。 |
```ts
PersistentStorage.PersistProps([{"highScore", "0"},{"wightScore","1"}]);
PersistentStorage.PersistProps([{'highScore', '0'},{'wightScore','1'}])
```
### Keys
......@@ -657,7 +657,7 @@ Keys(): Array<string>
| Array<string> | 返回所有持久化属性的标记。 |
```ts
let simple = PersistentStorage.keys();
let simple = PersistentStorage.Keys()
```
> **说明:**
......@@ -673,7 +673,7 @@ let simple = PersistentStorage.keys();
创建一个environment对象。
```ts
let simple = new Environment();
let simple = new Environment()
```
### EnvProp
......@@ -685,34 +685,45 @@ EnvProp\<S>(key: string, value: S): boolean
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 | 参数描述 |
| ------ | ------ | :--- | --------------- | ----------------- |
| key | string | 是 | 要关联的key值 | 要关联的key值。 |
| ------ | ------ | --- | --------------- | ----------------- |
| key | string | 是 | 要关联的key值 | 要关联的key值,支持的范围详见内置环境变量说明。 |
| value | S | 是 | 要关联的value值 | 要关联的value值。 |
**返回值:**
| 类型 | 描述 |
| ------- | ---------------------------------- |
| boolean | 返回该属性再AppStorage中是否存在。 |
| boolean | 返回该属性在AppStorage中是否存在。 |
**内置环境变量说明:**
| key | 类型 | 说明 |
| ------------ | ------------- | ------------------- |
| accessibilityEnabled | boolean | 无障碍屏幕朗读是否启用。 |
| colorMode | ColorMode | 深浅色模式,可选值为:<br>- ColorMode.LIGHT:浅色模式;<br>- ColorMode.DARK:深色模式。 |
| fontScale | number | 字体大小比例,取值范围为[0.85, 1.45]。 |
| fontWeightScale | number | 字重比例,取值范围为[0.6, 1.6]。 |
| layoutDirection | LayoutDirection | 布局方向类型,可选值为:<br>- LayoutDirection.LTR:从左到右;<br>- LayoutDirection.RTL:从右到左。 |
| languageCode | string | 当前系统语言,小写字母,例如zh。 |
```ts
Environment.EnvProp("accessibilityEnabled", "default");
Environment.EnvProp('accessibilityEnabled', 'default')
```
### EnvProps
EnvProps(props: {key: string;defaultValue: any}[]): void
EnvProps(props: {key: string, defaultValue: any}[]): void
关联此系统项数组到AppStorage中
**参数:**
| 参数名 | 类型 | 必填 | 参数描述 | 参数描述 |
| ------ | ---------------------------------- | :--- | ------------------ | ------------------ |
| ------ | ---------------------------------- | --- | ------------------ | ------------------ |
| key | {key: string, defaultValue: any}[] | 是 | 要关联的属性数组。 | 要关联的属性数组。 |
```ts
Environment.EnvProps([{"accessibilityEnabled", "default"},{"accessibilityUnEnabled","undefault"}]);
Environment.EnvProps([{'accessibilityEnabled', 'default'},{'accessibilityUnEnabled','undefault'}])
```
### Keys
......@@ -728,6 +739,6 @@ Keys(): Array<string>
| Array<string> | 返回关联的系统项数组。 |
```ts
let simple = Environment.keys();
let simple = Environment.Keys()
```
......@@ -11,18 +11,15 @@
- [资源文件的分类](ui-ts-basic-resource-file-categories.md)
- [资源访问](ts-resource-access.md)
- [像素单位](ts-pixel-units.md)
- 深入理解组件化
- [自定义组件初始化](ts-custom-component-initialization.md)
- [自定义组件生命周期回调函数](ts-custom-component-lifecycle-callbacks.md)
- [组件创建和重新初始化示例](ts-component-creation-re-initialization.md)
- 常见组件开发指导
- [Button开发指导](ui-ts-basic-components-button.md)
- [Web开发指导](ui-ts-components-web.md)
- 常见布局开发指导
- [弹性布局](ui-ts-layout-flex.md)
- [栅格布局](ui-ts-layout-grid-container.md)
- [媒体查询](ui-ts-layout-mediaquery.md)
- [Web组件开发](ui-ts-components-web.md)
- 体验声明式UI
- [创建声明式UI工程](ui-ts-creating-project.md)
- [初识Component](ui-ts-components.md)
......@@ -33,7 +30,6 @@
- [构建食物分类Grid布局](ui-ts-building-category-grid-layout.md)
- [页面跳转与数据传递](ui-ts-page-redirection-data-transmission.md)
- [性能提升的推荐方法](ts-performance-improvement-recommendation.md)
- UI开发(兼容JS的类Web开发范式)
- [概述](ui-js-overview.md)
- 框架说明
......
......@@ -24,7 +24,7 @@ FA应用的ArkTS模块(entry/src/main)的典型开发目录结构如下:
> **说明:**
>
> - 资源目录resources文件夹位于src/main下,此目录下资源文件的详细规范以及子目录结构规范参看[资源文件的分类](ui-ts-basic-resource-file-categories.md)。
>- 页面支持导入TypeScript和JavaScript文件。
> - 页面支持导入TypeScript和JavaScript文件。
**js标签配置:**
......
# Button
Button是按钮组件,通常用于响应用户的点击操作,如提交表单,其类型包括胶囊按钮、圆形按钮、普通按钮。具体用法请参考[Button](../reference/arkui-ts/ts-basic-components-button.md)
## 创建按钮
Button通过调用接口来创建,接口调用有以下两种形式:
- 创建包含子组件的按钮
`Button(options?: {type?: ButtonType, stateEffect?: boolean})`,该接口用于创建包含子组件的按钮,其中type用于设置Button类型,stateEffect属性设置Button是否开启点击效果。
```
Button({ type: ButtonType.Normal, stateEffect: true }) {
Row() {
Image($r('app.media.loading')).width(20).height(20).margin({ left: 12 })
Text('loading').fontSize(12).fontColor(0xffffff).margin({ left: 5, right: 12 })
}.alignItems(VerticalAlign.Center)
}.borderRadius(8).backgroundColor(0x317aff).width(90)
```
![zh-cn_image_0000001260555857](figures/zh-cn_image_0000001260555857.png)
- 创建不包含子组件的按钮
`Button(label?: string, options?: { type?: ButtonType, stateEffect?: boolean })`,该接口用于创建不包含子组件的按钮,其中label确定所创建的Button是否包含子组件。
```
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.borderRadius(8)
.backgroundColor(0x317aff)
.width(90)
```
![zh-cn_image_0000001215796030](figures/zh-cn_image_0000001215796030.png)
## 设置按钮类型
Button有三种可选类型,分别为Capsule(胶囊类型)、Circle(圆形按钮)和Normal(普通按钮),通过type进行设置。
- 胶囊按钮(默认类型)
```ts
Button('Disable', { type: ButtonType.Capsule, stateEffect: false })
.backgroundColor(0x317aff)
.width(90)
```
![zh-cn_image_0000001215645452](figures/zh-cn_image_0000001215645452.png)
- 圆形按钮
```ts
Button('Circle', { type: ButtonType.Circle, stateEffect: false })
.backgroundColor(0x317aff)
.width(90)
.height(90)
```
![zh-cn_image_0000001215965420](figures/zh-cn_image_0000001215965420.png)
## 自定义样式
- 设置边框弧度
一般使用通用属性来自定义按钮样式。例如通过borderRadius属性设置按钮的边框弧度。
```ts
Button('circle border', { type: ButtonType.Normal })
.borderRadius(20)
```
![zh-cn_image_0000001190463780](figures/zh-cn_image_0000001190463780.png)
- 设置文本样式
通过添加文本样式设置按钮文本的展示样式。
```ts
Button('font style', { type: ButtonType.Normal })
.fontSize(20)
.fontColor(Color.Red)
.fontWeight(800)
```
![zh-cn_image_0000001189744672](figures/zh-cn_image_0000001189744672.png)
- 设置背景颜色
添加backgroundColor属性设置按钮的背景颜色。
```ts
Button('background color').backgroundColor(0xF55A42)
```
![zh-cn_image_0000001235146483](figures/zh-cn_image_0000001235146483.png)
- 用作功能型按钮
为删除操作创建一个按钮。
```ts
Button({ type: ButtonType.Circle, stateEffect: true }) {
Image($r('app.media.ic_public_delete_filled')).width(30).height(30)
}.width(55).height(55).margin({ left: 20 }).backgroundColor(0xF55A42)
```
![zh-cn_image_0000001260373911](figures/zh-cn_image_0000001260373911.png)
## 添加事件
Button组件通常用于触发某些操作,可以在绑定onClick事件来响应点击操作后的自定义行为。
```ts
Button('Ok', { type: ButtonType.Normal, stateEffect: true })
.onClick(()=>{
console.info('Button onClick')
})
```
## 场景示例
- 用于启动操作
可以将按钮用于启动操作的任何用户界面元素。按钮会根据用户的操作触发相应的事件。如,在List容器里边通过点击按钮进行页面跳转:
```ts
// xxx.ets
import router from '@ohos.router'
@Entry
@Component
struct ButtonCase1 {
build() {
List({ space: 4 }) {
ListItem() {
Button("First").onClick(() => {
router.push({ url: 'xxx' })
})
}
ListItem() {
Button("Second").onClick(() => {
router.push({ url: 'yyy' })
})
}
ListItem() {
Button("Third").onClick(() => {
router.push({ url: 'zzz' })
})
}
}
.listDirection(Axis.Vertical)
.backgroundColor(0xDCDCDC).padding(20)
}
}
```
​ ![zh-cn_image_0000001235626467](figures/zh-cn_image_0000001235626467.png)
- 用于表单的提交
在用户登录/注册页面,用户的登录或注册的提交操作会用按钮。
```ts
// xxx.ets
@Entry
@Component
struct ButtonCase2 {
build() {
Column() {
TextInput({ placeholder: 'input your username' }).margin({ top: 20 })
TextInput({ placeholder: 'input your password' }).type(InputType.Password).margin({ top: 20 })
Button('Register').width(300).margin({ top: 20 })
}.padding(20)
}
}
```
![zh-cn_image_0000001190466492](figures/zh-cn_image_0000001190466492.png)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册