🚀一、ArkTS語言基本語法
🔎1.簡介
HarmonyOS的ArkTS語言是一種基於TypeScript開發的語言,它專為HarmonyOS系統開發而設計。ArkTS語言結合了JavaScript的靈活性和TypeScript的嚴謹性,使得開發者能夠快速、高效地開發出高質量的HarmonyOS應用程式。
ArkTS語言具有以下特點:
靜態型別檢查:開發者在編寫程式碼時可以使用型別註解來進行型別檢查,從而減少因型別錯誤而導致的bug。
非同步/同步程式設計:ArkTS語言支援基於Promise和async/await的非同步/同步程式設計方式,能夠更好地處理非同步操作。
內建模組:ArkTS語言內建了許多常用的模組,如檔案系統、網路請求、圖形渲染等,使得開發者不必自己編寫這些模組。
相容性:ArkTS語言使用TypeScript語法,可以與JavaScript程式碼無縫整合,並且可以編譯成JavaScript程式碼來在其他平臺上執行。
ArkTS語言基礎類庫是HarmonyOS系統上為應用開發者提供的常用基礎能力,主要包含能力如下圖所示:
ArkTS是HarmonyOS優選的主力應用開發語言。ArkTS圍繞應用開發在TypeScript(簡稱TS)生態基礎上做了進一步擴充套件,繼承了TS的所有特性,是TS的超集。
ArkTS和HTML的差別:
🔎2.TypeScript的基礎語法
TypeScript是一種由微軟開發的JavaScript超集語言,它支援JavaScript的所有語法,但新增了一些新的特性和語法,使開發更加可靠和高效。TypeScript最大的特點是引入了靜態型別,開發者可以在編譯時發現型別錯誤,提高程式碼的可維護性和可讀性。
TypeScript程式碼可以在編譯時被轉換成JavaScript程式碼,在瀏覽器和Node.js環境下都可以執行。雖然TypeScript相對於JavaScript來說更加複雜,但是它可以幫助開發者更好地組織和管理複雜的專案,特別是在團隊協作中提高程式碼的質量和可維護性。
TypeScript基礎知識包括基本型別、變數宣告、函式、類、介面、泛型等。另外,TypeScript還支援模組化開發,可以使用ES模組規範或者CommonJS規範匯入和匯出模組。在實際專案開發中,TypeScript還可以結合工具鏈如Webpack、Babel進行編譯、打包等操作。
除了上面提到的變數宣告、函式定義、類定義、介面定義和列舉型別外,TypeScript還有一些基礎語法需要掌握:
🦋2.1 型別註解
TypeScript的靜態型別檢查是透過型別註解實現的。在宣告變數或函式時,可以使用冒號加上型別註解,指定變數或函式的型別。例如:
let name: string = "TypeScript";
function add(a: number, b: number): number {
return a + b;
}
🦋2.2 介面
TypeScript的介面是用來描述物件的形狀的。可以定義物件需要包含哪些屬性和方法,以及它們的型別。例如:
interface Person {
name: string;
age: number;
sayHello(): void;
}
let tom: Person = {
name: "Tom",
age: 18,
sayHello: function() {
console.log(`Hello, my name is ${this.name}!`);
}
};
🦋2.3 泛型
TypeScript的泛型可以幫助我們編寫更加靈活、可重用的程式碼。它允許在編寫函式、類或介面時使用引數化型別,從而提高程式碼的通用性和可讀性。例如:
function identity<T>(arg: T): T {
<details>
<summary>點選檢視程式碼</summary>
function identity
return arg;
}
let output = identity
console.log(output); // 輸出 TypeScript
</details>
return arg;
}
let output = identity<string>("TypeScript");
console.log(output); // 輸出 TypeScript
🦋2.4 類的繼承
TypeScript的類可以繼承其他類,從而實現程式碼的重用和擴充套件。透過關鍵字extends可以讓一個類繼承另一個類,並繼承其屬性和方法。例如:
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0) {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Dog extends Animal {
bark() {
console.log("Woof! Woof!");
}
}
let dog = new Dog("Bobby");
dog.move(10); // 輸出 "Bobby moved 10m."
dog.bark(); // 輸出 "Woof! Woof!"
🦋2.5 類的訪問修飾符
TypeScript的類可以透過訪問修飾符來控制類的屬性和方法的訪問許可權。有三個訪問修飾符可以使用:public、private和protected。預設情況下,都是public。
public:公共的,任何外部或內部都可以訪問。
private:私有的,只有類的內部可以訪問,外部無法訪問。
protected:受保護的,只有類的內部和其子類可以訪問,外部無法訪問。
class Person {
protected name: string;
constructor(name: string) {
this.name = name;
}
protected sayHello() {
console.log(`Hello, I'm ${this.name}.`);
}
}
class Student extends Person {
constructor(name: string) {
super(name);
}
public sayHelloToTeacher(teacher: Person) {
console.log(`Hello, ${teacher.name}, I'm ${this.name}.`);
}
}
let tom = new Student("Tom");
let bob = new Person("Bob");
tom.sayHelloToTeacher(bob); // 輸出 "Hello, Bob, I'm Tom."
bob.sayHello(); // 報錯:屬性 'sayHello' 受保護,只能在類 'Person' 及其子類中訪問。
以上只是舉例一些TS的基礎語法,TS內容遠不止這些不懂的可以去學學TS。
🔎3.ArkTS的基本組成
裝飾器:用於裝飾類、結構、方法以及變數,並賦予其特殊的含義。如上述示例中@Entry、@Component和@State都是裝飾器,@Component表示自定義元件,@Entry表示該自定義元件為入口元件,@State表示元件中的狀態變數,狀態變數變化會觸發UI重新整理。
UI描述:以宣告式的方式來描述UI的結構,例如build()方法中的程式碼塊。
自定義元件:可複用的UI單元,可組合其他元件,如上述被@Component裝飾的struct Hello。
系統元件:ArkUI框架中預設內建的基礎和容器元件,可直接被開發者呼叫,比如示例中的Column、Text、Divider、Button。
屬性方法:元件可以透過鏈式呼叫配置多項屬性,如fontSize()、width()、height()、backgroundColor()等。
事件方法:元件可以透過鏈式呼叫設定多個事件的響應邏輯,如跟隨在Button後面的onClick()。
系統元件、屬性方法、事件方法具體使用可參考基於ArkTS的宣告式開發正規化。
除此之外,ArkTS擴充套件了多種語法正規化來使開發更加便捷:
@Builder/@BuilderParam:特殊的封裝UI描述的方法,細粒度的封裝和複用UI描述。
@Extend/@Style:擴充套件內建元件和封裝屬性樣式,更靈活地組合內建元件。
stateStyles:多型樣式,可以依據元件的內部狀態的不同,設定不同樣式。
🔎4.自定義元件
@Component
struct HelloComponent {
@State message: string = 'Hello, World!';
build() {
// HelloComponent自定義元件組合系統元件Row和Text
Row() {
Text(this.message)
.onClick(() => {
// 狀態變數message的改變驅動UI重新整理,UI從'Hello, World!'重新整理為'Hello, ArkUI!'
this.message = 'Hello, ArkUI!';
})
}
}
}
@Entry
@Component
struct ParentComponent {
build() {
Column() {
Text('ArkUI message')
HelloComponent({ message: 'Hello, World!' });
Divider()
HelloComponent({ message: '你好!' });
}
}
}
struct:自定義元件基於struct實現,struct + 自定義元件名 +
{…}的組合構成自定義元件,不能有繼承關係。對於struct的例項化,可以省略new。
build()函式:build()函式用於定義自定義元件的宣告式UI描述,自定義元件必須定義build()函式。
@Entry:@Entry裝飾的自定義元件將作為UI頁面的入口。在單個UI頁面中,最多可以使用@Entry裝飾一個自定義元件。@Entry可以接受一個可選的LocalStorage的引數。
🦋4.1 build()函式規範
1、根節點唯一
@Entry
@Component
struct MyComponent {
build() {
// 根節點唯一且必要,必須為容器元件
Row() {
ChildComponent()
}
}
}
@Component
struct ChildComponent {
build() {
// 根節點唯一且必要,可為非容器元件
Image('test.jpg')
}
}
2、不允許宣告本地變數、列印、作用域
build() {
// 反例:不允許宣告本地變數
let a: number = 1;
// 反例:不允許console.info
console.info('print debug log');
// 反例:不允許本地作用域
{
...
}
}
3、不允許呼叫沒有用@Builder裝飾的方法,允許系統元件的引數是TS方法的返回值。
@Component
struct ParentComponent {
doSomeCalculations() {
}
calcTextValue(): string {
return 'Hello World';
}
@Builder doSomeRender() {
Text(`Hello World`)
}
build() {
Column() {
// 反例:不能呼叫沒有用@Builder裝飾的方法
this.doSomeCalculations();
// 正例:可以呼叫
this.doSomeRender();
// 正例:引數可以為呼叫TS方法的返回值
Text(this.calcTextValue())
}
}
}
4、不允許switch和表示式
build() {
Column() {
// 反例:不允許使用switch語法
switch (expression) {
case 1:
Text('...')
break;
case 2:
Image('...')
break;
default:
Text('...')
break;
}
// 反例:不允許使用表示式
(this.aVar > 10) ? Text('...') : Image('...')
}
}
🔎5.頁面和自定義元件生命週期
頁面生命週期,即被@Entry裝飾的元件生命週期,提供以下生命週期介面:
- onPageShow:頁面每次顯示時觸發。
- onPageHide:頁面每次隱藏時觸發一次。
- onBackPress:當使用者點選返回按鈕時觸發。
元件生命週期,即一般用@Component裝飾的自定義元件的生命週期,提供以下生命週期介面:
- aboutToAppear:元件即將出現時回撥該介面,具體時機為在建立自定義元件的新例項後,在執行其build()函式之前執行。
- aboutToDisappear:在自定義元件即將析構銷燬時執行。
// Index.ets
import router from '@ohos.router';
@Entry
@Component
struct MyComponent {
@State showChild: boolean = true;
// 只有被@Entry裝飾的元件才可以呼叫頁面的生命週期
onPageShow() {
console.info('Index onPageShow');
}
// 只有被@Entry裝飾的元件才可以呼叫頁面的生命週期
onPageHide() {
console.info('Index onPageHide');
}
// 只有被@Entry裝飾的元件才可以呼叫頁面的生命週期
onBackPress() {
console.info('Index onBackPress');
}
// 元件生命週期
aboutToAppear() {
console.info('MyComponent aboutToAppear');
}
// 元件生命週期
aboutToDisappear() {
console.info('MyComponent aboutToDisappear');
}
build() {
Column() {
// this.showChild為true,建立Child子元件,執行Child aboutToAppear
if (this.showChild) {
Child()
}
// this.showChild為false,刪除Child子元件,執行Child aboutToDisappear
Button('create or delete Child').onClick(() => {
this.showChild = false;
})
// push到Page2頁面,執行onPageHide
Button('push to next page')
.onClick(() => {
router.pushUrl({ url: 'pages/Page2' });
})
}
}
}
@Component
struct Child {
@State title: string = 'Hello World';
// 元件生命週期
aboutToDisappear() {
console.info('[lifeCycle] Child aboutToDisappear')
}
// 元件生命週期
aboutToAppear() {
console.info('[lifeCycle] Child aboutToAppear')
}
build() {
Text(this.title).fontSize(50).onClick(() => {
this.title = 'Hello ArkUI';
})
}
}
🔎6.裝飾函式
🦋6.1 @Builder裝飾器
@Builder主要是定義頁面UI
☀️6.1.1 裝飾指向
1、自定義元件內自定義構建函式
@Builder MyBuilderFunction(){ ... }
#使用
this.MyBuilderFunction(){ ... }
2、MyGlobalBuilderFunction()
@Builder function MyGlobalBuilderFunction(){ ... }
#使用
MyGlobalBuilderFunction()
☀️6.1.2 引數傳遞
1、按引用傳遞引數
@Builder function ABuilder($$: { paramA1: string }) {
Row() {
Text(`UseStateVarByReference: ${$$.paramA1} `)
}
}
@Entry
@Component
struct Parent {
@State label: string = 'Hello';
build() {
Column() {
// 在Parent元件中呼叫ABuilder的時候,將this.label引用傳遞給ABuilder
ABuilder({ paramA1: this.label })
Button('Click me').onClick(() => {
// 點選“Click me”後,UI從“Hello”重新整理為“ArkUI”
this.label = 'ArkUI';
})
}
}
}
2、按值傳遞引數
@Builder function ABuilder(paramA1: string) {
Row() {
Text(`UseStateVarByValue: ${paramA1} `)
}
}
@Entry
@Component
struct Parent {
label: string = 'Hello';
build() {
Column() {
ABuilder(this.label)
}
}
}
🦋6.2 @BuilderParam裝飾器
@BuilderParam用來裝飾指向@Builder方法的變數,開發者可在初始化自定義元件時對此屬性進行賦值,為自定義元件增加特定的功能。
☀️6.2.1 裝飾指向
1、本地初始化@BuilderParam
@Builder function GlobalBuilder0() {}
@Component
struct Child {
@Builder doNothingBuilder() {};
@BuilderParam aBuilder0: () => void = this.doNothingBuilder;
@BuilderParam aBuilder1: () => void = GlobalBuilder0;
build(){}
}
2、初始化子元件@BuilderParam
@Component
struct Child {
@BuilderParam aBuilder0: () => void;
build() {
Column() {
this.aBuilder0()
}
}
}
@Entry
@Component
struct Parent {
@Builder componentBuilder() {
Text(`Parent builder `)
}
build() {
Column() {
Child({ aBuilder0: this.componentBuilder })
}
}
}
this都是器其本身,不會存在傳遞。
☀️6.2.2 使用場景
1、引數化傳遞
@Builder function GlobalBuilder1($$ : {label: string }) {
Text($$.label)
.width(400)
.height(50)
.backgroundColor(Color.Blue)
}
@Component
struct Child {
label: string = 'Child'
// 無引數類,指向的componentBuilder也是無引數型別
@BuilderParam aBuilder0: () => void;
// 有引數型別,指向的GlobalBuilder1也是有引數型別的方法
@BuilderParam aBuilder1: ($$ : { label : string}) => void;
build() {
Column() {
this.aBuilder0()
this.aBuilder1({label: 'global Builder label' } )
}
}
}
@Entry
@Component
struct Parent {
label: string = 'Parent'
@Builder componentBuilder() {
Text(`${this.label}`)
}
build() {
Column() {
this.componentBuilder()
Child({ aBuilder0: this.componentBuilder, aBuilder1: GlobalBuilder1 })
}
}
}
2、尾隨閉包
// xxx.ets
@Component
struct CustomContainer {
@Prop header: string;
@BuilderParam closer: () => void
build() {
Column() {
Text(this.header)
.fontSize(30)
this.closer()
}
}
}
@Builder function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
.fontSize(30)
Text(label2)
.fontSize(30)
}
}
@Entry
@Component
struct CustomContainerUser {
@State text: string = 'header';
build() {
Column() {
// 建立CustomContainer,在建立CustomContainer時,透過其後緊跟一個大括號“{}”形成尾隨閉包
// 作為傳遞給子元件CustomContainer @BuilderParam closer: () => void的引數
CustomContainer({ header: this.text }) {
Column() {
specificParam('testA', 'testB')
}.backgroundColor(Color.Yellow)
.onClick(() => {
this.text = 'changeHeader';
})
}
}
}
}
🦋6.3 @Styles裝飾器
@Styles裝飾器主要是定義公共樣式
☀️6.3.1 裝飾指向
1、全域性
// 全域性
@Styles function functionName() { ... }
// 在元件內
@Component
struct FancyUse {
@Styles fancy() {
.height(100)
}
}
2、元件內
@Component
struct FancyUse {
@State heightValue: number = 100
@Styles fancy() {
.height(this.heightValue)
.backgroundColor(Color.Yellow)
.onClick(() => {
this.heightValue = 200
})
}
}
☀️6.3.2 使用場景
// 定義在全域性的@Styles封裝的樣式
@Styles function globalFancy () {
.width(150)
.height(100)
.backgroundColor(Color.Pink)
}
@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('FancyA')
.globalFancy ()
.fontSize(30)
// 使用元件內的@Styles封裝的樣式
Text('FancyB')
.fancy()
.fontSize(30)
}
}
}
🦋6.4 @Extend裝飾器
@Extend用於擴充套件原生元件樣式,作用和@Styles差不多。
☀️6.4.1 裝飾指向
@Extend僅支援定義在全域性,不支援在元件內部定義
1、@Extend支援封裝指定的元件的私有屬性和私有事件
// @Extend(Text)可以支援Text的私有屬性fontColor
@Extend(Text) function fancy () {
.fontColor(Color.Red)
}
// superFancyText可以呼叫預定義的fancy
@Extend(Text) function superFancyText(size:number) {
.fontSize(size)
.fancy()
}
2、@Extend裝飾的方法支援引數
// xxx.ets
@Extend(Text) function fancy (fontSize: number) {
.fontColor(Color.Red)
.fontSize(fontSize)
}
@Entry
@Component
struct FancyUse {
build() {
Row({ space: 10 }) {
Text('Fancy')
.fancy(16)
Text('Fancy')
.fancy(24)
}
}
}
3、@Extend裝飾的方法的引數可以為function
@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))
}
}
}
4、@Extend的引數可以為狀態變數
@Extend(Text) function fancy (fontSize: number) {
.fontColor(Color.Red)
.fontSize(fontSize)
}
@Entry
@Component
struct FancyUse {
@State fontSizeValue: number = 20
build() {
Row({ space: 10 }) {
Text('Fancy')
.fancy(this.fontSizeValue)
.onClick(() => {
this.fontSizeValue = 30
})
}
}
}
☀️6.4.2 使用場景
@Extend(Text) function fancyText(weightValue: number, color: Color) {
.fontStyle(FontStyle.Italic)
.fontWeight(weightValue)
.backgroundColor(color)
}
@Entry
@Component
struct FancyUse {
@State label: string = 'Hello World'
build() {
Row({ space: 10 }) {
Text(`${this.label}`)
.fancyText(100, Color.Blue)
Text(`${this.label}`)
.fancyText(200, Color.Pink)
Text(`${this.label}`)
.fancyText(300, Color.Orange)
}.margin('20%')
}
}
🔎7.多型樣式
stateStyles是屬性方法,可以根據UI內部狀態來設定樣式,類似於css偽類,但語法不同。ArkUI提供以下四種狀態:
- focused:獲焦態
- normal:正常態
- pressed:按壓態
- disabled:不可用態
🦋7.1 基本使用
@Entry
@Component
struct CompWithInlineStateStyles {
@State focusedColor: Color = Color.Red;
normalColor: Color = Color.Green
build() {
Column() {
Button('clickMe').height(100).width(100)
.stateStyles({
normal: {
.backgroundColor(this.normalColor)
},
focused: {
.backgroundColor(this.focusedColor)
}
})
.onClick(() => {
this.focusedColor = Color.Pink
})
.margin('30%')
}
}
}
🦋7.2 @Styles和stateStyles聯合使用
@Entry
@Component
struct MyComponent {
@Styles normalStyle() {
.backgroundColor(Color.Gray)
}
@Styles pressedStyle() {
.backgroundColor(Color.Red)
}
build() {
Column() {
Text('Text1')
.fontSize(50)
.fontColor(Color.White)
.stateStyles({
normal: this.normalStyle,
pressed: this.pressedStyle,
})
}
}
}
🦋7.3 stateStyles裡使用常規變數和狀態變數
@Entry
@Component
struct CompWithInlineStateStyles {
@State focusedColor: Color = Color.Red;
normalColor: Color = Color.Green
build() {
Button('clickMe').height(100).width(100)
.stateStyles({
normal: {
.backgroundColor(this.normalColor)
},
focused: {
.backgroundColor(this.focusedColor)
}
})
.onClick(() => {
this.focusedColor = Color.Pink
})
.margin('30%')
}
}