一、什么是鸿蒙

鸿蒙即 HarmonyOS ,是华为公司推出的支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备的分布式操作系统,并且它提供了多语言开发的 API,支持 Java、XML、C/C++、JS、CSS、HML(类 html 的鸿蒙自己的标记语言)等开发语言,而且它提供多种响应式布局方案,支持栅格化布局,可以使用同一套代码部署在手机手表平板等多种不同尺寸屏幕的设备上。

二、开发准备

2.1 环境安装

1、登录 HarmonysOS 应用开发门户,点击右上角注册按钮,注册开发者帐号。

2、进入 HUAWEI DevEco Studio 产品页,登录华为开发者账号后下载 DevEco Studio 安装包并进行安装。

3、启动 DevEco Studio,根据工具引导下载 HarmonyOS SDK。
初窥鸿蒙-LMLPHP

4、下载 HarmonyOS SDK 成功后会进入欢迎页,点击欢迎页中的 Configure > Settings 打开设置窗口,点击 Apparance&behavior > System settings > HarmonyOS SDK,选中 JS SDK 进行下载。
初窥鸿蒙-LMLPHP

至此开发环境安装完成。

2.2 新建项目

点击菜单栏中的 File > New > New Project
初窥鸿蒙-LMLPHP
选择需要开发的项目设备,然后选择 Empty Featrue Ability(JS) 后,点击 Next,此时会出现项目的信息配置
初窥鸿蒙-LMLPHP
点击 Finish,一个新的项目就被创建出来了。

2.3 项目目录

初窥鸿蒙-LMLPHP

使用 JS SDK 进行开发的话,需要关注的是 entry > src > main > js 文件夹,其中:

  • i18n 目录下存放的是多语言的 json 文件

  • pages 下存放的是项目的多个页面,每个页面都由 hml、js 和 css 组成

  • app.js 中存放的是全局的 js 逻辑和 app 的生命周期管理

除此之外,还可以自己创建 common 目录用于存放公共资源文件,比如:公共样式和公用方法。

2.4 生命周期

生命周期分为应用的生命周期以及页面的生命周期

初窥鸿蒙-LMLPHP

其中,应用的生命周期主要分为应用创建时调用的 onCreate,以及应用销毁时触发的 onDestroy。

而页面的生命周期分为:

  • onInit:页面数据准备完成时触发;
  • onReady:页面编译完成时触发;
  • onShow:页面展示时触发;
  • onHide:页面被隐藏时触发;
  • onDestroy:页面被销毁时触发;

由于JS UI只支持应用同时运行并展示一个页面,因此当应用从页面 A 跳转到页面 B 时,首先触发页面 A 的 onHide、onDestroy 函数,然后依次调用页面 B 的 onInit、onReady、onShow 函数来初始化和显示页面 B。

三、组件

在 hml 文件中,组件分为容器组件、基础组件、媒体组件、画布组件、栅格组件,由于篇幅有限,这里只列举一下组件名称和对应的描述,感兴趣的同学可以点击 组件文档 进行查阅。

3.1 容器组件

3.2 基础组件

3.3 媒体组件

媒体组件暂时只有 video 组件一个,除了智能穿戴设备不支持外,手机、平板、智慧屏设备均支持该组件,它为设备提供了视频播放功能。

3.4 画布组件

画布组件展示只有 canvas 组件一个,手机、平板、智慧屏、智能穿戴设备均支持该组件,它为设备提供了自定义绘制图形的能力。

3.5 栅格组件

栅格系统有 Margins, Gutters, Columns 三个属性:

  • Margins:用于控制元素距离屏幕最边缘的距离
  • Gutters:用来控制元素和元素之间的距离关系
  • Columns:用来辅助布局的主要定位工具,不同的屏幕尺寸匹配不同的 Columns 数量来辅助布局定位,它会根据实际设备的宽度和 Columns 数量自动计算每一个 Columns 的宽度

初窥鸿蒙-LMLPHP

不同的设备根据水平宽度 px,显示不同数量的栅格数:

xs : 0px < 水平分辨率 < 320px:2 Columns 栅格;

sm : 320px <= 水平分辨率 < 600px:4 Columns 栅格;

md : 600px <= 水平分辨率 < 840px:8 Columns 栅格;

lg : 840px <= 水平分辨率:12 Columns 栅格。

四、HML语法

HML(HarmonyOS Markup Language)是一套类 HTML 的标记语言,通过组件,事件构建出页面的内容。页面具备事件绑定、数据绑定、列表渲染、条件渲染和逻辑控制等能力。

4.1 事件绑定

hml 中事件绑定默认返回一个事件对象参数,可以通过该参数获取事件信息,同时也可以传递额外参数。

<!-- xxx.hml -->
<div>
  <!-- 正常格式 -->
  <div onclick="clickfunc"></div>
  <!-- 缩写 -->
  <div @click="clickfunc('hello')"></div>
  <!-- 使用事件冒泡模式绑定事件回调函数 -->
  <div on:touchstart.bubble="touchstartfunc"></div>
  <!-- 使用事件捕获模式绑定事件回调函数 -->
  <div on:touchstart.capture="touchstartfunc"></div>
  <!-- on:{event}等价于on:{event}.bubble -->
  <div on:touchstart="touchstartfunc"></div>
  <!-- 绑定事件回调函数,但阻止事件向上传递 -->
  <div grab:touchstart.bubble="touchstartfunc"></div>
  <!-- 绑定事件回调函数,但阻止事件向下传递 -->
  <div grab:touchstart.capture="touchstartfunc"></div>
  <!-- grab:{event}等价于grab:{event}.bubble -->
  <div grab:touchstart="touchstartfunc"></div>
</div>
// xxx.js
export default {
  data: {
    text: '',
  },
  clickfunc: function(str, e) {
    console.log(e);
    this.text = str;
  },
  touchstartfunc: function(e) {
    console.log(e);
  }
}

4.2 数据绑定

数据绑定的形式分两种:数据初始化,数据更新
初窥鸿蒙-LMLPHP

4.2.1 数据初始化

hml 中的数据都来自于对应 js 中的 data 对象,因此在初始化页面时,在 data 对象中写入数据,hml 中就可以通过 {{}} 的形式绑定数据。

// xxx.js
export default {
  data: {
    text: 'HELLO WORLD'
  }
}
<!-- xxx.hml -->
<div>{{text}}</div>

4.2.2 数据更新

通过为页面元素绑定事件,可以调用方法更新数据,从而触发视图更新数据

// xxx.js
export default {
  data: {
    text: 'HELLO WORLD'
  },
  changeText: function() {
    this.$set('text', '你好,世界');
  }
}
<!-- xxx.hml -->
<div @click='changeText'>{{text}}</div>

4.3 列表渲染

hml 中需要进行列表渲染的话只需在组件上添加 for 属性并绑定需要渲染的数据,同时可自定义变量和索引的名称:

// xxx.js
export default {
  data: {
    array: [
      {id: 1, name: '老周', age: 28},
      {id: 2, name: '老李', age: 29},
    ],
  },
  changeText: function(val, index) {
    if (val === "老李"){
      this.array.splice(index, 1, {id:2, name: '老王', age: 30});
    } else {
      this.array.splice(index, 1, {id:3, name: '老郑', age: 31});
    }
  },
}
<!-- xxx.hml -->
<div class="array-container">
  <!-- div列表渲染 -->
  <!-- 默认$item代表数组中的元素, $idx代表数组中的元素索引 -->
  <div for="{{array}}" tid="id" onclick="changeText($item.name, $idx)">
    <text>{{$idx}}.{{$item.name}}</text>
  </div>
  <!-- 自定义元素变量名称 -->
  <div for="{{value in array}}" tid="id" onclick="changeText(value.name, $idx)">
    <text>{{$idx}}.{{value.name}}</text>
  </div>
  <!-- 自定义元素变量、索引名称 -->
  <div for="{{(index, value) in array}}" tid="id" onclick="changeText(value.name, index)">
    <text>{{index}}.{{value.name}}</text>
  </div>
</div>

4.4 条件渲染

hml 中实现条件渲染有两种方式,分别是为组件添加 if/elif/else 或 show 属性,它们的区别在于 if/elif/else 属性不符合条件判断则不会在 vdom 中构建,而 show 属性为 false 时虽然不会渲染,但是会在 vdom 中构建,只是设置了 display 样式为 none。

因此出于性能因素考虑,显示隐藏状态需要频繁切换推荐使用 show,显示状态改变次数较少则使用 if/elif/else。

// xxx.js
export default {
  data: {
    show: false,
    display: true,
    visible: false
  },
  toggle: function() {
    this.visible = !this.visible;
  }
}
<!-- xxx.hml -->
<div class="container">
  <text if="{{show}}"> 你好,世界 </text>
  <text elif="{{display}}"> hi </text>
  <text else> Hello World </text>

  <button class="btn" type="capsule" value="toggle" onclick="toggle"></button>
  <text show="{{visible}}" > Hello World! </text>
</div>

4.5 逻辑控制块

hml 中提供了

4.6 自定义组件

HML 可以通过 element 标签引用模板文件,通过它可以实现自定义组件。

<!-- template.hml -->
<div class="item">
  <text>Name: {{name}}</text>
  <text>Age: {{age}}</text>
</div>
<!-- index.hml -->
<element name='man' src='../../common/template.hml'></element>
<div>
  <man name="老朱" age="28"></man>
</div>

其中 element 标签的 name 属性则为自定义组件的名称,src 属性为自定义组件相对该文件的路径,可以为自定义组件标签添加属性向其传递数据,自定义组件内也可使用 $emit 方法向父组件传递参数。

五、JS语法

鸿蒙中的 js 文件支持 ES6 语法。

5.1 引用

鸿蒙中可以使用 import 方法引入功能模块或 js 代码:

import router from '@system.router'
import utils from '../../common/utils.js'

5.2 获取app对象

在页面中可以使用 this.$app.$def 获取在 app.js 中暴露的对象。

// app.js
export default {
  onCreate() {
    console.info('App onCreate');
  },
  onDestroy() {
    console.info('App onDestroy');
  },
  globalData: {
    appData: 'appData',
    appVersion: '2.0',
  },
  changeAppVer () {
    this.globalData.appVersion = '3.0';
  }
};
// index.js
export default {
  data: {
    appData: 'localData',
    appVersion:'1.0',
  },
  onInit() {
    this.appData = this.$app.$def.globalData.appData;
    this.appVersion = this.$app.$def.globalData.appVersion;
  },
  pageMethod() {
    this.$app.$def.changeAppVer();
  }
}

5.3 页面对象

5.4 方法

5.4.1 数据方法

export default {
  data: {
    appInfo: {
      OS: 'HarmonyOS',
      Version: '2.0',
    },
  },
  changeAppInfo() {
    this.$set('appInfo.Version', '3.0');
    console.log(this.appInfo);
    this.$delete('appInfo');
    console.log(this.appInfo);
  }
}

5.4.2 事件方法

鸿蒙中可以使用 $watch 方法观察 data 中的属性变化,如果属性值改变,则会触发绑定的事件。

export default {
  props: ['title'],
  onInit() {
    this.$watch('title', 'onPropChange');
  },
  onPropChange(newV, oldV) {
    console.info('title属性由'+ oldV +'变化为' + newV);
  },
}

5.5 路由

{
  "pages": [
    "pages/index/index",
    "pages/detail/index"
  ]
}

鸿蒙 app 中页面的路由信息保存在 src > main > config.json 文件中的 pages 内,引入 @system.router 后,调用其 push 方法传入需要跳转页面的 uri,即可完成跳转,也可使用其 back 方法回到首页。

import router from '@system.router';
export default {
  launch() {
    router.push ({
      uri: 'pages/detail/index',
    });
  },
  goBack() {
    router.back();
  }
}

六、CSS语法

CSS 是描述 HML 页面结构的样式语言,所有组件均存在系统默认样式,也可在页面 CSS 样式文件中对组件、页面自定义不同的样式。

6.1 尺寸单位

鸿蒙中尺寸单位有两种,px(逻辑像素) 以及百分比。

{
  "window": {
    "designWidth": 720,
    "autoDesignWidth": false
  }
}

逻辑像素的配置在 src > main > config.json 文件中的 window 内,designWidth 为屏幕的逻辑宽度,默认为720px,实际显示时会将页面布局缩放至屏幕实际宽度,如100px在实际宽度为1440物理像素的屏幕上,实际渲染为200物理像素。

当 autoDesignWidth 设置为 true 时,逻辑像素 px 将按照屏幕密度进行缩放,如 100px 在屏幕密度为3的设备上,实际渲染为300物理像素。

而百分比单位表示该组件占父组件尺寸的百分比,如组件的 width 设置为50%,代表其宽度为父组件的50%。

6.2 样式导入

CSS 样式文件支持 @import 语句,导入 CSS 文件。

@import '../../common/style.css';

七、总结

使用鸿蒙的 JS SDK 开发 App,整体的项目结构、生命周期以及开发流程很像微信的小程序,而 hml 和 JS 的语法又很像 Vue,整个流程走下来,感觉对 web 开发者而言还是很友好的,相信有 Web 前端开发基础的小伙伴们都可以快速的上手。

由于篇幅有限,文中还有很多没有提到的鸿蒙赋予开发者的硬件调用能力,希望鸿蒙可以越做越好,让越来越多的开发者和用户加入到鸿蒙的大生态中来。

八、参考


欢迎关注凹凸实验室博客:aotu.io

或者关注凹凸实验室公众号(AOTULabs),不定时推送文章:

初窥鸿蒙-LMLPHP

06-11 01:40