一、前言
鸿蒙元服务虽然与h5在很多地方虽然有相似之处,但还是有部分不同的地方,鸿蒙服务开发模式更接近与vue2版本,很多写法与其相似。该篇文章主要用于帮助有h5基础的伙伴能够快速上手鸿蒙元服务,并且对个人在开发过程中遇到的一些坑做个总结。
二、开发相关
项目目录
前端部分主要看js目录下的文件目录即可,除default目录外,其他文件都是与服务卡片相关的。
commom:存放公共配置文件方法等
components:存放公共组件 i18n:i18n相关
media:存放静态文件,图片等
pages:存放页面的目录,包括js,hml,css
utils:存放工具方法,比如网络请求封装等
app.js:全局文件,能够在这个文件中定义全局变量,拥有应用级的生命周期函数
其他关键目录:
supervisual:低代码相关
config.json:项目配置相关,包括路由等
config.json文件
用于给整个项目进行一些关键配置
定义路由
这种定义路由的方式,可能开发过微信小程序的伙伴会比较熟悉,在微信小程序中,一般第一个路径即是项目打开的页面,可惜在鸿蒙元服务中没有这个便捷的功能,designWidth用于定义页面以多宽的设计图来绘制,autoDesginWidth设为true,即是系统根据手机自动设置。
config.json详细配置请看官方文档: developer.harmonyos.com/cn/docs/doc...
HML
HML是一套类HTML的标记语言,通过组件,事件构建出页面的内容。页面具备数据绑定、事件绑定、列表渲染、条件渲染和逻辑控制等高级能力,由鸿蒙内部实现。
js
<!-- xxx.hml -->
<div class="container">
<text class="title">{{count}}</text>
<div class="box">
<input type="button" class="btn" value="increase" onclick="increase" />
<input type="button" class="btn" value="decrease" @click="decrease" />
<!-- 传递额外参数 -->
<input type="button" class="btn" value="double" @click="multiply(2)" />
<input type="button" class="btn" value="decuple" @click="multiply(10)" />
<input type="button" class="btn" value="square" @click="multiply(count)" />
</div>
</div>
// xxx.js
export default {
data: {
count: 0
},
increase() {
this.count++;
},
decrease() {
this.count--;
},
multiply(multiplier) {
this.count = multiplier * this.count;
}
};
/* xxx.css */
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
left: 0px;
top: 0px;
width: 454px;
height: 454px;
}
.title {
font-size: 30px;
text-align: center;
width: 200px;
height: 100px;
}
.box {
width: 454px;
height: 200px;
justify-content: center;
align-items: center;
flex-wrap: wrap;
}
.btn {
width: 200px;
border-radius: 0;
margin-top: 10px;
margin-left: 10px;
}
看这段代码是不是就觉得很亲近了,在hml中通过"{{}}"的形式绑定数据,用@和on的方法来绑定事件,同时支持冒泡、捕获等方式。
列表渲染for
js
<!-- xxx.hml -->
<div class="array-container" style="flex-direction: column;margin: 200px;">
<!-- div列表渲染 -->
<!-- 默认$item代表数组中的元素, $idx代表数组中的元素索引 -->
<div for="{{array}}" tid="id" onclick="changeText">
<text>{{$idx}}.{{$item.name}}</text>
</div>
<!-- 自定义元素变量名称 -->
<div for="{{value in array}}" tid="id" onclick="changeText">
<text>{{$idx}}.{{value.name}}</text>
</div>
<!-- 自定义元素变量、索引名称 -->
<div for="{{(index, value) in array}}" tid="id" onclick="changeText">
<text>{{index}}.{{value.name}}</text>
</div>
</div>
tid等于vue中的key,id即为array每一项中的唯一属性,需要注意的是,与vue不同,在鸿蒙元服务中,tid是必须的,如果没有tid可能会引起运行异常的情况。
条件渲染if和show
js
<!-- xxx.hml -->
//if
<div class="container">
<button class="btn" type="capsule" value="toggleShow" onclick="toggleShow"></button>
<button class="btn" type="capsule" value="toggleDisplay" onclick="toggleDisplay"></button>
<text if="{{visible}}"> Hello-world1 </text>
<text elif="{{display}}"> Hello-world2 </text>
<text else> Hello-World </text>
</div>
//show
<!-- xxx.hml -->
<div class="container">
<button class="btn" type="capsule" value="toggle" onclick="toggle"></button>
<text show="{{visible}}" > Hello World </text>
</div>
if和show相当于vue中的v-if和v-show,原理也一样。
自定义组件使用(props和emit传值)
js
<!-- template.hml -->
<div class="item">
<text>Name: {{name}}</text>
<text>Age: {{age}}</text>
<text class="text-style" onclick="childClicked" id="text" ref="animator">点击这里查看隐藏文本</text>
</div>
<!-- template.js -->
export default {
props:{
name,
age
contentList
}
childClicked () {
//获取标签对象
//this.$element("text");
//this.$element("text").currentOffset().y 获取属性;
//通过ref的形式来获取
//this.$refs.animator
this.$emit('eventType1',{text:'123'});
},
};
<!-- index.hml -->
//注册
<element name='comp' src='../../common/template.hml'></element>
<div>
//使用
<comp name="Tony" age="18" content-list="contentList" @event-type1="textClicked"></comp>
</div>
<!-- template.js -->
export default {
textClicked (e) {
//e.detail 拿到传过来的数据 e.detail.text
},
};
注意:组件传递props和emit属性时,强制使用横杆连接的变量名进行传递,接收时,需要使用驼峰名进行接收,通过e.detail拿到emit传过来的参数,通过$element()方法或ref的形式来获取元素对象,其他用法基本和vue2相同。
生命周期和插槽等用法参考官方文档developer.harmonyos.com/cn/docs/doc...
通用事件
developer.harmonyos.com/cn/docs/doc...
内部系统组件
常用的组件包括:
容器组件:dialog、div、滚动组件用于上拉加载(list、list-item、list-item-group)、popup、轮播组件(swiper)
基础组件:image、text、span、input、label
js
<div>
<text>123</text>
</div>
注意:
1.div组件内部不能够直接嵌入文字,需要通过text组件进行包裹
2.list组件在相同方向的滚动不能嵌套使用,否则会造成滚动异常
3.image标签有些图片格式不支持,需要转换为可支持的格式
CSS
华为鸿蒙元服务不支持less,sass等预编译语言,只支持css,相对于h5来说,还做了部分阉割,有些属性在h5能用,在鸿蒙元服务确用不了。
元素标签默认样式
需要注意的是,在元服务中,所有的div标签都是一个flex盒子,所以在我们使用div的时候,如果是纵向布局,那我们需要去手动改变flex-direction: column,更改主轴方向。
js
//hml
<div id="tabBarCon">
<div id="tab1">
</div>
<div id="tab2" onclick="handleJumpToCart">
</div>
<div id="tab3" onclick="handleJumpToMine">
</div>
</div>
//css
.tabBarCon{
flex-direction:column;
}
元素选择器
只支持部分选择器和部分伪类选择器,像h5中的伪元素选择器都是不支持的,也不支持嵌套使用,由于不存在伪元素选择器,所以遇到有时候一些特殊场景时,我们只能在hml中去判断元素索引来添加动态样式。
属性与h5中的差异
属性 | 鸿蒙元服务 | h5 |
---|---|---|
position | 只支持absolute、relative、fixed | 支持absolute、relative、fixed、sticky |
background渐变 | linear-gradient(134.27deg, #ff397e 0%, #ff074c 98%),渐变百分比不支持带小数点 | 支持 |
长度单位 | 只支持px、百分比,不支持rem、em、vw、vh | px、百分比、rem、em、vw、vh |
多行文字省略 | text-overflow: ellipsis; max-lines: 1;(只能用于text组件) | 单行和多行使用属性不同 |
JS
特点:
1.支持ES6
2.用法和vue2相似
js
// app.js
export default {
onCreate() {
console.info('Application onCreate');
},
onDestroy() {
console.info('Application onDestroy');
},
globalData: {
appData: 'appData',
appVersion: '2.0',
},
globalMethod() {
console.info('This is a global method!');
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;
},
invokeGlobalMethod() {
this.$app.$def.globalMethod();
},
getAppVersion() {
this.appVersion = this.$app.$def.globalData.appVersion;
}
}
data:定义变量
onInit:生命周期函数
getAppVersion:方法,不需要写在methods里面,直接与生命周期函数同级 this. <math xmlns="http://www.w3.org/1998/Math/MathML"> a p p . app. </math>app.def:可以拿到全局对象,
导入导出
支持ESmodule
js
//import
import router from '@ohos.router';
//export
export const xxx=123;
应用级生命周期
页面级生命周期
网络请求
使用@ohos.net.http内置模块即可,下面是对网络请求做了一个简单封装,使用的时候直接导入,调用相应请求方法即可,可惜的鸿蒙元服务目前没法进行抓包,所以网络请求调试的时候只能通过打断点的形式进行调试。
js
import http from '@ohos.net.http';
import { invokeShowLogin } from '../common/invoke_user';
export default {
interceptors(response) {
const result = JSON.parse(response.result || {});
const {code,errno} = result
if (errno === 1024 || code === 1005) {
return invokeShowLogin();
}
return result;
},
get(url, data) {
return http.createHttp().request(
// 填写http请求的url地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定
url,
{
method: http.RequestMethod.GET, // 可选,默认为http.RequestMethod.GET
// 开发者根据自身业务需要添加header字段
header: {
'Content-Type': 'application/json'
},
// 当使用POST请求时此字段用于传递内容
extraData: data,
connectTimeout: 10*1000,
readTimeout: 10*1000,
}
).then(res=>{
return this.interceptors(res);
});
},
post(url, data) {
return http.createHttp().request(
// 填写http请求的url地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定
url,
{
method: http.RequestMethod.POST, // 可选,默认为http.RequestMethod.GET
// 开发者根据自身业务需要添加header字段
header: {
'Content-Type': ' application/x-www-form-urlencoded'
},
// 当使用POST请求时此字段用于传递内容
extraData: data,
connectTimeout: 10*1000, // 可选,默认为60s
readTimeout: 10*1000, // 可选,默认为60s
}
).then(res=>{
return this.interceptors(res);
});
},
postJson(url, data) {
return http.createHttp().request(
// 填写http请求的url地址,可以带参数也可以不带参数。URL地址需要开发者自定义。请求的参数可以在extraData中指定
url,
{
method: http.RequestMethod.POST, // 可选,默认为http.RequestMethod.GET
// 开发者根据自身业务需要添加header字段
header: {
'Content-Type': 'application/json'
},
// 当使用POST请求时此字段用于传递内容
extraData: data,
connectTimeout: 10*1000, // 可选,默认为60s
readTimeout: 10*1000, // 可选,默认为60s
}
).then(res=>{
return this.interceptors(res);
})
}
}
数据存储
只有本地持久化存储这种方式,关闭应用,数据不会丢失。
js
storage.set({
key: 'loginInfo',
value: JSON.stringify({
uid, skey
}),
});
storage.get({
key: 'userInfo',
value: JSON.stringify(userInfo),
});
路由跳转
js
<!-- index.hml -->
<div class="container">
<text class="title">This is the index page.</text>
<button type="capsule" value="Go to the second page" class="button" onclick="launch"></button>
</div>
// index.js
import router from '@ohos.router';
export default {
launch() {
router.push ({
url: 'pages/detail/detail',
//携带的参数
params:{a:123}
});
//router.back()
//router.replace()
},
}.
// detail.js
import router from '@ohos.router';
export default {
data:{
a:''
}
onInit(){
//页面携带过来的参数可以直接使用
//this.a
}
}
官方文档链接
- config.json:developer.harmonyos.com/cn/docs/doc... &developer.harmonyos.com/cn/docs/doc...
- http请求:developer.harmonyos.com/cn/docs/doc...
- hml:developer.harmonyos.com/cn/docs/doc...
- css:developer.harmonyos.com/cn/docs/doc...
- js:developer.harmonyos.com/cn/docs/doc...
- 生命周期:developer.harmonyos.com/cn/docs/doc...
- 目录结构:developer.harmonyos.com/cn/docs/doc...