初识 Harmony OS 第一课

1,348 阅读3分钟

前言

(本文章非0基础保姆级教程)

最近的 HarmonyOS 可谓是火的不得了,网上各种说法铺天盖地,相信之后不管是 Vue、React以及 Uniapp 等技术框架也会支持鸿蒙的,或者又有什么新的情况会出现,不管怎么样,这么火的我也跟一波风来学习一下,主要还是因为我主攻前端,所以学习起来不费力嘛 😉

开发软件使用的是 DevEco Studio,具体安装教程和环境配置我就不过多的赘述了,网上也是一抓一大把,看个视频也行,咱们自己需要配置的环境也就是个node、npm以及SDK。直接照着安装步骤来就可以了。

image.png

上面的图片是环境配置好之后我们点击创建项目弹出的项目模板页面,我们可以在这里选择一个模板进行开发,我就选择第一个空模板吧。

然后我们可以就可以获得一个最简单的模板了,下面的图片就是我们的代码以及运行出来的效果,他给生成出来的代码也是比较容易理解的,我在代码上方也做了注释

image.png

我们还可以下载一个 SDK 来真机模拟,在工具栏里面可以找到该页面,下载对应机型即可

image.png

image.png

image.png

我们最好使用 ArtTS 语言来进行开发,是 Harmony OS 主力开发语言,它是基于 TypeScript 的拓展,匹配了 ArtUI 框架、拓展了声明式 UI 、状态管理等很多功能,而 typescript 又是 JavaScript 的超集,这对于我们前端开发人员尤其的好


TypeScript

对于 JavaScript 我就不赘诉了,这需要很长的文章,我们基于 JavaScript 来简单入门学习一下 typescript(这需要你有一些 JavaScript 的基础,当然无需很多)

ts 基础数据类型

// typescript 中的所有数字类型都是浮点数,类型为 number
let num:number = 123;

// 布尔值
let bool:boolean = true;

// 字符串
let str:string = "Hello ts";

// 数组
let arr:number[] = [1,2,3]; // 声明数组
let arr02:Array<number> = [1,2,3]; // 数组泛型声明

// 元组
let an:[number,boolean,string] = [2,true,"Hello World"];

// 枚举
enum BackGroundColor{red,green};
const color:BackGroundColor = BackGroundColor.red;

/**
 * @ unknown
 * 在编程阶段不清楚或者说无法确定该变量的数据类型的话,就可以使用 unknown 来声明或者说标记
 */
let notSure:unknown = "Hello";
notSure = 123;

// void 当一个函数没有返回值的时候,它的类型要为 viod
const test = ():void=> { console.log("Hello") }

// undefined
let un:undefined = undefined;

// null
let nu:null = null;

// 联合数据类型(表示取值可以为多种数据类型中的任意一种)
let testData:string | number | Array<number>;

函数

// 命名函数
function testFun(a:number,b:number):number {
    return a * b;
};

// 匿名函数
let testFun02 = function(a:number,b:number):number {
    return a * b;
};

// 可选参数
let testFun03 = function(a:number,b?:number):number {
    return a;
};

// 剩余参数
let testFun04 = function(a:number,b?:number,...arr:string[]):number {
    return a;
};

// 类
class TestClass {
    private name:string;

    constructor(name:string) {
        this.name = name;
    }

    public TestPublicFunc():string {
        return `My Name is${this.name}`;
    }
}

// 继承

class SonClass extends TestClass {
    private age:number;

    constructor(name:string,age:number) {
        super(name);
        this.age = age;
    }

    public ConsoleFunc():void {
        console.log(`${this.TestPublicFunc()},My Age is ${this.age}`);
    }
}

let testVal = new SonClass("Toamto",23);
testVal.ConsoleFunc();  // My Name isToamto,My Age is 23

ArkTs

终于到了这一刻了,对于 typescript 我真的不想再多余介绍了,深入了解可以看他们的官方文档。ArkTs!启动! 配一张 ArkTs 的架构图

image.png

声明式UI指的就是开发时我们只需要关注描述UI呈现的结果,不需要关注内部如何实现,再者就是我们所熟悉的数据驱动视图,双向数据绑定什么的

关于 ArkTs 我们也可以直接查看他的官方文档:

ArkTs

// 装饰器:装饰类、结构以及方法和变量,赋予其特殊含义
@Component

// 结构体
struct TestComponent {
  // 使用 @State 标注的变量当他的值发生改变的时候会让组件刷新
  @State isChange:boolean = false;
  // bulid 是UI描述,它以声明式的方式来描述 UI 结构
  build() {
    // Row为水平方向布局容器,这是系统提供的基础组件或者说容器,例如 Text等都是
    Row() {

    }
  }
}

一个页面中必然会有若干个组件,而这些子组件应用到父组件则需要使用 import 以及 export,我们可以看到下面的代码,在 struct 前有一个 export 导出关键词,那么这个组件就可以被外部进行访问并且使用,接着我们即可在父组件页面中导入该组件并使用

// 子组件
@Component
export struct ListComponent {
  @State isChange:boolean = false;
  build() {
    Row() {
      Text("Hello").fontSize(24)
    }
  }
}
// 父组件
import { ListComponent } from './test01';
@Entry
@Component
struct Index {
  @State message: string = 'Hello World'
  build() {
    // 行
    Row() {
      // 列
      Column() {
        ListComponent()
        Text(this.message)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

效果如下

image.png

自定义组件生命周期

生命周期是相当重要的一环,我们可以在组件的生命周期中做很多事情

image.png

  • aboutToAppear():它将会在 build 函数执行之前来执行,我们可以在这里进行数据初始化等操作
  • aboutToDisappear():他将会在组件实例被销毁的时候进行触发
  • onPageShow() 和 onPageHide():这两个就不用多说了,页面显示和隐藏时会触发
  • 当然还有一个有趣的 API onBackPress():假设用户处于当前页面,当用户操作返回操作的时候,就会触发该函数,而该函数需要一个返回值,如果返回为 True 那么页面自己处理返回逻辑,不进行返回页面的操作,如果返回值为 Flase,意思就是由系统来处理返回逻辑【默认为 Flase】

当然不仅仅是这么几个生命周期,从9版本开始,又添加了五个生命周期为我们所用

分别为:

  1. onLayout:框架会在自定义组件布局时,将该自定义组件的子节点信息和自身的尺寸范围通过onLayout传递给该自定义组件。不允许在onLayout函数中改变状态变量。
  2. onMeasure:框架会在自定义组件确定尺寸时,将该自定义组件的子节点信息和自身的尺寸范围通过onMeasure传递给该自定义组件。不允许在onMeasure函数中改变状态变量。
  3. LayoutChild:子组件布局信息。
  4. LayoutBorderInfo:子组件border信息。
  5. LayoutInfo:子组件layout信息。

以上我们介绍的是自定义组件的生命周期,而页面以及应用的生命周期则可以查看这张图(从官网文档中可查阅到)

image.png

image.png

双向数据绑定

对于双向数据绑定大家应该都不会陌生,在开发中,我们可以使用 @State 来声明一个变量,这个变量的改变将会使该变量身处的组件进行重新渲染,从而达到数据驱动页面这样一个效果,但是如果是两个组件之间,怎么进行这种操作呢?

在 ArkTs 中,我们可以在父组件中使用 @State 声明一个变量。这还是老套路,接着我们需要在子组件中使用 @Link 再次声明一个变量,这样的话我们只需要在这两个变量之间建立一些联系,即可达到双向数据绑定的效果了。现在我们遇到的问题是怎么样给他俩建立一个联系呢?

我们在子组件中使用 @Link 声明的变量并没有给其初始化,这样的话,我们只需要在父组件中使用子组件的那一刻将父组件中的动态变量给到子组件的Link变量,这样就可以实现不同组件之间的双向数据绑定了

而父组件传给子组件的“引用值”则需要使用 $ 符号进行引用的创建

我们来看代码演示:

  • 子组件:
@Component
export struct testComponent {
  @Link isChange:boolean;
  build() {
    Row() {
      Column() {
        Text("Hello ArkTs").fontSize(24)
        Button("点击改变文字").onClick((event: ClickEvent) => {
          this.isChange = !this.isChange;
        })
      }
    }
  }
}
  • 父组件
import { testComponent } from './testComp';
@Entry
@Component
struct Index {
  @State message:string = 'Hello ArkTs'
  @State val:boolean = false
  build() {
    // 行
    Row() {
      // 列
      Column() {
        testComponent({isChange:$val})
        Text(this.val ? this.message : 'Hello World')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

image.png

除了 @State@Link还有两个比较常用且基础的装饰器

  1. @Prop:它与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但更改不会通知给父组件,即@Prop属于单向数据绑定
  2. @Builder:装饰的方法用于定义组件的声明式UI描述,在一个自定义组件内快速生成多个布局内容。

todoList案例

父组件

import { todoListItemComponent } from './ToDoListItem';
@Entry
@Component
struct Index {
  @State list:Array<object> = [
    {
      id:0,
      title:'测试'
    },
    {
      id:1,
      title:'测试2'
    }
  ]
  build() {
    Row() {
      Column() {
        Text("ToDoList")
          .fontSize(32)
          .fontWeight(FontWeight.Bold)
          .fontColor('#404040')
          .margin(24)
        ForEach(this.list,(item:string)=> {
          todoListItemComponent({title: item["title"]})
        },(item:string)=> item["id"])
      }.height('100%')
    }.width('100%')
    .padding(32)
    .backgroundColor('#72efefef')
  }
}

子组件

@Component
export struct todoListItemComponent {
  private title: string;
  @State isComplete: boolean = false;

  @Builder labelIcon(url) {
    Image(url)
      .width(52)
      .height(52)
      .margin(32)
  }

  build() {
    Row() {
      if (this.isComplete) {
        this.labelIcon($r("app.media.todolist"))
      } else {
        this.labelIcon($r("app.media.todolistno"))
      }
        Text(this.title)
          .fontSize(32)
          .fontWeight(FontWeight.Bold)
          .fontColor('#404040')
          .opacity(this.isComplete ? 0.4 : 1)
          .decoration({type: this.isComplete ? TextDecorationType.LineThrough :TextDecorationType.None})
    }.backgroundColor('#ffffff')
    .width('100%')
    .margin(12)
    .borderRadius(32)
    .onClick(()=> {
      this.isComplete = !this.isComplete;
    })
  }
}

image.png

当然,这个 todoList 并不能够使用,只是做了点击标记这一个交互功能

但是初识 ArkTs 我想已经够了 🤓,后续我会继续书写 harmonyOS 相关文章,欢迎大家学习或指正,我们一起讨论