HarmonyOS Developer 兼容JS的类Web开发范式
概述
兼容JS的类Web开发范式的方舟开发框架,采用经典的HML、CSS、JavaScript三段式开发方式。使用HML标签文件进行布局搭建,使用CSS文件进行样式描述,使用JavaScript文件进行逻辑处理。UI组件与数据之间通过单向数据绑定的方式建立关联,当数据发生变化时,UI界面自动触发更新。此种开发方式,更接近Web前端开发者的使用习惯,快速将已有的Web应用改造成方舟开发框架应用。主要适用于界面较为简单的中小型应用开发。
请参考兼容JS的类Web开发范式API文档,全面地了解组件,更好地开发应用。
整体架构
使用兼容JS的类Web开发范式的方舟开发框架,包括应用层(Application)、前端框架层(Framework)、引擎层(Engine)和平台适配层(Porting Layer)。
- Application
应用层表示开发者开发的FA应用,这里的FA应用特指JS FA应用。 - Framework
前端框架层主要完成前端页面解析,以及提供MVVM(Model-View-ViewModel)开发模式、页面路由机制和自定义组件等能力。 - Engine
引擎层主要提供动画解析、DOM(Document Object Model)树构建、布局计算、渲染命令构建与绘制、事件管理等能力。 - Porting Layer
适配层主要完成对平台层进行抽象,提供抽象接口,可以对接到系统平台。比如:事件对接、渲染管线对接和系统生命周期对接等。
框架说明
文件组织
目录结构
JS FA应用的JS模块(entry/src/main/js/module)的典型开发目录结构如下:
图1 目录结构
图2 多实例资源共享目录结构
目录结构中文件分类如下:
- .hml结尾的HML模板文件,描述当前页面的文件布局结构。
- .css结尾的CSS样式文件,描述页面样式。
- .js结尾的JS文件,处理页面间的交互。
各个文件夹的作用:
- app.js文件用于全局JavaScript逻辑和应用生命周期管理,详见app.js。
- pages目录用于存放所有组件页面。
- common目录用于存放公共资源文件,比如:媒体资源,自定义组件和JS文件。
- resources目录用于存放资源配置文件,比如:多分辨率加载等配置文件,详见资源限定与访问章节。
- share目录用于配置多个实例共享的资源内容,比如:share中的图片和JSON文件可被default1和default2实例共享。
说明
- i18n和resources文件夹不可重命名。
- 如果share目录中的资源和实例(default)中的资源文件同名且目录一致时,实例中资源的优先级高于share中资源的优先级。
- share目录当前不支持i18n。
- 在使用DevEco Studio进行应用开发时,目录结构中的可选文件夹需要开发者根据实际情况自行创建。
文件访问规则
应用资源可通过绝对路径或相对路径的方式进行访问,绝对路径以"/"开头,相对路径以"./"或"../"。具体访问规则如下:
- 引用代码文件,推荐使用相对路径,比如:../common/utils.js。
- 引用资源文件,推荐使用绝对路径。比如:/common/xxx.png。
- 公共代码文件和资源文件推荐放在common下,通过以上两条规则进行访问。
- CSS样式文件中通过url()函数创建<url>数据类型,如:url(/common/xxx.png)。
说明
当代码文件A需要引用代码文件B时:
- 如果代码文件A和文件B位于同一目录,则代码文件B引用资源文件时可使用相对路径,也可使用绝对路径。
- 如果代码文件A和文件B位于不同目录,则代码文件B引用资源文件时必须使用绝对路径。因为Webpack打包时,代码文件B的目录会发生变化。
- 在js文件中通过数据绑定的方式指定资源文件路径时,必须使用绝对路径。
媒体文件格式
表1 支持的图片格式
格式 | 支持的文件类型 |
BMP | .bmp |
GIF | .gif |
JPEG | .jpg |
PNG | .png |
WebP | .webp |
表2 支持的视频格式
格式 | 支持的文件类型 |
H.264 AVC Baseline Profile (BP) | .3gp .mp4 |
js标签配置
js标签中包含了实例名称、页面路由和窗口样式信息。
标签 | 类型 | 默认值 | 必填 | 描述 |
name | string | default | 是 | 标识JS实例的名字。 |
pages | Array | - | 是 | 路由信息,详见“**pages**”。 |
window | Object | - | 否 | 窗口信息,详见“**window**”。 |
说明
name、pages和window等标签配置需在配置文件(config.json)中的“js”标签中完成设置。
pages
定义每个页面的路由信息,每个页面由页面路径和页面名组成,页面的文件名就是页面名。比如:
{
...
"pages": [
"pages/index/index",
"pages/detail/detail"
]
...
}
说明
- pages列表中第一个页面是应用的首页,即entry入口。
- 页面文件名不能使用组件名称,比如:text.hml、button.hml等。
window
window用于定义与显示窗口相关的配置。对于屏幕适配问题,有2种配置方法:
- 指定designWidth(屏幕逻辑宽度),所有与大小相关的样式(例如width、font-size)均以designWidth和实际屏幕宽度的比例进行缩放,例如在designWidth为720时,如果设置width为100px时,在实际宽度为1440物理像素的屏幕上,width实际渲染像素为200物理像素。
- 设置autoDesignWidth为true,此时designWidth字段将会被忽略,渲染组件和布局时按屏幕密度进行缩放。屏幕逻辑宽度由设备宽度和屏幕密度自动计算得出,在不同设备上可能不同,请使用相对布局来适配多种设备。例如:在466*466分辨率,320dpi的设备上,屏幕密度为2(以160dpi为基准),1px等于渲染出的2物理像素。
说明
- 组件样式中<length>类型的默认值,按屏幕密度进行计算和绘制,如:在屏幕密度为2(以160dpi为基准)的设备上,默认<length>为1px时,设备上实际渲染出2物理像素。
- autoDesignWidth、designWidth的设置不影响默认值计算方式和绘制结果。
属性 | 类型 | 必填 | 缺省值 | 描述 |
designWidth | number | 否 | 720 | 页面显示设计时的参考值,实际显示效果基于设备宽度与参考值之间的比例进行缩放。 |
autoDesignWidth | boolean | 否 | false | 页面设计基准宽度是否自动计算,当设为true时,designWidth将会被忽略,设计基准宽度由设备宽度与屏幕密度计算得出。 |
示例如下:
{
...
"window": {
"designWidth": 720,
"autoDesignWidth": false
}
...
}
示例
{
"app": {
"bundleName": "com.example.player",
"version": {
"code": 1,
"name": "1.0"
},
"vendor": "example"
}
"module": {
...
"js": [
{
"name": "default",
"pages": [
"pages/index/index",
"pages/detail/detail"
],
"window": {
"designWidth": 720,
"autoDesignWidth": false
}
}
],
"abilities": [
{
...
}
]
}
}
app.js
应用生命周期
每个应用可以在app.js自定义应用级生命周期的实现逻辑,以下示例仅在生命周期函数中打印对应日志:
// app.js
export default {
onCreate() {
console.info('Application onCreate');
},
onDestroy() {
console.info('Application onDestroy');
},
}
应用对象6+
属性 | 类型 | 描述 |
getApp | Function | 提供getApp()全局方法,可以在自定义js文件中获取app.js中暴露的对象。 |
示例如下:
// app.js
export default {
data: {
test: "by getAPP"
},
onCreate() {
console.info('AceApplication onCreate');
},
onDestroy() {
console.info('AceApplication onDestroy');
},
};
// test.js 自定义逻辑代码
export var appData = getApp().data;
生命周期
应用生命周期
在app.js中可以定义如下应用生命周期函数:
属性 | 类型 | 描述 | 触发时机 |
onCreate | () => void | 应用创建 | 当应用创建时调用。 |
onShow6+ | () => void | 应用处于前台 | 当应用处于前台时触发。 |
onHide6+ | () => void | 应用处于后台 | 当应用处于后台时触发。 |
onDestroy | () => void | 应用销毁 | 当应用退出时触发。 |
页面生命周期
在页面JS文件中可以定义如下页面生命周期函数:
属性 | 类型 | 描述 | 触发时机 |
onInit | () => void | 页面初始化 | 页面数据初始化完成时触发,只触发一次。 |
onReady | () => void | 页面创建完成 | 页面创建完成时触发,只触发一次。 |
onShow | () => void | 页面显示 | 页面显示时触发。 |
onHide | () => void | 页面消失 | 页面消失时触发。 |
onDestroy | () => void | 页面销毁 | 页面销毁时触发。 |
onBackPress | () => boolean | 返回按钮动作 | 当用户点击返回按钮时触发。 - 返回true表示页面自己处理返回逻辑。 - 返回false表示使用默认的返回逻辑。 - 不返回值会作为false处理。 |
onActive()5+ | () => void | 页面激活 | 页面激活时触发。 |
onInactive()5+ | () => void | 页面暂停 | 页面暂停时触发。 |
onNewRequest()5+ | () => void | FA重新请求 | FA已经启动时收到新的请求后触发。 |
页面A的生命周期接口的调用顺序
- 打开页面A:onInit() -> onReady() -> onShow()
- 在页面A打开页面B:onHide()
- 从页面B返回页面A:onShow()
- 退出页面A:onBackPress() -> onHide() -> onDestroy()
- 页面隐藏到后台运行:onInactive() -> onHide()
- 页面从后台运行恢复到前台:onShow() -> onActive()
资源限定与访问
资源限定词
资源限定词可以由一个或多个表征应用场景或设备特征的限定词组合而成,包括屏幕密度等维度,限定词之间通过中划线(-)连接。开发者在resources目录下创建限定词文件时,需要掌握限定词文件的命名要求以及与限定词文件与设备状态的匹配规则。
资源限定词的命名要求
- 限定词的组合顺序:屏幕密度。开发者可以根据应用的使用场景和设备特征,选择其中的一类或几类限定词组成目录名称,顺序不可颠倒。
- 限定词的连接方式:限定词之间均采用中划线(-)连接。例如:res-dark-ldpi.json 。
- 限定词的取值范围:每类限定词的取值必须符合下表的条件,否则,将无法匹配目录中的资源文件,限定词大小写敏感。
- 限定词前缀:resources资源文件的资源限定词有前缀res,例如res-ldpi.json。
- 默认资源限定文件:resources资源文件的默认资源限定文件为res-defaults.json。
- 资源限定文件中不支持使用枚举格式的颜色来设置资源。
表1 资源限定词
类型 | 含义与取值说明 |
屏幕密度 | 表示设备的屏幕密度(单位为dpi),取值如下: - ldpi:表示低密度屏幕(~120dpi)(0.75基准密度) - mdpi:表示中密度屏幕(~160dpi)(基准密度) - hdpi:表示高密度屏幕(~240dpi)(1.5基准密度) - xhdpi:表示加高密度屏幕(~320dpi)(2.0基准密度) - xxhdpi:表示超超高密度屏幕(~480dpi)(3.0基准密度) - xxxhdpi:表示超超超高密度屏幕(~640dpi)(4.0基准密度) |
限定词与设备状态的匹配规则
- 在为设备匹配对应的资源文件时,限定词目录匹配的优先级从高到低依次为:MCC和MNC> 横竖屏 > 深色模式 > 设备类型 > 屏幕密度。在资源限定词目录均未匹配的情况下,则匹配默认资源限定文件。
- 如果限定词目录中包含资源限定词,则对应限定词的取值必须与当前的设备状态完全一致,该目录才能够参与设备的资源匹配。例如:资源限定文件res-hdpi.json与当前设备密度xhdpi无法匹配。
引用JS模块内resources资源
在应用开发的hml和js文件中使用$r的语法,可以对JS模块内的resources目录下的json资源进行格式化,获取相应的资源内容,该目录与pages目录同级,具体目录结构请参考文件组织。
属性 | 类型 | 描述 |
$r | (key: string) => string | 获取资源限定下具体的资源内容。例如:$r('strings.hello')。 参数说明: - key:定义在资源限定文件中的键值,如strings.hello。 |
res-defaults.json示例:
{
"strings": {
"hello": "hello world"
}
}
示例
resources/res-dark.json:
{
"image": {
"clockFace": "common/dark_face.png"
},
"colors": {
"background": "#000000"
}
}
resources/res-defaults.json:
{
"image": {
"clockFace": "common/face.png"
},
"colors": {
"background": "#ffffff"
}
}
<!-- xxx.hml -->
<div style="background-color: {{ $r('colors.background') }}">
<image src="{{ $r('image.clockFace') }}"></image>
</div>
说明
资源限定文件中不支持颜色枚举格式。
多语言支持
基于开发框架的应用会覆盖多个国家和地区,开发框架支持多语言能力后,可以让应用开发者无需开发多个不同语言的版本,就可以同时支持多种语言的切换,为项目维护带来便利。
开发者仅需要通过定义资源文件和引用资源两个步骤,就可以使用开发框架的多语言能力;如果需要在应用中获取当前系统语言,请参考获取语言。
定义资源文件
资源文件用于存放应用在多种语言场景下的资源内容,开发框架使用JSON文件保存资源定义。在文件组织中指定的i18n文件夹内放置语言资源文件,其中语言资源文件的命名是由语言、文字、国家或地区的限定词通过中划线连接组成,其中文字和国家或地区可以省略,如zh-Hant-HK(中国香港地区使用的繁体中文)、zh-CN(中国使用的简体中文)、zh(中文)。命名规则如下:
language[-script-region].json
限定词的取值需符合下表要求。
表1 限定词取值要求
限定词类型 | 含义与取值说明 |
语言 | 表示设备使用的语言类型,由2~3个小写字母组成。例如:zh表示中文,en表示英语,mai表示迈蒂利语。 详细取值范围,请查阅ISO 639(ISO制定的语言编码标准)。 |
文字 | 表示设备使用的文字类型,由1个大写字母(首字母)和3个小写字母组成。例如:Hans表示简体中文,Hant表示繁体中文。 详细取值范围,请查阅ISO 15924(ISO制定的文字编码标准)。 |
国家或地区 | 表示用户所在的国家或地区,由2~3个大写字母或者3个数字组成。例如:CN表示中国,GB表示英国。 详细取值范围,请查阅ISO 3166-1(ISO制定的国家和地区编码标准)。 |
当开发框架无法在应用中找到系统语言的资源文件时,默认使用en-US.json中的资源内容。
资源文件内容格式如下:
en-US.json
{
"strings": {
"hello": "Hello world!",
"object": "Object parameter substitution-{name}",
"array": "Array type parameter substitution-{0}",
"symbol": "@#$%^&*()_+-={}[]\\|:;\"'<>,./?"
},
"files": {
"image": "image/en_picture.PNG"
}
}
由于不同语言针对单复数有不同的匹配规则,在资源文件中使用“zero”“one”“two”“few”“many”“other”定义不同单复数场景下的词条内容。例如中文不区分单复数,仅存在“other”场景;英文存在“one”、“other”场景;阿拉伯语存在上述6种场景。
以en-US.json和ar-AE.json为例,资源文件内容格式如下:
en-US.json
{
"strings": {
"people": {
"one": "one person",
"other": "{count} people"
}
}
}
ar-AE.json
{
"strings": {
"people": {
"zero": "لا أحد",
"one": "وحده",
"two": "اثنان",
"few": "ستة اشخاص",
"many": "خمسون شخص",
"other": "مائة شخص"
}
}
}
引用资源
在应用开发的页面中使用多语言的语法,包含简单格式化和单复数格式化两种,都可以在hml或js中使用。
- 简单格式化方法
在应用中使用$t方法引用资源,$t既可以在hml中使用,也可以在js中使用。系统将根据当前语言环境和指定的资源路径(通过$t的path参数设置),显示对应语言的资源文件中的内容。
表2 简单格式化
属性 | 类型 | 参数 | 必填 | 描述 |
$t | Function | 请见表 $t参数说明 | 是 | 根据系统语言完成简单的替换:this.$t('strings.hello') |
表3 $t参数说明
参数 | 类型 | 必填 | 描述 |
path | string | 是 | 资源路径 |
params | Array|Object | 否 | 运行时用来替换占位符的实际内容,占位符分为两种: - 具名占位符,例如{name}。实际内容必须用Object类型指定,例如:$t('strings.object', {name:'Hello world'})。 - 数字占位符,例如{0}。实际内容必须用Array类型指定,例如:$t('strings.array', [Hello world'] |
- 简单格式化示例代码
<!-- xxx.hml -->
<div>
<!-- 不使用占位符,text中显示“Hello world!” -->
<text>{{ $t('strings.hello') }}</text>
<!-- 具名占位符格式,运行时将占位符{name}替换为“Hello world” -->
<text>{{ $t('strings.object', { name: 'Hello world' }) }}</text>
<!-- 数字占位符格式,运行时将占位符{0}替换为“Hello world” -->
<text>{{ $t('strings.array', ['Hello world']) }}</text>
<!-- 先在js中获取资源内容,再在text中显示“Hello world” -->
<text>{{ hello }}</text>
<!-- 先在js中获取资源内容,并将占位符{name}替换为“Hello world”,再在text中显示“Object parameter substitution-Hello world” -->
<text>{{ replaceObject }}</text>
<!-- 先在js中获取资源内容,并将占位符{0}替换为“Hello world”,再在text中显示“Array type parameter substitution-Hello world” -->
<text>{{ replaceArray }}</text>
<!-- 获取图片路径 -->
<image src="{{ $t('files.image') }}" class="image"></image>
<!-- 先在js中获取图片路径,再在image中显示图片 -->
<image src="{{ replaceSrc }}" class="image"></image>
</div>
// xxx.js
// 下面为在js文件中的使用方法。
export default {
data: {
hello: '',
replaceObject: '',
replaceArray: '',
replaceSrc: '',
},
onInit() {
this.hello = this.$t('strings.hello');
this.replaceObject = this.$t('strings.object', { name: 'Hello world' });
this.replaceArray = this.$t('strings.array', ['Hello world']);
this.replaceSrc = this.$t('files.image');
},
}
- 单复数格式化方法
表4 单复数格式化
属性 | 类型 | 参数 | 必填 | 描述 |
$tc | Function | 请见表 $tc参数说明 | 是 | 根据系统语言完成单复数替换:this.$tc('strings.people') > 说明: > 定义资源的内容通过json格式的key为“zero”、“one”、“two”、“few”、“many”和“other”区分。 |
表5 $tc参数说明
参数 | 类型 | 必填 | 描述 |
path | string | 是 | 资源路径 |
count | number | 是 | 要表达的值 |
- 单复数格式化示例代码
<!--xxx.hml-->
<div>
<!-- 传递数值为0时: "0 people" 阿拉伯语中此处匹配key为zero的词条-->
<text>{{ $tc('strings.people', 0) }}</text>
<!-- 传递数值为1时: "one person" 阿拉伯语中此处匹配key为one的词条-->
<text>{{ $tc('strings.people', 1) }}</text>
<!-- 传递数值为2时: "2 people" 阿拉伯语中此处匹配key为two的词条-->
<text>{{ $tc('strings.people', 2) }}</text>
<!-- 传递数值为6时: "6 people" 阿拉伯语中此处匹配key为few的词条-->
<text>{{ $tc('strings.people', 6) }}</text>
<!-- 传递数值为50时: "50 people" 阿拉伯语中此处匹配key为many的词条-->
<text>{{ $tc('strings.people', 50) }}</text>
<!-- 传递数值为100时: "100 people" 阿拉伯语中此处匹配key为other的词条-->
<text>{{ $tc('strings.people', 100) }}</text>
</div>
获取语言
获取语言功能请参考应用配置。