基本語法
文件構成
一個文件以 __.ets__結尾的,基本上要包含
-
裝飾器
- @Entry
- @Component
- @builder
- @State
- ...
-
自定義組件
用@Component裝飾的struct Index -
變量聲明
-
UI描述
build方法裏包含的代碼塊 -
系統組件
ArkUI框架中默認內置的基礎和容器組件,可直接被開發者調用。
就是ArkTS所支持的語法,類似於html中的標籤(span,div,p,image), -
屬性方法
css屬性,鏈式調用,看以下例子 -
事件方法
@Entry // 裝飾器
@Component // 裝飾器 自定義組件聲明
struct Index{ // 自定義組件
@State message: string = 'hello arkTS' // 裝飾器 變量聲明
private color:string = 'red' // 變量聲明
build(){ // UI描述
Column(){ // 系統組件
Text(this.message) // 系統組件
.fontSize(20)
.fontWeight(400)
.onClick(()=>{}) // 事件方法
}
.height(100) // 屬性方法
}
}
裝飾器
@Entry
入口文件的標識,@Entry裝飾的自定義組件將作爲UI頁面的入口。在單個UI頁面中, 最多可以使用@Entry裝飾一個自定義組件
只能有一個build函數,其build()函數下的根節點唯一且必要,且必須爲容器組件,其中ForEach禁止作爲根節點
@Component
@Component裝飾器僅能裝飾struct關鍵字聲明的數據結構。struct被@Component裝飾後具備組件化的能力,需要實現build方法描述UI,一個struct只能被一個@Component裝飾,
一個Component代表一個組件。
@Builder
自定義構建函數,相當於@Component裏build()的代碼塊,遵遁build()的函數語法規則,方便複用
遵遁UI語法法則
// 自定義組件內自定義構建函數
@Component
struct Index{
@Builder
ItemBuilder(){
Row(){
Text('builder')
Text('hello')
}
}
build(){
Column(){
this.ItemBuilder()
}
}
}
// 全局自定義構建函數
@Builder
function ItemBuilder(){
Row(){
Text('builder')
Text('hello')
}
}
@Component
struct Index{
build(){
Column(){
ItemBuilder()
}
}
}
自定義組件內自定義構建函數和全局構建函數的區別:
- 在自定義組件內定義一個或多個@Builder方法,該方法被認爲是該組件的私有、特殊類型的成員函數,在自定義函數體中,this指代當前所屬組件,組件的狀態變量可以在自定義構建函數內訪問。建議通過this訪問自定義組件的狀態變量而不是參數傳遞
- 全局的自定義構建函數可以被整個應用獲取,不允許使用this和bind方法
- 如果不涉及組件狀態變化,建議使用全局的自定義構建方法,若是要大量使用組件內的變量,可採用自定義組件內自定義構建函數
參數傳遞:
-
按引用傳值
傳遞的參數可爲狀態變量,且狀態變量的改變會引起UI重新渲染。ArkUI提供$$作爲按引用傳遞參數的範式@Builder function MyBuilder($$: {msg: string, num: string}){ Row(){ Text($$.msg) Text($$.num) } } @Component struct Index{ @State msg:string = 'hello' @State num:number = 1 build(){ Column(){ MyBuilder({msg: this.msg, num: this.num}) } } }
注意點:裏面的參數類型只能是string | Resource
-
按值傳值
普通傳參數即可,若傳的參數爲狀態變量的話,不會引起UI重新渲染
@BuilderParam
???
@Styles
樣式複用,類似於css的類名所創建的css代碼塊
// 全局
@Styles function globalFancy () {
.width(150)
.height(100)
.backgroundColor(Color.Pink)
}
@Entry
@Component
struct FancyUse {
build() {
Column({ space: 10 }) {
// 使用全局的@Styles封裝的樣式
Text('FancyA')
.globalFancy ()
.fontSize(30)
}
}
}
// 組件內
@Entry
@Component
struct FancyUse {
@State heightValue: number = 100
// 定義在組件內的@Styles封裝的樣式
@Styles fancy() {
.width(200)
.height(this.heightValue)
.backgroundColor(Color.Yellow)
.onClick(() => {
this.heightValue = 200
})
}
build() {
Column({ space: 10 }) {
// 使用組件內的@Styles封裝的樣式
Text('FancyB')
.fancy()
.fontSize(30)
}
}
}
注意點:
- 不支持參數傳遞
- 全局無法根據常量和狀態變量去訪問,組件內可以通過this去訪問常量和狀態變量
- 組件內@Styles高於全局@Styles,組件內找不到會找全局
- 全局聲明時需要function,組件內不需要
@Extend
擴展樣式,在@Styles基礎上擴展__原生組件樣式__,意思就是__必須要有原生組件__支撐,比如Column,Row,Text等等,比@Styles更強大
使用規則:
-
僅支持全局
-
@Extend支持封裝指定的組件的私有屬性和私有事件和預定義相同組件的@Extend的方法
// @Extend(Text)可以支持Text的私有屬性fontColor @Extend(Text) function TextExtends () { .fontColor(Color.Red) } // superTextExtends可以調用預定義的TextExtends @Extend(Text) function superTextExtends(size:number) { .fontSize(size) .TextExtends() }
-
@Extend裝飾的方法支持參數/狀態變量,開發者可以在調用時傳遞參數,調用遵循TS方法傳值調用,狀態變量可以引起重新渲染
@Extend(Text) function TextExtend (fontSize?: number,fontWeight?: number) { .fontColor(Color.Red) .fontSize(fontSize) .fontWeight(fontWeight) } @Entry @Component struct FancyUse { @State fontWeight:number = 100 build() { Column(){ Row({ space: 10 }) { Text('Fancy') .TextExtend() Text('Fancy') .TextExtend(24) Text('Fancy') .TextExtend(32,this.fontWeight) } Button('click') .onClick(()=>{ this.fontWeight = 600 }) } } }
-
@Extend裝飾的方法的參數可以爲function,作爲Event事件的句柄
@Extend(Text) function makeMeClick(onClick: () => void) { .backgroundColor(Color.Blue) .onClick(onClick) } @Entry @Component struct FancyUse { @State label: string = 'Hello World'; onClickHandler() { this.label = 'Hello ArkUI'; } build() { Row({ space: 10 }) { Text(`${this.label}`) .makeMeClick(this.onClickHandler.bind(this)) } } }
stateStyles
stateStyles可以依據組件的內部狀態的不同,快速設置不同樣式
css僞類?stateStyles是屬性方法,可以根據UI內部狀態來設置樣式,類似於css僞類,有四種狀態
- focused 獲取焦點
- normal 正常
- pressed 按壓
- disabled 禁止
@Entry
@Component
struct UlImage{
@State clickColor: string = 'red'
@Styles pressedStyle() {
.backgroundColor(Color.Green)
}
@State focusedColor: Color = Color.Red;
build(){
Column(){
Text('111')
Row(){
Button('Click me')
.stateStyles({
focused: {
.backgroundColor(this.focusedColor)
},
pressed: this.pressedStyle,
normal: {
.backgroundColor(Color.Yellow)
}
})
.onClick(() => {
this.focusedColor = Color.Pink
})
}
Button('stateStyles')
.stateStyles({
disabled: {
.backgroundColor(Color.Pink)
}
})
}
}
}
stateStyles裏是對象形式,一直都是focues模式?展現不了normal狀態??
@State
@State裝飾的變量,或稱爲狀態變量,一旦變量擁有了狀態屬性,就和自定義組件的渲染綁定起來。當狀態改變時,UI會發生對應的渲染改
@State聲明的變量是私有屬性,只能從組件內部訪問,聲明時必須指定類型和初始值
能導致ui重新渲染的方式:
-
基本數據類型: string,number,boolean
-
當裝飾的對象是array時,可以觀察到數組本身的賦值和添加、刪除、更新數組的變化,但深一級的觀察不到,看例子
@Entry @Component struct UlImage{ @State arr: Array<{name: string, age: number}> = [{ name: 'peter', age: 18 },{ name: 'peter', age: 18 },{ name: 'peter', age: 18 }] build(){ Column(){ Text('111') ForEach(this.arr, item=>{ Row({space:10}){ Text(item.name) Text(item.age + '') } }) Row(){ Button('Click me') .onClick(() => { this.arr.push({name: 'jerry', age: 22}) // 這個能能觸發渲染 this.arr[1] = { // 這個能能觸發渲染 name: 'tom', age: 20 } this.arr[1].name = 'tom' // 這個觸發不了渲染 }) } } }
-
當裝飾的數據類型爲class或者Object時,可以觀察到自身的賦值的變化,和其屬性賦值的變化,即Object.keys(observedObject)返回的所有屬性,同樣的道理,但深一級的觀察不到,看例子
class Name { public value: string; constructor(value: string) { this.value = value; } } class Model { public value: string; public name: Name; constructor(value: string, a: Name) { this.value = value; this.name = a; } } @Entry @Component struct UlImage{ @State title: Model = new Model('Hello', new Name('World')); build(){ Column(){ Text(this.title.value) Text(this.title.name.value) Row(){ Button('Click me') .onClick(() => { // this.arr.push({name: 'jerry', age: 22}) // 能變 // this.title = new Model('Hi', new ClassA('ArkUI')); // 能變 // this.title.value = 'Hi'; // 能變 this.title.name.value = 'ArkUI'; // 不能變 }) } } } } @Component struct OP{ message:string = '00' build(){ Column(){ Text(this.message) } } }
__如何實現深一級的觀察呢??__答案:@Observe/@ObjectLink
@Prop
單向同步,父組件流向子組件,類似於vue的prop,但不同的是@Prop是可操作的,比如修改
@Prop裝飾的變量是可變的,子組件變化不會同步回其父組件,就是@Prop變量允許在子組件修改,但修改後的變化不會同步回父組件,父組件的改變會同步給子組件
注意:@Prop裝飾器不能在@Entry裝飾的自定義組件中使用
@Prop沒有初始值,都是從父組件那裏傳來,父組件可以是常規變量,@State,@Link,@Prop...
支持的傳參類型:string,number,boolean,enum
@Entry
@Component
struct Index1{
msg: string = 'hello world'
@State name:string = 'peter'
@State age: number = 18
isMale: boolean = true
build(){
Column(){
Hello({msg: this.msg,name: this.name,age: this.age, isMale: this.isMale})
Button('click')
.onClick(()=>{
this.msg = 'hello arkTs'
this.name = 'tom'
this.age = 30
this.isMale = false
})
}
}
}
@Component
struct Hello{
@Prop msg: string
@Prop name: string
@Prop age: number
@Prop isMale: boolean
build(){
Column(){
Text(this.msg)
Text(this.name)
Text(this.age + '')
Text(this.isMale + '')
HelloHello({msg: this.msg + 'today'})
}
}
}
@Component
struct HelloHello{
@Prop msg: string
build(){
Column(){
Text(this.msg)
}
}
}
@Link
雙向同步,兩方修改都會同步到另一方
注意:@Prop裝飾器不能在@Entry裝飾的自定義組件中使用
@Link沒有初始值,都是從父組件那裏傳來,父組件可以是@State,@Link,@Prop...,不包含常量,接收方式:@Link子組件從父組件初始化@State的語法爲Comp({ aLink: this.aState })和Comp({aLink: \(aState}),(目前試的情況只有\)aState,這個this.aState會報錯)
支持的傳參類型:Object、class、string、number、boolean、enum,其中Object,class,array的檢測方式和 @State一樣,更深一級就無法檢測到
原理:1. @Link的數據源的更新:子組件@Link包裝類把當前this指針註冊給父組件。父組件@State變量變更後,會遍歷更新所有依賴它的系統組件(elementid)和狀態變量(比如@Link包裝類),通知@Link包裝類更新後,子組件中所有依賴@Link狀態變量的系統組件(elementId)都會被通知更新2. 當子組件中@Link更新:@Link更新後,調用父組件的@State包裝類的set方法,將更新後的數值同步回父組件,子組件@Link和父組件@State分別遍歷依賴的系統組件,進行對應的UI的更新
有個bug?看例子
class Age{
value:number
constructor(value:number) {
this.value = value
}
}
class Person{
name: string
age: Age
constructor(name: string, age: Age) {
this.name = name
this.age = age
}
}
@Entry
@Component
struct LinkIndex{
@State msg:string = 'hello world'
@State person: Person = new Person('peter', new Age(18))
build(){
Column(){
LinkChildren({message: $msg,msg: this.msg, person: $person})
Button('click')
.onClick(()=>{
this.msg = 'hello arkTs'
// this.person.name = 'tom' // 單個的情況下不會渲染age,會渲染name
this.person.age.value = 30 // // !bug!!!!!!!當name和age.value同時更改時,都會重新渲染
})
}
}
}
@Component
struct LinkChildren{
@Link message: string
@Prop msg:string
@Link person: Person
build(){
Column(){
Text('[LinkChildren] ' + this.message)
Text('[LinkChildren] ' + this.msg)
Row({space:10}){
Text(this.person.name)
Text(this.person.age.value + '')
}
LinkChildrenChildren({message: $message, msg: $msg})
Button('LinkChildren click')
.onClick(()=>{
// this.person.age.value = 100 // 單個的情況下不會渲染age,會渲染name
this.person.name = 'jerry' // !bug!!!!!!!當name和age.value同時更改時,都會重新渲染
})
}
}
}
@Component
struct LinkChildrenChildren{
@Link message: string
@Link msg: string
build(){
Column(){
Text('[LinkChildrenChildren] ' + this.message)
Text('[LinkChildrenChildren] ' + this.msg)
}
}
}
@Provide/@Consume
與後代組件的雙向數據同步,類似於@Link的雙向數據同步,只不過省略了嵌套,改成用@Provide去聲明,@Consume去獲取去修改,擁有@Link的特性和參數傳遞的方式
@Provide可以在@Entry聲明,有初始值,@Consume也可以在子組件中聲明
class Age{
value:number
constructor(value:number) {
this.value = value
}
}
class Person{
name: string
age: Age
constructor(name: string, age: Age) {
this.name = name
this.age = age
}
}
@Entry
@Component
struct LinkIndex{
@Provide msg:string = 'hello world'
@Provide person: Person = new Person('peter', new Age(18))
build(){
Column({space:20}){
LinkChildren()
Text(this.msg)
Button('click')
.onClick(()=>{
this.msg = 'hello arkTs~~~~~~'
this.person.name = 'tom'
// this.person.age.value = 30// !bug!!!!!!!當name和age.value同時更改時,都會重新渲染
})
}
}
}
@Component
struct LinkChildren{
@Consume msg:string
build(){
Column({space:20}){
Text('[LinkChildren] ' + this.msg)
LinkChildrenChildren()
Button('LinkChildren click')
.onClick(()=>{
// this.person.age.value = 100
this.msg = 'hello arkTs'
})
}
}
}
@Component
struct LinkChildrenChildren{
@Consume person: Person
build(){
Column({space:20}){
Text('[LinkChildrenChildren] ' + this.person.name)
Text('[LinkChildrenChildren] ' + this.person.age.value)
Button('LinkChildrenChildren click')
.onClick(()=>{
// this.person.age.value = 100
this.person.name = 'jerry'// !bug!!!!!!!當name和age.value同時更改時,都會重新渲染
})
}
}
}
@Observed/@ObjectLink
使用@Observed裝飾class會改變class原始的原型鏈,@Observed和其他類裝飾器裝飾同一個class可能會帶來問題
@ObjectLink裝飾器不能在@Entry裝飾的自定義組件中使用
雙向數據變化同步__? ?__
- 被@Observed裝飾的類,可以被觀察到屬性的變化;
- 子組件中@ObjectLink裝飾器裝飾的狀態變量用於接收@Observed裝飾的類的實例,和父組件中對應的狀態變量建立雙向數據綁定。這個實例可以是數組中的被@Observed裝飾的項,或者是class object中的屬性,這個屬性同樣也需要被@Observed裝飾
注意:
-
@ObjectLink裝飾的變量不能被賦值,如果要使用賦值操作,請使用@Prop
-
@ObjectLink裝飾的數據是可讀的,只能允許數據屬性賦值,而不能對自身賦值
使用方法:將要監聽的屬性單獨用@Component自定義組件,然後將要監聽的屬性用@ObjectLink裝飾,這樣父組件或子組件修改二級屬性都會更改渲染,看例子
// object的二級屬性更改渲染
@Observed class Sex{
value: string;
constructor(sex: string) {
this.value = sex
}
}
@Observed class Person {
name: string;
age: number;
sex?: Sex
constructor(name: string, age: number,sex?: Sex) {
this.name = name;
this.age = age;
this.sex = sex
}
}
@Entry
@Component
struct UIImage{
@State person: Person = {
name: 'peter',
age: 18,
sex: new Sex('male')
}
build(){
Column({space:10}){
Text(this.person.name)
Text(this.person.age + '')
Text(this.person.sex.value)//這個不變,怎麼會是雙向的呢????????
// Text(this.person.sex.value)
Children({sex: this.person.sex})
Button('click')
.onClick(()=>{
// this.person.name = 'tom'
this.person.sex.value = this.person.sex.value === 'male' ? 'female':'male'
})
}
}
}
@Component
struct Children{
@ObjectLink sex: Sex
build(){
Column(){
Text(this.sex.value + '')
}
}
}
// array的二級屬性更改渲染
@Observed
class Item {
name: string;
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age
}
}
@Observed
class ListData extends Array<Item>{}
@Entry
@Component
struct UlImage {
@State arr: ListData = new ListData(new Item('peter', 18),new Item('peter', 18),new Item('peter', 18))
build() {
Column() {
Text('111')
ForEach(this.arr, item => {
It({item})
// Row({ space: 10 }) {
// Text(item.name)
// Text(item.age + '')
// }
})
Row() {
Button('Click me')
.onClick(() => {
// this.arr.push({ name: 'jerry', age: 22 }) // 這個能能觸發渲染
// this.arr[1] = { // 這個能能觸發渲染
// name: 'tom',
// age: 20
// }
this.arr[1].name = 'tom' // 這個觸發不了渲染
})
}
}
}
}
@Component
struct It{
@ObjectLink item: Item
build(){
Column(){
Row({ space: 10 }) {
Text(this.item.name)
Text(this.item.age + '')
.onClick(()=>{
this.item.age = 100
})
}
}
}
}