关于 Harmony OS4 入门的一点心得

170 阅读7分钟

1+8+N是HarmonyOS的打造全场景战略

其中,1是智能手机,8是指大屏、音箱、眼镜、手表、车机、耳机、平板、PC等等。围绕着关键的八大生态,周边还有合作伙伴开发的N个其他生态,指的是移动办公、智能家居、运动健康、影音娱乐及智能出行各大板块的延伸业务。

一、设计

二、开发

2.1 什么是 ArkTS

  • 结论:归根结底还是javascript
  • 约等:React + TypeScript + Antd
2.1.1 基本语法

TS语法,前端可无缝接入

2.1.2 官方组件

基础组件(Button、Image...)、布局组件(Flex、Row...)、媒体组件(Video)、绘制组件(化各种图形的,圆、长方形、三角形...)、画布组件(Canvas)

同样提供一些指令式组件,主要是各种用途的弹窗、选择器

前端也可以无缝接入,只需要了解每个组件对应的属性方法和事件方法就行(阅读文档)

ForEach组件比较特殊循环控制渲染的(不需要return)

2.1.3 自定义组件

提供两种重用UI的方式

  • 基本自定义组件 export导出 import引入

  • Builder装饰器:自定义构建函数,

一种更轻量的UI元素复用机制@Builder,@Builder所装饰的函数遵循build()函数语法规则,开发者可以将重复使用的UI元素抽象成一个方法,在build方法里调用。

2.1.4 生命周期

页面生命周期,即被@Entry装饰的组件生命周期,提供以下生命周期接口:

  • onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效。

  • onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入前后台等场景,仅@Entry装饰的自定义组件生效。

  • onBackPress:当用户点击返回按钮时触发,仅@Entry装饰的自定义组件生效。

组件生命周期,即一般用@Component装饰的自定义组件的生命周期,提供以下生命周期接口:

  • aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。

  • aboutToDisappear:在自定义组件析构销毁之前执行。不允许在aboutToDisappear函数中改变状态变量,特别是@Link变量的修改可能会导致应用程序行为不稳定。

2.1.5 渲染控制

条件渲染

  if (this.count > 0) {
     Text(`count is positive`)
       .fontColor(Color.Green)
  }

循环渲染

ForEach(
  arr: Array,
  itemGenerator: (item: any, index?: number) => void,
  keyGenerator?: (item: any, index?: number): string => string
)

数据懒加载

LazyForEach(
    dataSource: IDataSource,             // 需要进行数据迭代的数据源
    itemGenerator: (item: any, index?: number) => void,  // 子组件生成函数
    keyGenerator?: (item: any, index?: number) => string // 键值生成函数
): void
2.1.6 组件状态管理
  • 状态变量:被状态装饰器装饰的变量,状态变量值的改变会引起UI的渲染更新。示例:@State num: number = 1,其中,@State是状态装饰器,num是状态变量。
  • 常规变量:没有被状态装饰器装饰的变量,通常应用于辅助计算。它的改变永远不会引起UI的刷新。以下示例中increaseBy变量为常规变量。
  • 数据源/同步源:状态变量的原始来源,可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。以下示例中数据源为count: 1。
  • 命名参数机制:父组件通过指定参数传递给子组件的状态变量,为父子传递同步参数的主要手段。示例:CompA: ({ aProp: this.aProp })。
  • 从父组件初始化:父组件使用命名参数机制,将指定参数传递给子组件。子组件初始化的默认值在有父组件传值的情况下,会被覆盖。示例
@Component
struct MyComponent {
  @State count: number = 0;
  private increaseBy: number = 1;

  build() {
  }
}

@Component
struct Parent {
  build() {
    Column() {
      // 从父组件初始化,覆盖本地定义的默认值
      MyComponent({ count: 1, increaseBy: 2 })
    }
  }
}
2.1.7 组件通信
1、@Prop装饰器:父子单向同步
  • 当装饰的类型是允许的类型,即string、number、boolean、enum类型都可以观察到的赋值变化;

    •   // 简单类型
        @Prop count: number;
        // 赋值的变化可以被观察到
        this.count = 1;
      

对于@State和@Prop的同步场景:

  • 使用父组件中@State变量的值初始化子组件中的@Prop变量。当@State变量变化时,该变量值也会同步更新至@Prop变量。

  • @Prop装饰的变量的修改不会影响其数据源@State装饰变量的值。

  • 除了@State,数据源也可以用@Link或@Prop装饰,对@Prop的同步机制是相同的。

  • 数据源和@Prop变量的类型需要相同。

2、@Link装饰器:父子双向同步
  • 当装饰的数据类型为boolean、string、number类型时,可以同步观察到数值的变化
  • 当装饰的数据类型为class或者Object时,可以观察到赋值和属性赋值的变化,即Object.keys(observedObject)返回的所有属性。
  • 当装饰的对象是array时,可以观察到数组添加、删除、更新数组单元的变化,示例请参考
class GreenButtonState {
  width: number = 0;
  constructor(width: number) {
    this.width = width;
  }
}
@Component
struct GreenButton {
  @Link greenButtonState: GreenButtonState;
  build() {
    Button('Green Button')
      .width(this.greenButtonState.width)
      .height(150.0)
      .backgroundColor('#00ff00')
      .onClick(() => {
        if (this.greenButtonState.width < 700) {
          // 更新class的属性,变化可以被观察到同步回父组件
          this.greenButtonState.width += 125;
        } else {
          // 更新class,变化可以被观察到同步回父组件
          this.greenButtonState = new GreenButtonState(100);
        }
      })
  }
}
@Component
struct YellowButton {
  @Link yellowButtonState: number;
  build() {
    Button('Yellow Button')
      .width(this.yellowButtonState)
      .height(150.0)
      .backgroundColor('#ffff00')
      .onClick(() => {
        // 子组件的简单类型可以同步回父组件
        this.yellowButtonState += 50.0;
      })
  }
}
@Entry
@Component
struct ShufflingContainer {
  @State greenButtonState: GreenButtonState = new GreenButtonState(300);
  @State yellowButtonProp: number = 100;
  build() {
    Column() {
      // 简单类型从父组件@State向子组件@Link数据同步
      Button('Parent View: Set yellowButton')
        .onClick(() => {
          this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 100 : 100;
        })
      // class类型从父组件@State向子组件@Link数据同步
      Button('Parent View: Set GreenButton')
        .onClick(() => {
          this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;
        })
      // class类型初始化@Link
      GreenButton({ greenButtonState: $greenButtonState })
      // 简单类型初始化@Link
      YellowButton({ yellowButtonState: $yellowButtonProp })
    }
  }
}
3、@Provide装饰器和@Consume装饰器:与后代组件双向同步
// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;

// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;
4、@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化
  • 被@Observed装饰的类,可以被观察到属性的变化;

  • 子组件中@ObjectLink装饰器装饰的状态变量用于接收@Observed装饰的类的实例,和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被@Observed装饰的项,或者是class object中的属性,这个属性同样也需要被@Observed装饰。

  • 单独使用@Observed是没有任何作用的,需要搭配@ObjectLink或者@Prop使用。

2.1.8 全局应用数据
  • LocalStorage:页面级(@Entry装饰的组件)UI状态存储,也可以用在UIAbility内,页面间共享状态。

在多个视图中共享,可以在所属UIAbility中创建LocalStorage实例,并调用windowStage.loadContent

// EntryAbility.ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class EntryAbility extends UIAbility {
  storage: LocalStorage = new LocalStorage({
    'PropA': 47
  });

  onWindowStageCreate(windowStage: window.WindowStage) {
    windowStage.loadContent('pages/Index', this.storage);
  }
}

应用获取数据

// 通过GetShared接口获取stage共享的LocalStorage实例
let storage = LocalStorage.GetShared()

@Entry(storage)
@Component
struct CompA {
  // can access LocalStorage instance using 
  // @LocalStorageLink/Prop decorated variables
  @LocalStorageLink('PropA') varA: number = 1;

  build() {
    Column() {
      Text(`${this.varA}`).fontSize(50)
    }
  }
}
  • AppStorage:特殊的单例LocalStorage对象,由UI框架在应用程序启动时创建,为应用程序UI状态属性提供中央存储;

  • PersistentStorage:持久化存储UI状态,在应用退出再次启动后,依然能保存选定的结果,这就需要用到PersistentStorage。通常和AppStorage配合使用,选择AppStorage存储的数据写入磁盘,以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同;

  • Environment:应用程序运行的设备的环境参数比如多语言,暗黑模式等,环境参数会同步到AppStorage中,可以和AppStorage搭配使用。

2.1.9 国际化

根据设备语言读取对应配置

2.1.10 页面路由跳转

@ohos.router

1、main_pages.json配置文件配置静态路由地址,配置文件路径:src/main/resources/base/profile/main_pages.json
{
  "src": [
    "pages/Index",
    "pages/Page",
    "pages/Twopage"
  ]
}
2、使用router.pushUrl方法进行应用内页面路由跳转以及传值
import router from '@ohos.router';
let msg:String='index页面传递的消息'
@Entry
@Component
struct Index {
  build() {
    Row() {
      Column() {
        Button('跳转')
          .onClick(()=>{
          router.pushUrl({
            url:'pages/Page',
            params:{
              src:msg
            }
          })
        })
      }
      .width('100%').height('100%')
    }
    .height('100%')
  }
}
3、使用router.getParams()方法进接收路由传值
import router from '@ohos.router';

@Entry
@Component
struct Page {
  @State message: string = '子页面2';
  @State src: string=router.getParams()?.['src'];
  build() {
    Row() {
      Column() {
        Text(this.message+this.src)
          .fontSize(50)
          .fontWeight(FontWeight.Bold);
    }
    .height('100%')
  }
}