华为鸿蒙系统应用 OpenHarmony JS 前端开发 基础入门教程-完结
最近程序圈最火的事情,应该就是鸿蒙系统2.0版本的发布,这标志着鸿蒙系统正式进入商用时代。
以前一个前端开发人员,能开发网页、能开发安卓系统和IOS系统,就可以说是一个合格的前端程序员了。但是鸿蒙的出现,也逼着你需要再掌握一门新系统的开发。如果你已经是一名前端,幸运的是不需要作太多的改变,只需要认真学习一下这个教程,就可以顺利的开发鸿蒙系统 。
中国加油,华为加油,前端开发小伙伴加油!
-- 致敬华为鸿蒙全生态底层操作系统的诞生
第一节、鸿蒙系统简介
(1)鸿蒙系统 OpenHarmony 由来
OpenHarmony 中文意思「开放、和谐」,代表了中华民族的包容和谦和,是咱们中国在移动端底层操作系统领域迈出的巨大一步,从此,在全世界我们可以说:“中国,也有自己的移动端底层操作系统了!”
鸿蒙 OpenHarmony 开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展。
(2)OpenHarmony 技术架构
OpenHarmony 整体遵从分层设计,从下向上依次为:内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 组件”逐级展开,在多设备部署场景下,支持根据实际需求裁剪某些非必要的组件,前端领域的发力点核心在于应用层「拓展应用、三方应用」构建服务。
(3)OpenHarmony 应用层介绍
应用层包括「系统应用」和「第三方非系统应用」。应用由一个或多个 FA(Feature Ability)或 PA(Particle Ability)组成。其中,FA 有 UI 界面,提供与用户交互的能力;而 PA 无 UI 界面,提供后台运行任务的能力以及统一的数据访问抽象。基于 FA/PA 开发的应用,能够实现特定的业务功能,支持跨设备调度与分发,为用户提供一致、高效的应用体验。显然,我们前端开发小伙伴学习的核心点就在于对 FA 中 UI 层面的开发工作。
UI 部分(FA)既支持纯 JavaScript 开发,也支持纯 Java 开发,还可以 Java 跟 JavaScript 混合开发。FA 支持使用 Java 和 JavaScript 两种方式开发 UI 界面。如果使用 Java,则跟 Android 一样,使用 xml 定义布局或者 Java 代码定义布局,每个页面都是一个 PageAbility,使用 Java 编写业务代码,不同的页面之间传递数据依然使用 intent。
而这里如果使用 JavaScript UI 框架的话,那么写法跟 「Vue + 小程序」 应用开发基本一模一样,同样支持 data、props、computed、watch 和 functions(methods),也能创建自定义的组件,这样 web 前端工程师就很容易转型成为鸿蒙 UI 开发工程师,不得不说华为在已有概念上做的整合,还是相当厉害的。
HTML 基础
CSS 基础
JavaScript 、ECMAScript(ES6) 基础
Vue 基础
小程序开发基础(事半功倍)
第二节、开发环境搭建和 HelloWorld 体验
(1)OpenHarmony 开发环境 DevEco Studio
DevEco Studio 使用约束
- OpenHarmony 支持使用 JS 语言开发应用。
- OpenHarmony 开发环境 DevEco Studio 支持 Windows 及 mac 系统。
(2)DevEco Studio 2.1 开发环境搭建
JavaScript 项目开发依赖 Node.js 环境,下载链接:
Node.js: https://nodejs.org/en/
- 鸿蒙系统专用 IDE 开发环境 「 DevEco Studio 」,下载链接:
DevEco Studio: https://developer.harmonyos.com/cn/develop/deveco-studio
(3)HelloWorld 应用体验
第一步:启动应用后,提示是否创建启动程序脚本,方便后续模板选择。
第二步:使用华为云 npm 包管理工具。
第三步:创建 or 导入项目。
第四步:选择项目类型及模板。
为了满足应用在多设备上运行的开发需求,DevEco Studio 2.1 Release 在原有单设备工程模板的基础上,新增了 11 个跨设备工程模板。开发者可根据工程向导,依次挑选模板和设备类型,轻松创建跨设备工程,自动生成示例代码和相关资源。
第五步:创建工程项目。
第六步:查阅并关闭开发简易提示。
第七步:项目自动下载对应的依赖及文件,这一步直接点击运行则会有错误提示。
第八步:在 DevEco Studio 菜单栏,点击Tools > SDK Manager 配置对应的 SDK 版本。
第九步:点击右上角 sign in 登陆华为账号,请输入已实名认证的华为帐号的用户名和密码进行登录,在网页中登陆成功后,完成授权。
第十步:进入到 src/main/js/default/pages/index 页面,打开 view -> Tool Windows -> Previewer 进行预览。
第十一步:进入 Tools -> Devices Manager 看到模拟器管理页面,这一步必须要实名认证的华为账号进行登录后,即可选择对应的设备,然后启动设备。
第十二步:启动设备后,再次点击 绿色运行按钮,即可在仿真设备上运行该项目。
PS:在 Views 可以快速设置编辑器主题,以及对应的开发设置。
第三节、应用开发目录结构及文件使用规则介绍
(1)应用开发目录介绍
JS FA 应用的 JS 模块 (entry/src/main/js/module) 的典型开发目录结构如下:
目录结构中文件分类如下:
- .hml 结尾的 HML 模板文件,这个文件用来描述当前页面的文件布局结构。
- .css 结尾的 CSS 样式文件,这个文件用于描述页面样式。
- .js 结尾的 JS 文件,这个文件用于处理页面和用户的交互。
各个文件夹的作用:
- app.js 文件用于全局 JavaScript 逻辑和应用生命周期管理。
- pages 目录用于存放所有组件页面。
- common 目录用于存放公共资源文件,比如:媒体资源和 JS 文件。
- i18n 目录用于配置不同语言场景资源内容,比如:应用文本词条,图片路径等资源,注意 i18n 是开发保留文件夹,不可重命名。
(2)文件使用规则
文件访问规则
应用资源可通过绝对路径或相对路径的方式进行访问,本开发框架中绝对路径以 "/" 开头,相对路径以 "./" 或 "../" ,具体访问规则如下:
引用代码文件,需使用相对路径,比如:../common/utils.js。
引用资源文件,推荐使用绝对路径。比如:/common/xxx.png。
公共代码文件和资源文件推荐放在 common 下,通过以上两条规则进行访问。
CSS 样式文件中通过 url() 函数创建
数据类型,如:url(/common/xxx.png)。 如果代码文件A和文件B位于同一目录,则代码文件B引用资源文件时可使用相对路径,也可使用绝对路径。
如果代码文件A和文件B位于不同目录,则代码文件B引用资源文件时必须使用绝对路径。因为Webpack打包时,代码文件B的目录会发生变化。
- 媒体文件格式
格式 支持版本 支持的文件类型 BMP API Version 3+ .bmp JPEG API Version 3+ .jpg PNG API Version 3+ .png
- app.js 标签中包含了实例名称、页面路由信息。
标签 类型 默认值 必填 描述 name string default 是 标识JS实例的名字。 pages Array - 是 路由信息,详见“pages”。 name、pages 标签配置需在配置文件中的 js 标签中完成设置。pages 定义每个页面的路由信息,每个页面由页面路径和页面名组成,页面的文件名就是页面名。比如:
{ ... "pages": [ "pages/index/index", "pages/detail/detail" ] ...}
说明
- 应用首页固定为 "pages/index/index"。
- 页面文件名不能使用组件名称,比如:text.hml、button.hml 等。
- 每个应用可以在 app.js 自定义应用级生命周期的实现逻辑,包括:
- onCreate:在应用生成时被调用的生命周期函数。
- onDestory:在应用销毁时被调用的生命周期函数。
第四节、基础组件介绍及 Chart 组件使用
(1)鸿蒙系统组件介绍
组件(Component)是构建页面的核心,每个组件通过对数据和方法的简单封装,实现独立的可视、可交互功能单元。组件之间相互独立,随取随用,也可以在需求相同的地方重复使用。
鸿蒙 JS API 提供了完善的组件介绍,详细情况我们去查阅一下官方文档: 组件 - 官方介绍
根据组件的功能,可以分为以下四大类:
组件类型 主要组件 基础组件 text、image、progress、rating、span、marquee、 image-animator
、divider
、search、menu、chart
容器组件 div、list、list-item、stack、swiper、tabs、tab-bar、tab-content、list-item-group、refresh、dialog 媒体组件 video 画布组件 canvas
(2)chart 组件体验使用
鸿蒙系统组件相较于其他前端组件仓库,给咱们专门封装了一个 chart 组件,图表组件,用于呈现线形图、柱状图、量规图界面。
使用 chart 组件进行体验,具体执行步骤如下:
- 创建 pages.chart 文件夹,包含 chart.hml、chart.js、chart.css 三个文件。
- 配置路由,在 config.json 这个文件里面。
- 预览页面时,务必要注意打开当前需要预览的页面文件夹下 hml、css、js 任意一个文件。
- 切换页面进行预览,不需要重新启动预览器,直接点击右上角刷新按钮即可,出现错误提示。
- pages.chart 文件夹下面的 hml、css、js 文件必须命名为 index,否则的话会出现预览错误。
- 出现预览错误修改后,刷新无效,必须重新启动预览器才能正常显示,点击右侧收起再展开即可。
图片路径引用失败不会出现文件查找失败错误提示,建议使用绝对路径进行文件路径编码,官方文档介绍在输出 hap 文件后,真机会因为 webpack 打包解析出现找不到文件的问题,不过模拟器是正常的。
PS:小窍门,绝对路径输入没有路径自动补全提示,可以使用「相对路径」选择到对应的文件,然后,在去掉前面的相对路径引入。
当然,如果在同一个文件夹内部,官方文档介绍使用相对路径不会出现 webpack 打包解析找不到文件的问题,所以,在同一个文件夹内部引用,使用相对路径即可。
ES6 Model 数据文件 import 导入只能使用相对路径,不能使用绝对路径,使用绝对路径会报错。
- 使用模拟器进行模拟的时候,编辑器工具会自动打包输出 hap 文件,模拟器直接加载运行该文件,模拟器不支持热更新,预览器预览会输出编译后文件,支持热更新,也支持多设备同时预览。
- 调整 chart 组件的相关参数,需要注意 xAxis 的 axisTick 参数设置过小,会出现数据丢失情况。
第五节、使用 image-animator 组件构建多图帧动画
(1)image-animator 图片帧动画播放器介绍
image-animator 图片帧动画播放器,多图帧动画的构建能力一直以来都是基于 Android 安卓系统使用 Java or C#语言进行开发的,而华为鸿蒙系统将「图片帧动画播放器」引入到了 JS 前端开发领域,这无疑是一次巨大的突破,这也代表着华为鸿蒙系统对其图片渲染能力的巨大自信。
「属性、事件、样式、方法」组件四要素,官方文档介绍
(2)image-animator 图片帧动画播放器实现
创建一个新的 Ablity ,新起一个抽象能力的应用服务,不同的 Ablity 之间可以通过 PA 进行调用。
Ablity 是应用所具备能力的抽象,也是应用程序的重要组成部分。Ablity 由一个或多个 FA(Feature Ability)或 PA(Particle Ability)组成。其中,FA 有 UI 界面,提供与用户交互的能力;而 PA 无 UI 界面,提供后台运行任务的能力以及统一的数据访问抽象。
- 配置 Ability 对应的基础参数
- 打开 pages.index.index.hml 启动预览器,对页面进行预览,输出基础 "你好 世界" 页面。
- 在阿里图标库内下载 5 张心形图片,5 张图片需要注意对应不同的大小。
https://www.iconfont.cn/search/index?searchType=icon&q=heart
- 在 index.hml 文件中导入对应的页面结构代码
<div class="container"> <image-animator class="animator" ref="animator" images="{{frames}}" duration="1s" /> <div class="btn-box"> <input class="btn" type="button" value="start" @click="handleStart" /> <input class="btn" type="button" value="stop" @click="handleStop" /> <input class="btn" type="button" value="pause" @click="handlePause" /> <input class="btn" type="button" value="resume" @click="handleResume" /> </div> </div>
- 导入 css 样式文件
.container { flex-direction: column; justify-content: center; align-items: center; left: 0px; top: 0px; width: 454px; height: 454px; } .animator { width: 70px; height: 70px; } .btn-box { width: 264px; height: 120px; flex-wrap: wrap; justify-content: space-around; align-items: center; } .btn { border-radius: 8px; width: 120px; margin-top: 8px; }
- 设置图片文件 data model 并 export 出来 common.datas.imgs.js
export default [ { src: "../images/heart50.png", }, { src: "../images/heart60.png", }, { src: "../images/heart70.png", }, { src: "../images/heart80.png", }, { src: "../images/heart90.png", }, { src: "../images/heart100.png" } ]
- 在 index.js 文件中导入图片模块,并写入相应逻辑,需要注意的是使用 $ref 获取到当前动画的节点对象。然后调用其对应的方法。
import imgs from "../../common/datas/imgs.js"
export default {
data: {
frames:imgs
},
handleStart() {
this.$refs.animator.start();
},
handlePause() {
this.$refs.animator.pause();
},
handleResume() {
this.$refs.animator.resume();
},
handleStop() {
this.$refs.animator.stop();
},
};
- 图片无法正常显示,相对路径无法查找到文件位置,是因为 index.js 引入模块后,相对路径发生了变化,修改对应的路径。
export default [
{
src: "/common/images/heart50.png",
},
{
src: "/common/images/heart60.png",
},
{
src: "/common/images/heart70.png",
},
{
src: "/common/images/heart80.png",
},
{
src: "/common/images/heart90.png",
},
{
src: "/common/images/heart100.png"
}
]
- 图片动画并未出现,为什么呢?因为图片的渲染机制是直接更改到对应的大小,所以,我们需要保证心形在图片中占有的位置,使用截图工具截图后,终于出现了心形跳动的结果。
第六节、TodoList 应用构建
(1)页面结构设计注意事项
- 页面结构使用 HTML 相同的标签进行嵌套,最外层是 div 容器。
- 文本内容放在
<text>
标签中才能呈现,否则不会呈现文本内容。- 可以直接调用 鸿蒙 JS 封装好的 组件,这里我们使用的是 switch 组件。
<div class="container"> <text class="title">待办事项</text> <div class="item"> <text class="todo">8点产品需求会议</text> <switch showtext="true" checked="true" texton="完成" textoff="待办" class="switch"></switch> <button class="remove" onclick="remove($idx)">删除</button> </div> <div class="item"> <text class="todo">9点开始开发工作</text> <switch showtext="true" checked="false" texton="完成" textoff="待办" class="switch"></switch> <button class="remove" onclick="remove($idx)">删除</button> </div> <div class="item"> <text class="todo">18点</text> <switch showtext="true" checked="false" texton="完成" textoff="待办" class="switch"></switch> <button class="remove" onclick="remove($idx)">删除</button> </div> <div class="info"> <text class="info-text">您还有</text> <text class="info-num">2</text> <text class="info-text">件事情待办,加油!</text> </div> <div class="add-todo"> <input class="plan-input" type="text"></input> <button class="plan-btn" onclick="addTodo">添加待办</button> </div> </div>
(2)页面样式设计注意事项
- 页面 CSS 支持 id、class、tag 选择器,建议使用 class 选择器。
- 页面样式系统基于 flex 弹性布局进行设置,默认就是 flex 弹性布局,需要注意,弹性布局会自动的拉升和压缩内部元素模块宽度、高度。
- 鸿蒙封装的 JS 组件,有一个专门的样式说明,这个和我们传统的 CSS 写法有很大的差异,这个尤其需要注意。
.container { flex-direction: column; justify-content: flex-start; align-items: center; padding-bottom: 100px; } .title { font-size: 25px; margin-top: 20px; margin-bottom: 20px; color: #000000; opacity: 0.9; font-size: 28px; } .item{ width: 325px; padding: 10px 0; flex-direction: row; align-items: center; justify-content: space-around; border-bottom: 1px solid #eee; } .todo{ color: #000; width: 180px; font-size: 18px; } .switch{ font-size: 12px; texton-color: green; textoff-color:red; text-padding: 5px; width: 100px; height: 24px; allow-scale: false; } .remove { font-size: 12px; margin-left: 10px; width: 50px; height: 22px; color: #fff; background-color: red; } .info{ width: 100%; margin-top: 10px; justify-content: center; } .info-text { font-size: 18px; color: #AD7A1B; } .info-num{ color: orangered; margin-left: 10px; margin-right: 10px; } .add-todo { position: fixed; left: 0; bottom: 0; width: 100%; height: 60px; flex-direction: row; justify-content: space-around; align-items: center; background-color: #ddd; } .plan-input { width: 240px; height: 40px; background-color: #fff; } .plan-btn { width: 90px; height: 35px; font-size: 15px; }
(3)数据渲染与事件绑定
- 第三方 JSON 数据导入,注意使用相对路径
export default [ { info: '给老王打个电话', status: true }, { info: '输出工作计划', status: false }, { info: '和小王对接需求', status: true }, { info: '整理客户资料', status: false }, { info: '和朋友一起聚餐', status: false } ] import todoList from "../../common/datas/todoList.js"
数据绑定
与vue用法类似
hml {{ 变量名 }}
js 变量放在 data 中
export default{ data:{ 变量名: value } }
列表渲染(for)
tid 属性指定数组中每个元素的唯一标识,默认值为 id,用于加速for循环的重渲染。
写法1(都不指定):
<div for="{{array}}" tid='id'></div>
,$idx代表元素索引,$item代表数组元素写法2(指定元素名称):
<div for="{{value in array}}" tid='id'></div>
,$idx 代表元素索引,value 代表数组元素写法3(指定索引和元素名称):
<div for="{{(index,value) in array}}" tid='id'></div>
,index代表元素索引,value代表数组元素。事件绑定
类似 v-on,在 js 中绑定的函数和 data 同级
hml:
<div onclick="clickfunc"></div>
或<div @click="clickfunc"></div>
js: 放在 data 后:
export default{ data:{ }, clickfunc: function(){ ...... } }
- 使用计算属性 computed 和 Vue 中的用法一样,依赖 data 中的数据进行计算,return 返回计算的结果。
import todoList from "../../common/datas/todoList.js" export default { data: { // 待办事件列表 todoList, inputTodo: "IDE无法调用输入" }, computed: { needTodoNum(){ let num = 0; this.todoList.forEach(item => { if(!item.status){ num++; } }); return num; } }, remove(index){ console.log(index) this.todoList.splice(index,1) }, addTodo() { console.log("在这里设置一个新值"); this.todoList.push({ info:'键盘输入', status: false }) }, checkStatus(index){ console.log(index); this.todoList[index].status = !this.todoList[index].status; } }
PS:需要注意的是 Prview 预览模拟器在 MAC 上无法接收键盘输入的内容,但是在真机模拟器上是正常的。我们使用一个日期选择器来模拟输入,在鸿蒙 JS 系统上没有 双向数据绑定,需要我们自行实现双向数据绑定。
<picker type="date" onchange="dateChange">{{date}}</picker>
export default { data: { date:'请选择日期' }, dateChange(e){ console.log(JSON.stringify(e)) this.date = e.year + "年" + e.month + "月" + e.day + "日" } };
第七节、页面样式布局及多终端运行 TodoList
(1)多终端页面样式设计规则
JS UI 框架页面样式,系统基于 flex 弹性布局进行设置,默认就是 flex 弹性布局,需要注意,弹性布局会自动的拉升和压缩内部元素模块宽度、高度。
JS UI框架中手机和智慧屏中的 px 指逻辑像素,px 是根据实际屏幕宽度物理像素进行缩放计算的。例如当width 设为 100px 时,在宽度为1440物理像素的屏幕上,实际显示的宽度为200物理像素。
需要注意的地方有:
- 在 TV 上有一个黑色的背景,需要针对性的调整对应的 CSS 样式,颜色需要重点处理反转。
- 穿戴设备是一个圆形的表盘,需要针对圆形容器特征专门设计对应的样式
媒体查询(Media Query)在移动设备上应用十分广泛,开发者经常需要根据设备的大致类型或者特定的特征和设备参数(例如屏幕分辨率)来修改应用的样式。为此媒体查询提供了如下功能:
针对设备和应用的属性信息,可以设计出相匹配的布局样式。
当屏幕发生动态改变时(比如分屏、横竖屏切换),应用页面布局同步更新。
使用 @media 来引入查询语句,对媒体功能和媒体类型进行判断,具体规则如下:
@media [media-type] [and|not|only] [(media-feature)] { CSS-Code; } // 当设备屏幕是圆形时条件成立 @media screen and (round-screen: true) { … } // 范围查询,CSS level 3 写法 @media (max-height: 800) { … } // 范围查询,CSS level 4 写法,与CSS level3写法等价 @media (height <= 800) { … } // 同时包含媒体类型和多个媒体特征的多条件复杂语句查询 @media screen and (device-type: tv) or (resolution < 2) { … }
- 我们常用类型来进行判断,按照 phone、tv、wearable 顺序输出不同响应式的页面布局
@media screen and (device-type: phone) {} @media screen and (device-type: tv) {} @media screen and (device-type: wearable) {}
(2)ToDoList 页面多端适配实现
@media screen and (device-type: phone) {
.title {
font-size: 28px;
}
}
@media screen and (device-type: tv) {
.container {
background-image: url("../../common/images/Wallpaper.png");
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.title {
font-size: 40px;
color: #FFFFFF;
}
.item{
width: 600px;
padding-top: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #666;
}
.todo{
color: #fff;
width: 400px;
font-size: 21px;
}
.info-text {
font-size: 21px;
}
.add-todo {
justify-content: center;
background-color: #333;
}
.plan-input {
width: 320px;
margin-right: 30px;
}
.plan-btn {
width: 120px;
height: 40px;
background-color: #007cba;
}
}
@media screen and (device-type: wearable) {
.title {
font-size: 16px;
margin-top: 15px;
margin-bottom: 15px;
color: #FFFFFF;
}
.item{
width: 180px;
padding: 5px 0;
border-bottom: 1px solid #333;
}
.todo{
color: #fff;
width: 100px;
font-size: 10px;
}
.switch{
font-size: 8px;
text-padding: 5px;
width: 50px;
height: 24px;
}
.remove {
font-size: 8px;
margin-left: 2px;
width: 30px;
height: 20px;
background-color: #fff;
text-color: red;
}
.info-text {
font-size: 10px;
}
.add-todo {
height: 40px;
padding-top: 5px;
align-items: flex-start;
justify-content: center;
background-color: #333;
}
.plan-input {
width:80px;
height: 20px;
margin-right: 4px;
}
.plan-btn {
width: 40px;
height: 20px;
font-size: 7px;
}
}
第八节、接口功能简介及使用 Storage 实现数据存储
(1)JS UI 框架概述
- JS UI 框架基础能力
声明式编程
JS UI 框架采用类 HTML 和 CSS 声明式编程语言作为页面布局和页面样式的开发语言,页面业务逻辑则支持ECMAScript 规范的 JavaScript 语言。JS UI 框架提供的声明式编程,可以让开发者避免编写 UI 状态切换的代码,视图配置信息更加直观。
跨设备
开发框架架构上支持UI跨设备显示能力,运行时自动映射到不同设备类型,开发者无感知,降低开发者多设备适配成本。
高性能
开发框架包含了许多核心的控件,如列表、图片和各类容器组件等,针对声明式语法进行了渲染流程的优化。
- JS UI 框架整体架构
应用层 Application
应用层表示开发者使用JS UI框架开发的FA应用,这里的FA应用特指JS FA应用。
前端框架层 Framework
前端框架层主要完成前端页面解析,以及提供MVVM(Model-View-ViewModel)开发模式、页面路由机制和自定义组件等能力。
引擎层 Engine
引擎层主要提供动画解析、DOM(Document Object Model)树构建、布局计算、渲染命令构建与绘制、事件管理等能力。
平台适配层 Porting Layer
适配层主要完成对平台层进行抽象,提供抽象接口,可以对接到系统平台。比如:事件对接、渲染管线对接和系统生命周期对接等。
(2)前端框架层 Framework API 服务概述
基本功能
应用上下文、日志打印、页面路由、弹窗、应用配置、定时器
网络访问
上传下载、数据请求
文件数据
数据存储、文件存储
分布式能力
分布式拉起、分布式迁移、分布式API在FA生命周期中的位置
系统能力
通知消息、振动、传感器、地理位置、网络状态、设备信息、屏幕亮度、电量信息、应用管理、媒体查询、国际化
(3)使用 API 数据存储能力实现应用刷新后状态保存
import storage from '@system.storage';
export default {
data: {
// 待办事件列表
todoList
},
onInit() {
storage.get({
key: 'todo',
success: data => {
console.log('获取Storage成功' + data);
//this.todoList = JSON.parse(data)
}
});
},
setStorage() {
storage.set({
key: 'todoList',
value: JSON.stringify(this.todoList)
});
},
remove(index) {
this.todoList.splice(index, 1);
this.setStorage();
},
addTodo() {
this.todoList.push({
info: '键盘输入',
status: false
})
this.setStorage();
},
checkStatus(index) {
this.todoList[index].status = !this.todoList[index].status;
this.setStorage();
}
}
第九节、网络数据请求
(1)fetch API 接口使用简介
- 导入鸿蒙系统 API 接口模块
import fetch from '@system.fetch';
配置网络访问允许的权限列表
ohos.permission.INTERNET
使用
fetch.fetch(OBJECT)
获取网络数据
参数名 类型 必填 说明 url string 是 资源地址。 data string | Object 否 请求的参数,可选类型是字符串或者json对象。 header Object 否 设置请求的header。 method string 否 请求方法默认为GET,可选值为:OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE。 responseType string 否 默认会根据服务器返回header中的Content-Type确定返回类型,支持文本和json格式。详见success返回值。 success Function 否 接口调用成功的回调函数。 fail Function 否 接口调用失败的回调函数。 complete Function 否 接口调用结束的回调函数。
(2)config.json 文件详解
- 应用的每个 HAP 的根目录下都存在一个「config.json」配置文件,config.json 由
app
、deviceConfig
和module
三个部分组成,缺一不可。文件内容主要涵盖以下三个方面:
属性名称 含义 数据类型 是否可缺省 app 表示应用的全局配置信息。同一个应用的不同HAP包的“app”配置必须保持一致。 对象 否 deviceConfig 表示应用在具体设备上的配置信息。 对象 否 module 表示HAP包的配置信息。该标签下的配置只对当前HAP包生效。 对象 否
- 配置文件「config.json」采用 JSON 文件格式,其中包含了一系列配置项,每个配置项由属性和值两部分构成:
属性,属性出现顺序不分先后,且每个属性最多只允许出现一次。
值,每个属性的值为JSON的基本数据类型(数值、字符串、布尔值、数组、对象或者null类型)。
- DevEco Studio 提供了两种编辑 config.json 文件的方式,在 config.json 的编辑窗口中,可在右上角切换代码编辑视图或可视化编辑视图。
(3)使用 fetch API 请求天气数据
准备天气查询地址:
https://api.seniverse.com/v3/weather/now.json?key=WNEUXAAE2G&location=南京&language=zh-Hans&unit=c
配置网络访问允许的权限列表
"module": { "reqPermissions": [ { "name": "ohos.permission.GET_NETWORK_INFO" }, { "name": "ohos.permission.SET_NETWORK_INFO" }, { "name": "ohos.permission.INTERNET" } ], ... }
- 配置设备允许网络配置
"deviceConfig": { "default": { "process": "com.huawei.hiworld.example", "supportBackup": false, "network": { "cleartextTraffic": true, "securityConfig": { "domainSettings": { "cleartextPermitted": true, "domains": [ { "subDomains": true, "name": "api.seniverse.com" } ] } } } } }
- 在 oninit 生命周期中发出请求
//导入鸿蒙的网络请求模块fetch import fetch from '@system.fetch'; export default { data: { winfo:"" }, onInit() { //发起对心知天气服务器的网络请求 fetch.fetch({ url:`https://api.seniverse.com/v3/weather/now.json? key=WNEUXAAE2G&location=南京&language=zh-Hans&unit=c`, responseType:"json", success:(resp)=> { //JSON.parse(字符串)转换成json数据格式 this.winfo=JSON.parse(resp.data); console.log(this.winfo) } }); } }
- 在页面中渲染数据请求结果。
第十节、自定义组件使用
(1)自定义组件创建
JS UI框架支持自定义组件,用户可根据业务需求将已有的组件进行扩展,增加自定义的私有属性和事件,封装成新的组件,方便在工程中多次调用,提高页面布局代码的可读性。
- 定义一个专门存放自定义组件的文件夹 components.tabbar 并设置3个基础文件 tabbar.hml、tabbar.js、tabbar.css,需要注意的是:3个文件的文件名必须保持一致,不然会存在找不到文件的情况。该自定义组件的目的是给页面底部配置一个 tabbar 选项卡体验。
- 设置底部选项卡对应的 json 数据源,用来保存 icon 图片、标题,以及点击选中后的 icon 图片。
// common.datas.tabbarItem.js export default [ { img:'common/images/home.png', simg:'common/images/home_s.png', name:'首页' }, { img:'common/images/hot.png', simg:'common/images/hot_s.png', name:'热点' }, { img:'common/images/us.png', simg:'common/images/us_s.png', name:'社区' }, { img:'common/images/me.png', simg:'common/images/me_s.png', name:'我' } ]
- 使用 toolbar + toolbar-item 内置组件构建底部 tabbar 选项卡服务「tabbar.hml」
<div class="container"> <toolbar class="tabbar"> <toolbar-item for="{{tabbarItems}}" icon='{{$item.img}}' value='{{$item.name}}' onclick="jump($idx)" ></toolbar-item> </toolbar> </div>
- 导入 tabbarItem 数据,并设置对应 data ,同时设置点击事件 动态设置点击对应 icon 选中。
import tabbarItems from '../../common/datas/tabbarItem.js'; export default { data:{ tabbarItems }, jump(index){ this.tabbarItems.forEach((item,index) => { item.img = tabbarItems[index].img; }); this.tabbarItems[index].img = this.tabbarItems[index].simg; } }
- 设置 CSS 样式 将 tabbar 选项卡置底「tabbar.css」
.tabbar { position: fixed; left: 0; bottom: 0; }
(2)自定义组件调用
自定义组件是用户根据业务需求,将已有的组件组合,封装成的新组件,可以在工程中多次调用,提高代码的可读性。
自定义组件通过element引入到宿主页面,使用方法:
<element name='comp' src='../../components/tabbar/tabbar.hml'></element> <div class="container"> <text class="title"> 首页 </text> <comp></comp> </div>
name 属性指自定义组件名称(非必填),组件名称对大小写不敏感,默认使用小写,src 属性指自定义组件hml 文件路径(必填),若没有设置 name 属性,则默认使用 hml 文件名作为组件名。
事件绑定:自定义组件中绑定子组件事件使用 (on|@)child1 语法,子组件中通过 this.$emit('child1', { params: '传递参数' }) 触发事件并进行传值,父组件执行 bindParentVmMethod 方法并接收子组件传递的参数。
第十一节、父子组件通信功能实现
(1)父组件通过 props 向子组件传值
Props 自定义组件可以通过 props 声明属性,父组件通过设置属性向子组件传递参数,camelCase(驼峰命名法)的 prop 名,在外部父组件传递参数时需要使用 kebab-case (短横线分隔命名)形式,即当属性compProp 在父组件引用时需要转换为 comp-prop。
添加默认值,子组件可以通过固定值 default 设置默认值,当父组件没有设置该属性时,将使用其默认值。此情况下 props 属性必须为对象形式,不能用数组形式。
数据单向性,父子组件之间数据的传递是单向的,只能从父组件传递给子组件,子组件不能直接修改父组件传递下来的值,可以将 props 传入的值用 data 接收后作为默认值,再对 data 的值进行修改。
- 子组件的定义
<!-- 务必需要注意的是:子组件的 hml、js、css 三个文件名必须保持一致 --> <div class="ctest"> <text class="title">我是子组件</text> <text>{{ name }}</text> </div>
export default { // props:[ "name" ], props:{ name:{ default: '默认内容' } } }
- 父组件的调用
<element name="ctest" src="../../components/ctest/ctest.hml"></element> <div class="container"> <ctest name="父传子内容"></ctest> </div>
(2)父组件通过 slot 向子组件分发内容
- 普通 slot 插槽分发内容
- 父组件调用
<ctest> <text>默认 slot 分发内容</text> </ctest>
- 子组件接收
<slot></slot>
- 具名插槽分发内容
- 父组件调用
<ctest> <text slot="hasname">具名 slot 分发的内容</text> </ctest>
- 子组件接收
<slot name="hasname"></slot>
(3)子组件通过自定义事件改变父组件状态
- 子组件也可以通过绑定的事件向上传递参数,在自定义事件上添加传递参数。
- 子组件向上传递参数text,父组件接收时通过 e.detail 来获取参数。
- 需要注意的是 父组件中自定义的事件名因为 hml 限制,对大小写不敏感,需要使用 - 进行拼接,但是,在子组件中调用则需要使用「驼峰式」进行 $emit 调用。
- 父组件调用传递自定义事件
<element name="ctest" src="../../components/ctest/ctest.hml"></element> <div class="container"> <!-- 父组件的 title 和 num 可以被子组件修改 --> <text>{{title}}</text> <text>{{num}}</text> <!-- 务必注意事件名对大小写不敏感,需要使用 - 进行拼接--> <ctest name="父传子内容" @change-father-title="changeTitle"></ctest> </div>
export default { data: { title: "我是父组件", num: 0 }, changeTitle(e){ console.log("父组件的方法被子组件模拟触发了"); // 这里需要注意的是:使用 e.detial 来接收传递的参数 this.title = "我被子组件改变了," + e.detail.text; this.num ++; } }
- 子组件模拟触发父组件传递的自定义事件,实现数据传递
<div class="ctest"> <text class="title">我是子组件</text> <button @click="changeFather()"> 点击改变父组件内容 </button> </div>
export default { // props:[ "name" ], props:{ name:{ default: '默认内容' } }, changeFather(){ console.log("子组件的Button被点击了") // 请务必注意,传递的参数必须是一个对象 this.$emit("changeFatherTitle", { text:"传递的参数" }) } }
第十二节、路由功能实现
(1)页面的定义
在 Pages 文件夹下面新建一个文件夹代表需要的路由,当然,我们也可以新建一个 Ablity 体验,这里演示 Pages.Name。
在 新建的文件夹下面务必需要注意新建三个文件
index.hml、index.js、index.css
三个文件,该文件名必须使用 index 来进行命名,使用其他命名会造成文件依赖无法找到。快捷方式,可以选择到对应的 Ability Pages 文件夹下面,然后 new page 直接添加页面,并会自动注册好路由,这是比较方便的
在 config.json 文件中
"js": [ { "pages": [ "pages/index/index" ], "name": "default", "window": { "designWidth": 720, "autoDesignWidth": true } } ]
(2)路由的使用
- 导入路由模块
import router from '@system.router';
- router.push(OBJECT),跳转到应用内的指定页面。
- router.replace(OBJECT),用应用内的某个页面替换当前页面,并销毁被替换的页面。
- router.back(OBJECT),返回上一页面或指定的页面。
// index页面,uri字段是页面路由,由配置文件中的pages列表指定。 router.push({ uri: 'pages/detail/detail', }); // detail页面 router.push({ uri: 'pages/mall/mall', }); // mall页面通过back,将返回detail页面 router.back(); // detail页面通过back,将返回index页面 router.back(); // 通过back,返回到detail页面 router.back({uri:'pages/detail/detail'});
- router.clear(),清空页面栈中的所有历史页面,仅保留当前页面作为栈顶页面。
- router.getLength(),获取当前在页面栈内的页面数量。
- router.getState(),获取当前页面的状态信息。
PS:页面路由需要在页面渲染完成之后才能调用,在onInit和onReady生命周期中页面还处于渲染阶段,禁止调用页面路由方法。