当前位置: 首页 > news >正文

网店推广方式优化关键词排名提升

网店推广方式,优化关键词排名提升,陈铭生的原型是谁,ie浏览器网页版鸿蒙HarmonyOS学习手册_入门篇 文章目录 鸿蒙HarmonyOS学习手册_入门篇入门快速入门开发准备基本概念UI框架应用模型工具准备 构建第一个ArkTS应用#xff08;Stage模型#xff09;-快速入门-入门创建ArkTS工程ArkTS工程目录结构#xff08;Stage模型#xff09;构建第一个…鸿蒙HarmonyOS学习手册_入门篇 文章目录 鸿蒙HarmonyOS学习手册_入门篇入门快速入门开发准备基本概念UI框架应用模型工具准备 构建第一个ArkTS应用Stage模型-快速入门-入门创建ArkTS工程ArkTS工程目录结构Stage模型构建第一个页面构建第二个页面实现页面间的跳转使用真机运行应用 构建第一个ArkTS应用FA模型创建ArkTS工程ArkTS工程目录结构FA模型构建第一个页面构建第二个页面实现页面间的跳转使用真机运行应用 构建第一个JS应用FA模型创建JS工程JS工程目录结构构建第一个页面构建第二个页面实现页面间的跳转使用真机运行应用 开发基础知识应用程序包基础知识应用程序包概述应用程序包结构Stage模型应用程序包结构FA模型应用程序包结构 应用程序包多HAP机制多HAP机制设计目标多HAP构建视图多HAP的开发调试与发布部署流程应用在终端设备上的安装多HAP使用规则多HAP运行机制及数据通信方式 应用程序包安装和卸载流程开发者终端设备用户 应用程序包更新流程共享包共享包概述HARHSP 应用程序包快速修复快速修复概述快速修复命令行调试开发指导 应用配置文件Stage模型应用配置文件概述Stage模型app.json5配置文件**表1** **app.json5文件配置标签说明** module.json5配置文件deviceTypes标签pages标签metadata标签该标签标识HAP的自定义元信息标签值为数组类型包含namevalueresource三个子标签。abilities标签skills标签extensionAbilities标签requestPermissions标签shortcuts标签distributionFilter标签testRunner标签 应用配置文件FA模型应用配置文件概述FA模型配置文件的内部结构 app对象内部结构version对象内部结构apiVersion内部结构 deviceConfig内部结构deviceConfig对象内部结构deviceConfig设备对象内部结构network对象的内部结构securityConfig对象的内部结构**表4** **securityConfig对象的内部结构说明**domainSettings对象内部结构 module对象内部结构distro对象内部结构metadata对象内部结构parameters对象内部结构results对象内部结构customizeData对象的内部结构deviceType标签abilities对象的内部结构uriPermission对象的内部结构skills对象的内部结构uris对象的内部结构reqPermissions权限申请usedScene对象内部结构js对象的内部结构window对象的内部结构mode对象的内部结构shortcuts对象的内部结构intents对象的内部结构forms对象的内部结构customizeData对象内部结构distroFilter对象的内部结构apiVersion对象的内部结构screenShape对象的内部结构screenWindow对象的内部结构screenDensity对象的内部结构countryCode对象的内部结构commonEvents对象的内部结构testRunner对象的内部结构definePermissions对象内部结构 资源分类与访问资源分类resources目录限定词目录资源组目录 资源访问应用资源系统资源 学习ArkTS语言初识ArkTS语言基本语法基本语法概述声明式UI描述创建组件配置属性配置事件配置子组件 自定义组件创建自定义组件页面和自定义组件生命周期 Builder装饰器自定义构建函数装饰器使用说明参数传递规则 BuilderParam装饰器引用Builder函数装饰器使用说明使用场景 Styles装饰器定义组件重用样式装饰器使用说明使用场景 Extend装饰器定义扩展组件样式装饰器使用说明使用场景 stateStyles多态样式概述使用场景 状态管理状态管理概述基本概念装饰器总览其他状态管理功能 管理组件拥有的状态State装饰器组件内状态Prop装饰器父子单向同步Link装饰器父子双向同步Provide装饰器和Consume装饰器与后代组件双向同步Observed装饰器和ObjectLink装饰器嵌套类对象属性变化 管理应用拥有的状态管理应用拥有的状态概述LocalStorage页面级UI状态存储AppStorage应用全局的UI状态存储 入门 快速入门 开发准备 本文档适用于HarmonyOS应用开发的初学者。通过构建一个简单的具有页面跳转/返回功能的应用如下图所示快速了解工程目录的主要文件熟悉HarmonyOS应用开发流程。 在开始之前您需要了解有关HarmonyOS应用的一些基本概念UI框架的简单说明、应用模型的基本概念。 基本概念 UI框架 HarmonyOS提供了一套UI开发框架即方舟开发框架ArkUI框架。方舟开发框架可为开发者提供应用UI开发所必需的能力比如多种组件、布局计算、动画能力、UI交互、绘制等。 方舟开发框架针对不同目的和技术背景的开发者提供了两种开发范式分别是基于ArkTS的声明式开发范式简称“声明式开发范式”和兼容JS的类Web开发范式简称“类Web开发范式”。以下是两种开发范式的简单对比。 开发范式名称语言生态UI更新方式适用场景适用人群声明式开发范式ArkTS语言数据驱动更新复杂度较大、团队合作度较高的程序移动系统应用开发人员、系统应用开发人员类Web开发范式JS语言数据驱动更新界面较为简单的程序应用和卡片Web前端开发人员 更多UI框架的开发内容及指导详见UI开发。 应用模型 应用模型是HarmonyOS为开发者提供的应用程序所需能力的抽象提炼它提供了应用程序必备的组件和运行机制。有了应用模型开发者可以基于一套统一的模型进行应用开发使应用开发更简单、高效。请见应用模型的构成要素。 随着系统的演进发展HarmonyOS先后提供了两种应用模型 FAFeature Ability模型 HarmonyOS API 7开始支持的模型已经不再主推。FA模型开发可见FA模型开发概述。Stage模型 HarmonyOS API 9开始新增的模型是目前主推且会长期演进的模型。在该模型中由于提供了AbilityStage、WindowStage等类作为应用组件和Window窗口的“舞台”因此称这种应用模型为Stage模型。Stage模型开发可见Stage模型开发概述。 FA模型和Stage模型的整体架构和设计思想等更多区别请见应用模型解读。 快速入门提供了一个含有两个页面的开发实例并使用了不同的开发语言或不同的应用模型进行开发以便开发者理解以上基本概念及应用开发流程。 工具准备 安装最新版DevEco Studio。请参考配置开发环境完成DevEco Studio的安装和开发环境配置。 完成上述操作及基本概念的理解后可参照构建第一个ArkTS应用Stage模型、构建第一个ArkTS应用FA模型、构建第一个JS应用FA模型中的任一章节进行下一步体验和学习。 构建第一个ArkTS应用Stage模型-快速入门-入门 说明 为确保运行效果本文以使用DevEco Studio 3.1 Release版本为例点击此处获取下载链接。 创建ArkTS工程 若首次打开DevEco Studio请点击Create Project创建工程。如果已经打开了一个工程请在菜单栏选择File New Create Project来创建一个新工程。 选择Application应用开发本文以应用开发为例Atomic Service对应为元服务开发选择模板“Empty Ability”点击Next进行下一步配置。 进入配置工程界面Compile SDK选择“3.1.0(API 9)”Model 选择“Stage”其他参数保持默认设置即可。 说明 支持使用ArkTS低代码开发方式。 低代码开发方式具有丰富的UI界面编辑功能通过可视化界面开发方式快速构建布局可有效降低开发者的上手成本并提升开发者构建UI界面的效率。 如需使用低代码开发方式请打开上图中的Enable Super Visual开关。 点击Finish工具会自动生成示例代码和相关资源等待工程创建完成。 ArkTS工程目录结构Stage模型 AppScope app.json5应用的全局配置信息。entry src main ets用于存放ArkTS源码。src main ets entryability应用/服务的入口。src main ets pages应用/服务包含的页面。src main resources用于存放应用/服务所用到的资源文件如图形、多媒体、字符串、布局文件等。关于资源文件详见资源分类与访问。src main module.json5Stage模型模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。具体的配置文件说明详见module.json5配置文件。build-profile.json5当前的模块信息、编译信息配置项包括buildOption、targets配置等。其中targets中可配置当前运行环境默认为HarmonyOS。hvigorfile.ts模块级编译构建任务脚本开发者可以自定义相关任务和代码实现。 oh_modules用于存放三方库依赖信息。关于原npm工程适配ohpm操作请参考历史工程迁移。build-profile.json5应用级配置信息包括签名、产品配置等。hvigorfile.ts应用级编译构建任务脚本。 构建第一个页面 使用文本组件。 工程同步完成后在“Project”窗口点击“entry src main ets pages”打开“Index.ets”文件可以看到页面由Text组件组成。“Index.ets”文件的示例如下 // Index.ets Entry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.width(100%)}.height(100%)} }添加按钮。 在默认页面基础上我们添加一个Button组件作为按钮响应用户点击从而实现跳转到另一个页面。“Index.ets”文件的示例如下 // Index.ets Entry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.width(100%)}.height(100%)} }在编辑窗口右上角的侧边工具栏点击Previewer打开预览器。第一个页面效果如下图所示 构建第二个页面 创建第二个页面。 新建第二个页面文件。在“Project”窗口打开“entry src main ets ”右键点击“pages”文件夹选择“New ArkTS File”命名为“Second”点击“Finish”。可以看到文件目录结构如下 说明 开发者也可以在右键点击“pages”文件夹时选择“New Page”则无需手动配置相关页面路由。 配置第二个页面的路由。在“Project”窗口打开“entry src main resources base profile”在main_pages.json文件中的“src”下配置第二个页面的路由“pages/Second”。示例如下 {src: [pages/Index,pages/Second] }添加文本及按钮。 参照第一个页面在第二个页面添加Text组件、Button组件等并设置其样式。“Second.ets”文件的示例如下 // Second.ets Entry Component struct Second {State message: string Hi therebuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)Button() {Text(Back).fontSize(25).fontWeight(FontWeight.Bold)}.type(ButtonType.Capsule).margin({top: 20}).backgroundColor(#0D9FFB).width(40%).height(5%)}.width(100%)}.height(100%)} }实现页面间的跳转 页面间的导航可以通过页面路由router来实现。页面路由router根据页面url找到目标页面从而实现跳转。使用页面路由请导入router模块。 第一个页面跳转到第二个页面。 在第一个页面中跳转按钮绑定onClick事件点击按钮时跳转到第二页。“Index.ets”文件的示例如下 // Index.ets // 导入页面路由模块 import router from ohos.router;Entry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)// 添加按钮以响应用户点击Button() {Text(Next).fontSize(30).fontWeight(FontWeight.Bold)}.type(ButtonType.Capsule).margin({top: 20}).backgroundColor(#0D9FFB).width(40%).height(5%)// 跳转按钮绑定onClick事件点击时跳转到第二页.onClick(() {console.info(Succeeded in clicking the Next button.)// 跳转到第二页router.pushUrl({ url: pages/Second }).then(() {console.info(Succeeded in jumping to the second page.)}).catch((err) {console.error(Failed to jump to the second page.Code is ${err.code}, message is ${err.message})})})}.width(100%)}.height(100%)} }第二个页面返回到第一个页面。 在第二个页面中返回按钮绑定onClick事件点击按钮时返回到第一页。“Second.ets”文件的示例如下 // Second.ets // 导入页面路由模块 import router from ohos.router;Entry Component struct Second {State message: string Hi therebuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)Button() {Text(Back).fontSize(25).fontWeight(FontWeight.Bold)}.type(ButtonType.Capsule).margin({top: 20}).backgroundColor(#0D9FFB).width(40%).height(5%)// 返回按钮绑定onClick事件点击按钮时返回到第一页.onClick(() {console.info(Succeeded in clicking the Back button.)try {// 返回第一页router.back()console.info(Succeeded in returning to the first page.)} catch (err) {console.error(Failed to return to the first page.Code is ${err.code}, message is ${err.message})}})}.width(100%)}.height(100%)} }打开“Index.ets”文件点击预览器中的按钮进行刷新。效果如下图所示 使用真机运行应用 运行HarmonyOS应用可以使用远程模拟器和物理真机设备区别在于使用远程模拟器运行应用不需要对应用进行签名。接下来将以物理真机设备为例介绍HarmonyOS应用的运行方法关于模拟器的使用请参考使用Remote Emulator运行应用/服务。 将搭载HarmonyOS系统的真机与电脑连接。具体指导及要求可查看使用本地真机运行应用/服务。 点击File Project Structure… Project SigningConfigs界面勾选“Support HarmonyOS”和“Automatically generate signature”点击界面提示的“Sign In”使用华为帐号登录。等待自动签名完成后点击“OK”即可。如下图所示 在编辑窗口右上角的工具栏点击按钮运行。效果如下图所示 恭喜您已经使用ArkTS语言开发Stage模型完成了第一个HarmonyOS应用快来探索更多的HarmonyOS功能吧。 构建第一个ArkTS应用FA模型 创建ArkTS工程 若首次打开DevEco Studio请点击Create Project创建工程。如果已经打开了一个工程请在菜单栏选择File New Create Project来创建一个新工程。 选择Application应用开发本文以应用开发为例Atomic Service对应为元服务开发选择模板“Empty Ability”点击Next进行下一步配置。 进入配置工程界面Compile SDK选择“3.0.0(API 8)”Compile SDK选择“3.1.0(API 9)”时注意同步选择 Model 为“FA”此处以选择“3.0.0(API 8)”为例Language选择“ArkTS”其他参数保持默认设置即可。 说明 DevEco Studio V3.0 Beta3及更高版本支持使用ArkTS低代码开发方式。 低代码开发方式具有丰富的UI界面编辑功能通过可视化界面开发方式快速构建布局可有效降低开发者的上手成本并提升开发者构建UI界面的效率。 如需使用低代码开发方式请打开上图中的Enable Super Visual开关。 点击Finish工具会自动生成示例代码和相关资源等待工程创建完成。 ArkTS工程目录结构FA模型 entryHarmonyOS工程模块编译构建生成一个HAP包。 src main ets用于存放ets源码。src main ets MainAbility应用/服务的入口。src main ets MainAbility pagesMainAbility包含的页面。src main ets MainAbility pages index.etspages列表中的第一个页面即应用的首页入口。src main ets MainAbility app.ets承载Ability生命周期。src main resources用于存放应用/服务所用到的资源文件如图形、多媒体、字符串、布局文件等。关于资源文件详见资源分类与访问。src main config.json模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。具体的配置文件说明详见应用配置文件FA模型。build-profile.json5当前的模块信息、编译信息配置项包括buildOption、targets配置等。其中targets中可配置当前运行环境默认为HarmonyOS。hvigorfile.ts模块级编译构建任务脚本开发者可以自定义相关任务和代码实现。 build-profile.json5应用级配置信息包括签名、产品配置等。hvigorfile.ts应用级编译构建任务脚本。 构建第一个页面 使用文本组件。 工程同步完成后在“Project”窗口点击“entry src main ets MainAbility pages”打开“index.ets”文件可以看到页面由Text组件组成。“index.ets”文件的示例如下 // index.ets Entry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.width(100%)}.height(100%)} }添加按钮。 在默认页面基础上我们添加一个Button组件作为按钮响应用户点击从而实现跳转到另一个页面。“index.ets”文件的示例如下 // index.ets Entry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)// 添加按钮以响应用户点击Button() {Text(Next).fontSize(30).fontWeight(FontWeight.Bold)}.type(ButtonType.Capsule).margin({top: 20}).backgroundColor(#0D9FFB).width(40%).height(5%)}.width(100%)}.height(100%)} }在编辑窗口右上角的侧边工具栏点击Previewer打开预览器。第一个页面效果如下图所示 构建第二个页面 创建第二个页面。 新建第二个页面文件。在“Project”窗口打开“entry src main ets MainAbility”右键点击“pages”文件夹选择“New ArkTS File”命名为“second”点击“Finish”。可以看到文件目录结构如下 说明 开发者也可以在右键点击“pages”文件夹时选择“New Page”则无需手动配置相关页面路由。 配置第二个页面的路由。在config.json文件中的“module - js - pages”下配置第二个页面的路由“pages/second”。示例如下 {module: {js: [{pages: [pages/index,pages/second]}]} }添加文本及按钮。 参照第一个页面在第二个页面添加Text组件、Button组件等并设置其样式。“second.ets”文件的示例如下 // second.ets Entry Component struct Second {State message: string Hi therebuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)Button() {Text(Back).fontSize(25).fontWeight(FontWeight.Bold)}.type(ButtonType.Capsule).margin({top: 20}).backgroundColor(#0D9FFB).width(40%).height(5%)}.width(100%)}.height(100%)} }实现页面间的跳转 页面间的导航可以通过页面路由router来实现。页面路由router根据页面url找到目标页面从而实现跳转。使用页面路由请导入router模块。 第一个页面跳转到第二个页面。 在第一个页面中跳转按钮绑定onClick事件点击按钮时跳转到第二页。“index.ets”文件的示例如下 // index.ets // 导入页面路由模块 import router from ohos.router;Entry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)// 添加按钮以响应用户点击Button() {Text(Next).fontSize(30).fontWeight(FontWeight.Bold)}.type(ButtonType.Capsule).margin({top: 20}).backgroundColor(#0D9FFB).width(40%).height(5%)// 跳转按钮绑定onClick事件点击时跳转到第二页.onClick(() {router.push({ url: pages/second })// 若为API 9工程则可使用以下接口// router.pushUrl({ url: pages/second })})}.width(100%)}.height(100%)} }第二个页面返回到第一个页面。 在第二个页面中返回按钮绑定onClick事件点击按钮时返回到第一页。“second.ets”文件的示例如下 // second.ets // 导入页面路由模块 import router from ohos.router;Entry Component struct Second {State message: string Hi therebuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)Button() {Text(Back).fontSize(25).fontWeight(FontWeight.Bold)}.type(ButtonType.Capsule).margin({top: 20}).backgroundColor(#0D9FFB).width(40%).height(5%)// 返回按钮绑定onClick事件点击按钮时返回到第一页.onClick(() {router.back()})}.width(100%)}.height(100%)} }打开index.ets文件点击预览器中的按钮进行刷新。效果如下图所示 使用真机运行应用 运行HarmonyOS应用可以使用远程模拟器和物理真机设备区别在于使用远程模拟器运行应用不需要对应用进行签名。接下来将以物理真机设备为例介绍HarmonyOS应用的运行方法关于模拟器的使用请参考使用Remote Emulator运行应用/服务。 将搭载HarmonyOS系统的真机与电脑连接。具体指导及要求可查看使用本地真机运行应用/服务。 点击File Project Structure… Project SigningConfigs界面勾选“Support HarmonyOS”和“Automatically generate signature”点击界面提示的“Sign In”使用华为帐号登录。等待自动签名完成后点击“OK”即可。如下图所示 在编辑窗口右上角的工具栏点击按钮运行。效果如下图所示 恭喜您已经使用ArkTS语言开发FA模型完成了第一个HarmonyOS应用快来探索更多的HarmonyOS功能吧。 构建第一个JS应用FA模型 创建JS工程 若首次打开DevEco Studio请点击Create Project创建工程。如果已经打开了一个工程请在菜单栏选择File New Create Project来创建一个新工程。 选择Application应用开发本文以应用开发为例Atomic Service对应为元服务开发选择模板“Empty Ability”点击Next进行下一步配置。 进入配置工程界面Compile SDK选择“3.0.0(API 8)”Compile SDK选择“3.1.0(API 9)”时注意同步选择 Model 为“FA”此处以选择“3.0.0(API 8)”为例Language选择“JS”其他参数保持默认设置即可。 说明 DevEco Studio V2.2 Beta1及更高版本支持使用JS低代码开发方式。 低代码开发方式具有丰富的UI界面编辑功能通过可视化界面开发方式快速构建布局可有效降低开发者的上手成本并提升开发者构建UI界面的效率。 如需使用低代码开发方式请打开上图中的Enable Super Visual开关。 点击Finish工具会自动生成示例代码和相关资源等待工程创建完成。 JS工程目录结构 entryHarmonyOS工程模块编译构建生成一个HAP包。 src main js用于存放js源码。src main js MainAbility应用/服务的入口。src main js MainAbility i18n用于配置不同语言场景资源内容比如应用文本词条、图片路径等资源。src main js MainAbility pagesMainAbility包含的页面。src main js MainAbility app.js承载Ability生命周期。src main resources用于存放应用/服务所用到的资源文件如图形、多媒体、字符串、布局文件等。关于资源文件详见资源限定与访问。src main config.json模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。具体的配置文件说明详见应用配置文件FA模型。build-profile.json5当前的模块信息、编译信息配置项包括buildOption、targets配置等。其中targets中可配置当前运行环境默认为HarmonyOS。hvigorfile.ts模块级编译构建任务脚本开发者可以自定义相关任务和代码实现。 build-profile.json5应用级配置信息包括签名、产品配置等。hvigorfile.ts应用级编译构建任务脚本。 构建第一个页面 使用文本组件。 工程同步完成后在“Project”窗口点击“entry src main js MainAbility pages index”打开“index.hml”文件设置Text组件内容。“index.hml”文件的示例如下 !-- index.hml -- div classcontainertext classtitleHello World/text /div添加按钮并绑定onclick方法。 在默认页面基础上我们添加一个button类型的input组件作为按钮响应用户点击从而实现跳转到另一个页面。“index.hml”文件的示例代码如下 !-- index.hml -- div classcontainertext classtitleHello World/text!-- 添加按钮值为Next并绑定onclick方法--input classbtn typebutton valueNext onclickonclick/input /div设置页面样式。 在“Project”窗口点击“entry src main js MainAbility pages index”打开“index.css”文件可以对页面中文本、按钮设置宽高、字体大小、间距等样式。“index.css”文件的示例如下 /* index.css */ .container {display: flex;flex-direction: column;justify-content: center;align-items: center;left: 0px;top: 0px;width: 100%;height: 100%; }.title {font-size: 100px;font-weight: bold;text-align: center;width: 100%;margin: 10px; }.btn {font-size: 60px;font-weight: bold;text-align: center;width: 40%;height: 5%;margin-top: 20px; }在编辑窗口右上角的侧边工具栏点击Previewer打开预览器。第一个页面效果如下图所示 构建第二个页面 创建第二个页面。 在“Project”窗口打开“entry src main js MainAbility”右键点击“pages”文件夹选择“New Page”命名为“second”点击“Finish”即完成第二个页面的创建。可以看到文件目录结构如下 添加文本及按钮。 参照第一个页面在第二个页面添加文本、按钮及点击按钮绑定页面返回等。“second.hml”文件的示例如下 !-- second.hml -- div classcontainertext classtitleHi there/text!-- 添加按钮值为Back并绑定back方法--input classbtn typebutton valueBack onclickback/input /div设置页面样式**。**“second.css”文件的示例如下 /* second.css */ .container {display: flex;flex-direction: column;justify-content: center;align-items: center;left: 0px;top: 0px;width: 100%;height: 100%; }.title {font-size: 100px;font-weight: bold;text-align: center;width: 100%;margin: 10px; }.btn {font-size: 60px;font-weight: bold;text-align: center;width: 40%;height: 5%;margin-top: 20px; }实现页面间的跳转 页面间的导航可以通过页面路由router来实现。页面路由router根据页面url找到目标页面从而实现跳转。使用页面路由请导入router模块。 第一个页面跳转到第二个页面。 在第一个页面中跳转按钮绑定onclick方法点击按钮时跳转到第二页。“index.js”示例如下 // index.js // 导入页面路由模块 import router from ohos.router;export default {onclick: function () {router.push({url: pages/second/second})} }第二个页面返回到第一个页面。 在第二个页面中返回按钮绑定back方法点击按钮时返回到第一页。“second.js”示例如下 // second.js // 导入页面路由模块 import router from ohos.router;export default {back: function () {router.back()} }打开index文件夹下的任意一个文件点击预览器中的按钮进行刷新。效果如下图所示 使用真机运行应用 运行HarmonyOS应用可以使用远程模拟器和物理真机设备区别在于使用远程模拟器运行应用不需要对应用进行签名。接下来将以物理真机设备为例介绍HarmonyOS应用的运行方法关于模拟器的使用请参考使用Remote Emulator运行应用/服务。 将搭载HarmonyOS系统的真机与电脑连接。具体指导及要求可查看使用本地真机运行应用/服务。 点击File Project Structure… Project SigningConfigs界面勾选“Support HarmonyOS”和“Automatically generate signature”点击界面提示的“Sign In”使用华为帐号登录。等待自动签名完成后点击“OK”即可。如下图所示 在编辑窗口右上角的工具栏点击按钮运行。效果如下图所示 恭喜您已经使用JS语言开发FA模型完成了第一个HarmonyOS应用快来探索更多的HarmonyOS功能吧。 开发基础知识 应用程序包基础知识 应用程序包概述 用户应用程序泛指运行在设备的操作系统之上为用户提供特定服务的程序简称“应用”。一个应用所对应的软件包文件称为“应用程序包”。 HarmonyOS提供了应用程序包开发、安装、查询、更新、卸载的管理机制方便开发者开发和管理HarmonyOS应用具体如下 应用软件所涉及的文件多种多样开发者可通过HarmonyOS提供的集成开发工具将其开发的可执行代码、资源、三方库等文件整合到一起制作成HarmonyOS应用程序包便于开发者对应用程序的部署。应用软件所涉及的设备类型多种多样开发者可通过HarmonyOS提供的应用程序包配置文件指定其应用程序包的分发设备类型便于应用市场对应用程序包的分发管理。应用软件所包含的功能多种多样将不同的功能特性按模块来划分和管理是一种良好的设计方式。HarmonyOS提供了同一应用程序的多包管理的机制开发者可以将不同的功能特性聚合到不同的包中方便后续的维护与扩展。应用软件涉及的芯片平台多种多样有x86、ARM等还有32位、64位之分HarmonyOS为应用程序包屏蔽了芯片平台的差异使应用程序包在不同的芯片平台都能够安装运行。应用软件涉及的软件信息多种多样有应用版本、应用名称、组件、申请权限等的信息HarmonyOS包管理为开发者提供了这些信息的查询接口方便开发者在程序中查询所需要的包信息。应用软件涉及的资源多种多样有媒体资源、原生资源、字符资源以及国际化的资源等HarmonyOS包管理将不同的资源归档到不同的目录中并集成资源索引文件方便应用对资源的查找和使用。 应用程序包结构 Stage模型应用程序包结构 基于Stage模型开发的应用经编译打包后其应用程序包结构如下图**应用程序包结构Stage模型**所示。开发者需要熟悉应用程序包结构相关的基本概念。 在开发态一个应用包含一个或者多个Module可以在DevEco Studio工程中创建一个或者多个Module。Module是HarmonyOS应用/服务的基本功能单元包含了源代码、资源文件、第三方库及应用/服务配置文件每一个Module都可以独立进行编译和运行。Module分为“Ability”和“Library”两种类型“Ability”类型的Module对应于编译后的HAPHarmony Ability Package“Library”类型的Module对应于HARHarmony Archive或者HSPHarmony Shared Package。 一个Module可以包含一个或多个UIAbility组件如下图所示。 图1 Module与UIAbility组件关系示意图 全文中介绍到的Module默认指的是“Ability”类型的Module。 开发者通过DevEco Studio把应用程序编译为一个或者多个.hap后缀的文件即HAP。HAP是HarmonyOS应用安装的基本单位包含了编译后的代码、资源、三方库及配置文件。HAP可分为Entry和Feature两种类型。 Entry类型的HAP是应用的主模块在module.json5配置文件中的type标签配置为“entry”类型。在同一个应用中同一设备类型只支持一个Entry类型的HAP通常用于实现应用的入口界面、入口图标、主特性功能等。Feature类型的HAP是应用的动态特性模块在module.json5配置文件中的type标签配置为“feature”类型。一个应用程序包可以包含一个或多个Feature类型的HAP也可以不包含Feature类型的HAP通常用于实现应用的特性功能可以配置成按需下载安装也可以配置成随Entry类型的HAP一起下载安装请参见module对象内部结构中的“deliveryWithInstall”。 每个HarmonyOS应用可以包含多个.hap文件一个应用中的.hap文件合在一起称为一个Bundle而bundleName就是应用的唯一标识请参见app.json5配置文件中的bundleName标签。需要特别说明的是在应用上架到应用市场时需要把应用包含的所有.hap文件即Bundle打包为一个.app后缀的文件用于上架这个.app文件称为App PackApplication Package其中同时包含了描述App Pack属性的pack.info文件在云端服务器分发和终端设备安装时都是以HAP为单位进行分发和安装的。 打包后的HAP包结构包括ets、libs、resources等文件夹和resources.index、module.json、pack.info等文件。 ets目录用于存放应用代码编译后的字节码文件。libs目录用于存放库文件。库文件是HarmonyOS应用依赖的第三方代码.so二进制文件。resources目录用于存放应用的资源文件字符串、图片等便于开发者使用和维护详见资源分类与访问。resources.index是资源索引表由IDE编译工程时生成。module.json是HAP的配置文件内容由工程配置中的module.json5和app.json5组成该文件是HAP中必不可少的文件。IDE会自动生成一部分默认配置开发者按需修改其中的配置。详细字段请参见应用配置文件。pack.info是Bundle中用于描述每个HAP属性的文件例如app中的bundleName和versionCode信息、module中的name、type和abilities等信息由IDE工具生成Bundle包时自动生成。 图2 应用程序包结构Stage模型 [外链图片转存中…(img-XvD15jEc-1704894464549)] FA模型应用程序包结构 基于FA模型开发的应用其应用程序包结构如下图**应用程序包结构FA模型**所示。开发者需要熟悉应用程序包结构相关的基本概念。 FA模型与Stage模型不同之处在于HAP内部文件存放位置不同FA模型将所有的资源文件、库文件和代码文件都放在assets文件夹中在文件夹内部进一步区分。 config.json是应用配置文件IDE会自动生成一部分模块代码开发者按需修改其中的配置。详细字段请参见应用配置文件。assets是HAP所有的资源文件、库文件和代码文件的集合内部可以分为entry和js文件夹。entry文件夹中存放的是resources目录和resources.index文件。resources目录用于存放应用的资源文件字符串、图片等便于开发者使用和维护详见资源分类与访问。resources.index是资源索引表由IDE调用SDK工具生成。js文件夹中存放的是编译后的代码文件。pack.info是Bundle中用于描述每个HAP属性的文件例如app中的bundleName和versionCode信息、module中的name、type和abilities等信息由IDE工具生成Bundle包时自动生成。 图1 应用程序包结构FA模型 [外链图片转存中…(img-tu5I5CA2-1704894464550)] 应用程序包多HAP机制 多HAP机制设计目标 方便开发者模块化的管理应用好的应用一般都是模块化管理模块之间属于松耦合关系。多HAP方便了开发者将业务划分成多个模块每个模块放到独立的HAP中。例如支付类应用有统一的主界面主界面管理“扫一扫”、“收付款”、“消息”、“理财”等各个模块。其中主界面管理其他模块的逻辑在Entry包中实现而“扫一扫”、“收付款”、“消息”和“理财”等模块在不同的Feature包中实现。可以同时开发多个Feature包能够实现Feature包单独的开发测试最终由Entry包统一集成Feature包的特性。方便开发者将多HAP合理地组合并部署到不同的设备上。例如应用程序包含一个Entry包和两个Featrue包Feature1和Feature2。其中Entry包可以部署到设备A和设备BFeature1只能部署到设备AFeature2包只部署到设备B上那么开发者就可以方便的组合Entry和Feature1部署到设备A上组合Entry和Feature2部署到设备B上。方便开发者按需加载所需模块减少包大小。开发者可以将一个应用的某些HAP配置成按需加载。应用在启动阶段初始用不到的特性可以配置暂不加载当用户用到这些特性的时候可由应用自动下载这些特性HAP一定程度上减少应用包的大小。方便应用资源共享减少程序包大小。多个HAP都需要用到的资源包括公共资源文件、公共页面等以及soshared object文件可以放到单独的HAP中其他HAP可以到该HAP中访问资源和so文件也一定程度上可以减少应用程序包大小。 多HAP构建视图 IDE支持在一个应用工程中进行多个HAP的开发与构建如下图所示。 图1 多HAP构建视图 [外链图片转存中…(img-TVwtH37d-1704894464550)] IDE开发态视图 AppScope目录 app.json5配置应用全局描述信息例如应用包名、版本号、应用图标、应用名称和依赖的SDK版本号等。 resources目录放置应用的图标资源和应用名称字符串资源。 说明 该目录由IDE自动生成名称不可更改。AppScope目录下面的文件名与Entry、Feature模块下面的文件名不能重复否则IDE会报错。 entry或者feature目录名称可由开发者自定义 由IDE引导开发者创建的Module在该Module中实现应用的业务逻辑可以创建多个Module图中entry和feature即是创建的两个Module。resources目录放置该Module中所使用到的资源。ets目录开发者的业务逻辑。module.json5配置该Module的描述信息如Module的名称、Module的入口代码路径、包含的组件信息等。 编译打包后的视图 一个开发态的Module编译后生成一个部署态的HAPModule和HAP一一对应。HAP中的module.json由开发视图中的app.json5和module.json5合成。所有的HAP最终会编译到一个App Pack中以.app为后缀的包文件用于发布到应用市场。 多HAP的开发调试与发布部署流程 多HAP的开发调试与发布部署流程如下图所示。 图1 多HAP的开发调试与发布部署流程 [外链图片转存中…(img-0S6vEwMt-1704894464552)] 开发 开发者通过DevEco Studio工具按照业务的需要创建多个Module在相应的Module中完成自身业务的开发。 调试 通过DevEco Studio编译打包生成单个或者多个HAP即可基于HAP进行调试。如需根据不同的部署环境、目标人群、运行环境等将同一个HAP定制编译为不同版本请参见定制编译指导。 在调试前需要先安装或更新HAP以下介绍具体做法。 使用DevEco Studio进行调试 使用指导可参考应用程序包调试方法其中包括了单HAP与多HAP通过DevEco Studio工具的安装调试方法。 使用hdc工具(可通过HarmonyOS SDK获取在SDK的toolchains目录下)进行调试 在调试前需要先安装或更新HAP此处有两种方式。 直接使用hdc安装、更新HAP。 HAP的路径为开发平台上的文件路径以Windows开发平台为例命令参考如下 // 安装、更新多HAP可以指定多个文件路径 hdc install C:\entry.hap C:\feature.hap // 执行结果 install bundle successfully. // 卸载 hdc uninstall com.example.myapplication // 执行结果 uninstall bundle successfully.先执行hdc shell再使用bm工具安装、更新HAP。 HAP的文件路径为真机上的文件路径命令参考如下 // 先执行hdc shell才能使用bm工具 hdc shell // 安装、更新多HAP可以指定多个文件路径 bm install -p /data/app/entry.hap /data/app/feature.hap // 执行结果 install bundle successfully. // 卸载 bm uninstall -n com.example.myapplication // 执行结果 uninstall bundle successfully.完成HAP安装或更新后即可参考相关调试命令进行调试。 发布 当开发的程序包满足发布要求时可以在工具中打包编译生成App包。将该App包上架到应用市场云端应用市场会对上架的App包校验签名校验签名通过后会将App包中的HAP拆分出来同时对拆分出的HAP重新添加签名然后对HAP进行分发。 部署 用户在设备上的应用市场客户端能够看到各种各样的应用这些应用均由云端分发而来有些是多HAP应用有些是单HAP应用。用户选择某个应用后应用市场将下载应用所包含的全部deliveryWithInstall设置为“true”的HAP。 应用在终端设备上的安装 下载完成后应用市场客户端再调用系统中包管理服务的安装接口安装下载的HAP包管理服务以应用为单位将其中所有HAP部署到指定目录下以完成应用的安装。 多HAP使用规则 App Pack包不能直接安装到设备上只是上架应用市场的单元。App Pack包中所有HAP的配置文件中的bundleName标签必须一致。App Pack包中所有HAP的配置文件中的versionCode标签必须一致。App Pack包中同一设备类型的所有HAP中必须有且只有一个entry类型的HAPfeature类型的HAP可以有一个或者多个也可以没有。App Pack包中的每个HAP必须配置moduleName标签同一设备类型的所有HAP对应的moduleName标签必须唯一。同一应用的所有HAP签名证书要保持一致。上架应用市场是以App Pack的形式上架并对其进行了签名。应用市场分发时会将所有HAP从App Pack中拆分出来同时对其中的所有HAP进行重签名这样保证了所有HAP签名证书的一致性。在调试阶段开发者通过命令行或IDE将HAP安装到设备上时要保证所有HAP签名证书一致否则会出现安装失败的问题。 多HAP运行机制及数据通信方式 多HAP机制主要是为方便开发者进行模块化管理。HAP和应用运行时的进程并不是一一对应的具体运行机制如下 默认情况下应用中同一包名的所有UIAbility、ServiceExtensionAbility、DataShareExtensionAbility运行在同一个独立进程中其他同类型ExtensionAbility分别运行在单独的进程。HAP支持在module.json5Stage模型或者config.jsonFA模型中通过process标签配置单独的进程仅系统应用支持三方应用不支持。配置了process的HAP其组件运行在单独的process进程中多个HAP可以配置相同的process则这些HAP运行在相同进程中process配置的详细说明请参见module.json5配置文件。应用运行时同一进程中的UIAbility组件被启动时才加载对应HAP的资源和代码。 基于上述机制多HAP数据通信方式如下 同一进程内的数据通信请参见线程间通信。跨进程的数据通信请参见进程间通信。多HAP如果运行在同一进程则多HAP间组件的通信方式与同一HAP内组件的通信方式相同。 应用程序包安装和卸载流程 开发者 开发者可以通过调试命令进行应用的安装和卸载可参考多HAP的调试流程。 图1 应用程序包安装和卸载流程开发者 [外链图片转存中…(img-ZG3yaXP9-1704894464554)] 终端设备用户 开发者将应用上架应用市场后终端设备用户可以在终端设备上使用应用市场进行应用的安装和卸载。 图2 应用程序包安装和卸载流程终端设备用户 [外链图片转存中…(img-9mTCnEVz-1704894464555)] 应用程序包更新流程 HarmonyOS包管理服务提供了应用程序包更新能力更新方式如下。 应用市场内更新新版本应用通过应用市场上架后应用市场通知终端用户该应用有新版本终端用户可以根据通知到应用市场客户端进行应用升级。应用内检测升级终端用户启动应用时应用市场检测到该应用有新版本会通知终端用户可以到应用市场进行应用的下载更新。 共享包 共享包概述 OpenHarmony提供了两种共享包HARHarmony Archive静态共享包和HSPHarmony Shared Package动态共享包。 HAR与HSP都是为了实现代码和资源的共享都可以包含代码、C库、资源和配置文件最大的不同之处在于HAR中的代码和资源跟随使用方编译如果有多个使用方它们的编译产物中会存在多份相同拷贝而HSP中的代码和资源可以独立编译运行时在一个进程中代码也只会存在一份。 图1 HAR和HSP在APP包中的形态示意图 [外链图片转存中…(img-XIUl1ljK-1704894464556)] HSP旨在解决HAR存在的几个问题 多个HAP引用相同的HAR导致的APP包大小膨胀问题。多个HAP引用相同的HARHAR中的一些状态变量无法共享的问题。 HSP的一些约束 HSP及其使用方都必须是Stage模型。HSP及其使用方都必须使用esmodule编译模式。HSP不支持在配置文件中声明abilities、extensionAbilities标签。 HSP按照使用场景可以分为应用内HSP和应用间HSP应用间HSP暂不支持。 HAR HARHarmony Archive是静态共享包可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP不能独立安装运行在设备上只能作为应用模块的依赖项被引用。 创建HAR模块 HAR对应DevEco Studio工程中的“Library”类型的Module可以通过DevEco Studio创建一个HAR模块。HAR模块默认不开启混淆能力开启混淆能力需要把HAR模块的build-profile.json5文件中的artifactType字段设置为obfuscation配置如下所示 {apiType: stageMode,buildOption: {artifactType: obfuscation} }artifactType字段有以下两种取值默认缺省为original。 original不混淆。obfuscation混淆目前仅支持uglify混淆。 需要对代码资产进行保护时建议开启混淆能力混淆能力开启后DevEco Studio在构建HAR时会对代码进行编译、混淆及压缩处理保护代码资产。 注意artifactType字段设置为obfuscation时apiType字段必须设置为stageMode因为Stage模型才支持混淆。 HAR开发注意事项 HAR不支持在配置文件中声明abilities、extensionAbilities组件。HAR不支持在配置文件中声明pages页面。HAR不支持在build-profile.json5文件的buildOption中配置worker。FA模型与Stage模型的HAR不支持相互引用。Stage模型的HAR不能引用AppScope内的内容。在编译构建时APPScope中的内容不会打包到HAR中导致HAR资源引用失败。 导出HAR的ArkUI组件、接口、资源 index.ets文件是HAR导出声明文件的入口HAR需要导出的接口统一在index.ets文件中导出。index.ets文件是DevEco Studio默认自动生成的用户也可以自定义在模块的oh-package.json5文件中的main字段配置入口声明文件配置如下所示 {main: index.ets }导出ArkUI组件 ArkUI组件的导出方式与ts的导出方式一致通过export导出ArkUI组件示例如下 // library/src/main/ets/components/MainPage/MainPage.ets Component export struct MainPage {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.width(100%)}.height(100%)} }HAR对外暴露的接口在index.ets导出文件中声明如下所示 // library/index.ets export { MainPage } from ./src/main/ets/components/MainPage/MainPage导出ts类和方法 通过export导出ts类和方法支持导出多个ts类和方法示例如下所示 // library/src/main/ts/test.ets export class Log {static info(msg) {console.info(msg);} }export function func() {return har func; }export function func2() {return har func2; }HAR对外暴露的接口在index.ets导出文件中声明如下所示 // library/index.ets export { Log } from ./src/main/ts/test export { func } from ./src/main/ts/test export { func2 } from ./src/main/ts/test资源 HAR模块编译打包时会把资源打包到HAR中。在编译构建HAP时DevEco Studio会从HAP模块及依赖的模块中收集资源文件如果不同模块下的资源文件出现重名冲突时DevEco Studio会按照以下优先级进行覆盖优先级由高到低 AppScope仅API9的Stage模型支持。HAP包自身模块。依赖的HAR模块如果依赖的多个HAR之间有资源冲突会按照依赖顺序进行覆盖依赖顺序在前的优先级较高。 引用HAR的ArkUI组件、接口、资源 引用HAR前需要先配置对HAR的依赖配置方式可参考。 引用HAR的ArkUI组件 HAR的依赖配置成功后可以引用HAR的ArkUI组件。ArkUI组件的导入方式与ts的导入方式一致通过import引入HAR导出的ArkUI组件示例如下所示 // entry/src/main/ets/pages/index.ets import { MainPage } from ohos/libraryEntry Component struct Index {State message: string Hello Worldbuild() {Row() {// 引用HAR的ArkUI组件MainPage()Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.width(100%)}.height(100%)} }引用HAR的类和方法 通过import引用HAR导出的ts类和方法示例如下所示 // entry/src/main/ets/pages/index.ets import { Log } from ohos/library import { func } from ohos/libraryEntry Component struct Index {build() {Row() {Column() {Button(Button).onClick((){// 引用HAR的类和方法Log.info(har msg);func();})}.width(100%)}.height(100%)} }引用HAR的资源 通过$r引用HAR中的资源例如在HAR模块的src/main/resources里添加字符串资源在string.json中定义namehello_har和图片资源icon_har.png然后在Entry模块中引用该字符串和图片资源的示例如下所示 // entry/src/main/ets/pages/index.ets Entry Component struct Index {build() {Row() {Column() {// 引用HAR的字符串资源Text($r(app.string.hello_har)).fontSize(50).fontWeight(FontWeight.Bold)// 引用HAR的图片资源Image($r(app.media.icon_har))}.width(100%)}.height(100%)} }HSP 应用内HSP开发指导 应用内HSP指的是专门为某一应用开发的HSP只能被该应用内部其他HAP/HSP使用用于应用内部代码、资源的共享。 应用内HSP跟随其宿主应用的APP包一起发布与该宿主应用具有相同的包名和生命周期。 开发应用内HSP HSP模块可以在DevEco Studio中由指定模板创建我们以创建一个名为library的HSP模块为例。基本的工程目录结构大致如下 library ├── src │ └── main │ ├── ets │ │ ├── pages │ │ └── index.ets │ ├── resources │ └── module.json5 └── oh-package.json5模块module.json5中的type标识模块类型HSP的type是shared。 {type: shared }HSP通过在入口文件中导出接口对外提供能力。入口文件在模块oh-package.json5的main中配置。例如 {main: ./src/main/ets/index.ets }导出ts类和方法 通过export导出ts类和方法例如 // library/src/main/ets/utils/test.ts export class Log {static info(msg) {console.info(msg);} }export function add(a: number, b: number) {return a b; }export function minus(a: number, b: number) {return a - b; }对外暴露的接口需要在入口文件index.ets中声明 // library/src/main/ets/index.ets export { Log, add, minus } from ./utils/test导出ArkUI组件 ArkUI组件也可以通过export导出例如 // library/src/main/ets/components/MyTitleBar.ets Component export struct MyTitleBar {build() {Row() {Text($r(app.string.library_title)).fontColor($r(app.color.white)).fontSize(25).margin({left:15})}.width(100%).height(50).padding({left:15}).backgroundColor(#0D9FFB)} }对外暴露的接口需要在入口文件index.ets中声明 // library/src/main/ets/index.ets export { MyTitleBar } from ./components/MyTitleBarHSP中资源使用说明 注意在HSP中通过 r / r/ r/rawfile可以使用本模块resources目录下的资源。 如果使用相对路径的方式例如 在HSP模块中使用Image(“common/example.png”)实际上该Image组件访问的是HSP调用方如entry下的资源entry/src/main/ets/common/example.png。 导出native方法 在HSP中也可以包含C编写的so。对于so中的native方法HSP通过间接的方式导出以导出libnative.so的乘法接口multi为例 // ibrary/src/main/ets/utils/nativeTest.ts import native from libnative.soexport function nativeMulti(a: number, b: number) {return native.multi(a, b); }对外暴露的接口需要在入口文件index.ets中声明 // library/src/main/ets/index.ets export { nativeMulti } from ./utils/nativeTest使用应用内HSP 要使用HSP中的接口首先需要在使用方的oh-package.json5中配置对它的依赖。如果应用内HSP和使用方在同一工程下可以直接本地引用例如 // entry/oh-package.json5 dependencies: {library: file:../library }然后就可以像使用HAR一样调用HSP的对外接口了。 例如上面的library已经导出了下面这些接口 // library/src/main/ets/index.ets export { Log, add, minus } from ./utils/test export { MyTitleBar } from ./components/MyTitleBar export { nativeMulti } from ./utils/nativeTest在使用方的代码中可以这样使用 // entry/src/main/ets/pages/index.ets import { Log, add, MyTitleBar, nativeMulti } from libraryEntry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {MyTitleBar()Text(this.message).fontSize(30).fontWeight(FontWeight.Bold)Button(add(1, 2)).onClick((){Log.info(add button click!);this.message result: add(1, 2);})Button(nativeMulti(3, 4)).onClick((){Log.info(nativeMulti button click!);this.message result: nativeMulti(3, 4);})}.width(100%)}.height(100%)} }跨包页面路由跳转 若开发者想在entry模块中添加一个按钮跳转至library模块中的menu页面路径为library/src/main/ets/pages/menu.ets那么可以在使用方的代码entry模块下的Index.ets路径为entry/src/main/ets/MainAbility/Index.ets里这样使用 import router from ohos.router;Entry Component struct Index {State message: string Hello Worldbuild() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)// 添加按钮以响应用户点击Button() {Text(click to menu).fontSize(30).fontWeight(FontWeight.Bold)}.type(ButtonType.Capsule).margin({top: 20}).backgroundColor(#0D9FFB).width(40%).height(5%)// 绑定点击事件.onClick(() {router.pushUrl({url: bundle:com.example.hmservice/library/ets/pages/menu}).then(() {console.log(push page success);}).catch(err {console.error(pushUrl failed, code is ${err.code}, message is ${err.message});})}).width(100%)}.height(100%)}} }其中router.pushUrl方法的入参中url的内容为 bundle:com.example.hmservice/library/ets/pages/menuurl内容的模板为 bundle:包名bundleName/模块名moduleName/路径/页面所在的文件名(不加.ets后缀)应用程序包快速修复 快速修复概述 快速修复是HarmonyOS系统提供给开发者的一种技术手段支持开发者以远快于应用升级的方式对应用程序包进行缺陷修复。和全量应用升级软件版本相比快速修复的主要优势在小、快和用户体验好。在较短的时间内不中断正在运行的应用的情况下即不需要重启应用修复应用的缺陷。 快速修复的使用规则 不支持新增.abc文件和.so文件。快速修复包部署时要确保对应应用包已安装如果未安装则部署失败。快速修复包中配置的包名和应用版本号必须和已安装的包名和版本号应用相同如果不同则部署失败。如果已经部署过快速修复包新部署的快速修复包的版本号必须大于之前快速修复包的版本号否则部署失败。快速修复包的签名信息和待修复的应用的签名信息必须一致否则会部署失败。新的应用版本发布安装时会清理掉快速修复包。 快速修复包结构 [外链图片转存中…(img-xOF0RPme-1704894464557)] 上图是HarmonyOS应用程序发布的快速修复的包格式 从图中可以看出包含两种包格式 appqfApplication Quick Fix appqf与应用的app pack包是一一对应关系具体可参考应用程序包结构的介绍。 appqf包是HarmonyOS应用用于发布到应用市场的单元不能够直接安装到设备上。它是由一个或多个hqfHarmony Ability Package Quick Fix组成这些hqf包在应用市场会从appqf包中拆分出来再被分发到具体的设备上。appqf包上架到应用市场前要有开发者的签名信息。 hqfHarmony Ability Package Quick Fix hqf包是修复HAP中问题的快速修复包用于安装到设备上的快速修复单元。一个hqf可以包含.abc的快速修复文件.so的快速修复文件和描述该包的配置文件。 .abc文件应用中修改后的ts代码编译后生成的字节码文件。 libs目录存放.so库文件的差分文件以.so.diff为后缀。区分的不同的系统cpu架构例如arm平台、x86平台。 patch.json 该文件用于描述hqf包版本信息的配置文件由开发者填写具体内容如下 {app : {bundleName : com.ohos.quickfix,versionCode : 1000000,versionName : 1.0.0,patchVersionCode : 1000000,patchVersionName : 1.0.0},module : {name : entry,type : patch,deviceTypes : [default,tablet],originalModuleHash : 11223344556677889900} }具体字段说明 字段类型说明备注bundleNamestring对应应用的包名不可缺省versionCodeint对应应用版本号不可缺省versionNamestring对应应用的版本名称patch类型不可缺省patchVersionCodeint补丁包的版本号不可缺省patchVersionNamestring补丁包的版本名称patch类型不可缺省namestring对应应用的moduleName用来修复该module的不可缺省typestring对应补丁包的类型当前可选择为patch不可缺省deviceTypesarray补丁包支持的设备类型不可缺省originalModuleHashstring原始module Name对应包的哈希值不可缺省 快速修复TS编译后的文件 [外链图片转存中…(img-zQPNmUN7-1704894464557)] 上图是通过TS代码编译工具生成快速修复.abc文件的流程 原始应用编译时生成.abc文件和.map文件。.abc是TS代码编译后的字节码文件应用运行时使用该文件。.map文件是通过TS代码编译工具编译TS代码时生成的中间文件记录有代码中的函数、类等信息。修复问题后的应用编译时根据上述的.map文件结合当前的TS代码得到差异部分根据差异部分生成快速修复的.abc文件。该.abc文件也既是最终要放到hqf包中的快速修复文件。 快速修复C编译后的文件 [外链图片转存中…(img-TP1yHvyN-1704894464558)] 上图是通过差分工具生成快速修复.so文件的流程 原始应用C源码通过编译工具生成.so文件该.so文件供应用在运行时使用。修复问题后的C源码通过编译工具生成.so文件该.so文件和原应用的.so文件通过差分工具生成.so快速修复文件该.so快速修复文件也既是最终要放到hqf包中的快速修复文件。 快速修复包的发布部署流程 [外链图片转存中…(img-Xr8maGTD-1704894464558)] 上图涉及到的模块如下 DevEco Studio用于开发代码的项目工程的集成开发环境。在快速修复的工程中能够给予原应用的代码和修复问题后的代码生成快速修复包并完成快速修复包的签名。应用市场服务器端开发者将开发完成的快速修复包上架到该平台平台会对上架的包进行签名验证、风险扫描和拆包重签名等然后分发到客户端。应用市场客户端用于接收应用市场服务器端分发的快速修复包并触发安装快速修复包。包管理服务设备上用于管理应用包及快速修复包安装和卸载的系统服务程序。快速修复引擎设备上用于管理应用切换使用快速修复包的系统服务程序。如果应用正在运行快速修复引擎接收到有快速修复包部署完成会通知应用切换快速修复包进而使得应用使能快速修复包。文件系统应用及快速修复包部署在设备上的位置。 上图是快速修复包的端到端发布部署流程 开发者通过DevEco Studio基于原应用的源码和修复后的源码编译打包生成快速修复包并通过DevEco Studio完成快速修复包的签名。将生成的带有签名的快速修复包上架到应用市场应用市场通过验证签名、风险扫描和拆包重签名后进行分发。设备侧的应用市场客户端检测到应用市场服务器端有新上架的快速修复包会下载最新版本的快速修复包接着通过系统中的包管理服务来安装部署快速修复包。快速修复包部署完成后再由快速修复引擎触发应用使用快速修复包进而保证用户使用到问题修复后的功能。 快速修复包的调试流程 [外链图片转存中…(img-Tq4RM0DG-1704894464559)] DevEco Studio中暂时还没有集成快速修复的能力。当前阶段HarmonyOS为开发者提供了命令行的调试开发工具可供使用具体的调试开发流程如下 基于原应用的源码和修复后的源码通过命令行工具可以编译生成快速修复包并通过命令行签名工具完成对快速修复的包的签名。通过命令行调试开发要对.hqf包签名并通过命令行工具将.hqf包安装到设备上.appqf包不能直接安装到设备上。通过快速修复的命令行工具将.hqf包安装部署到设备上。.hqf包安装部署完成后回调通知快速修复引擎触发应用使用快速修复包进而保证用户使用到问题修复后的功能。 快速修复命令行调试开发指导 当前阶段HarmonyOS为开发者提供了命令行的调试开发工具可供使用。比如包名为com.ohos.quickfix的示例应用版本号为1000000。该应用的当前版本运行中有某问题需要修复此时开发者可参考如下指导使用快速修复能力解决应用问题。 编写配置文件patch.json 目前DevEco Studio中还不支持patch.json的配置因此开发者可根据项目需要编写好该文件后放到的项目任意目录方便后续打包工具能够找到该文件即可。在本地新建一个patch.json文件配置编写示例如下 {app : {bundleName : com.ohos.quickfix,versionCode : 1000000, // 应用版本号versionName : 1.0.0.1,patchVersionCode : 1000000, // 补丁版本号patchVersionName : 1.0.0.1},module : {name : entry,type : patch,deviceTypes : [default,tablet],originalModuleHash : 11223344556677889900 // 待修复hap包的sha256值} }生成快速修复文件 快速修复TS文件的代码 在DevEco Studio中修改TS文件后编译HAP可以在工程目录下找到对应的abc文件如build\default\cache\default\LegacyCompileETS\jsbundle\temporary\pages\index.abc。 快速修复C中的代码 在DevEco Studio中编译原C的代码生成.so文件修复原C的代码编译生成新的.so文件。可以在工程目录中找到该so如build\default\intermediates\libs\default\arm64-v8a\libentry.so。 在本地HarmonyOS SDK路径的toolchains文件夹下查看diff.exe。通过该工具基于新.so文件和旧的.so文件生成.so的快速修复文件命令如下 $ diff.exe -s Example.z.so -d Example.z.so -p Example.z.so.diff命令行参数含义 -s旧so的路径 -d新so的路径 -p生成的差分文件的路径 生成.hqf的快速修复包 基于上述的patch.json、.abc快速修复文件和.so快速修复文件可以通过本地HarmonyOS SDK路径的toolchains文件夹下的app_packing_tool.jar生成.hqf包执行打包命令如下 $ java -jar app_packing_tool.jar --mode hqf --json-path patch.json --lib-path libs --ets-patch patchs --out-path entry-default-unsigned.hqf --force true命令行参数介绍 命令说明备注mode模式必选json-pathpatch.json路径必选lib-path.so快速修复文件路径该路径可参考快速修复包结构可选ets-path.abc快速修复文件路径可选 快速修复包的签名 签名与hap签名相同将上述生成的entry-default-unsigned.hqf包通过签名工具进行签名。可以使用本地HarmonyOS SDK路径的toolchains文件夹下的hap-sign-tool.jar命令如下 $ java -jar hap-sign-tool.jar sign-app -keyAlias HarmonyOS Application Release -signAlg SHA256withECDSA -mode localSign -appCertFile HarmonyOSApplication.pem -profileFile ohos_provision_release.p7b -inFile entry-default-unsigned.hqf -keystoreFile HarmonyOS.p12 -outFile entry-signed-release.hqf -keyPwd 123456 -keystorePwd 123456安装快速修复包 将上述entry-signed-release.hqf包推送到设备上 hdc.exe file send .\entry-signed-release.hqf /data/在设备上通过下述命令行安装补丁包 $ bm quickfix -a -f /data/entry-signed-release.hqf快速修复相关完整命令行参考如下 $ bm quickfix -h usage: bm quickfix options options list: -h, --help list available commands -q, --query indicates query quickfix, used with -b or --bundle-name -b, --bundle-name bundle-name query quickfix status and information by a specified bundle name -a, --apply indicates apply quickfix, used with -f or --file-path -f, --file-path file-path apply a quickfix file by a specified path -f, --file-path file-path file-path ... apply some quickfix files of one bundle -f, --file-path bundle-direction apply quickfix files by direction, under which are quickfix files应用配置文件Stage模型 应用配置文件概述Stage模型 每个应用项目必须在项目的代码目录下加入配置文件这些配置文件会向编译工具、操作系统和应用市场提供应用的基本信息。 在基于Stage模型开发的应用项目代码下都存在一个app.json5及一个或多个module.json5这两种配置文件。 app.json5主要包含以下内容 应用的全局配置信息包含应用的包名、开发厂商、版本号等基本信息。特定设备类型的配置信息。 module.json5主要包含以下内容 Module的基本配置信息例如Module名称、类型、描述、支持的设备类型等基本信息。应用组件信息包含UIAbility组件和ExtensionAbility组件的描述信息。应用运行过程中所需的权限信息。 app.json5配置文件 先通过一个示例整体认识一下app.json5配置文件。 {app: {bundleName: com.application.myapplication,vendor: example,versionCode: 1000000,versionName: 1.0.0,icon: $media:app_icon,label: $string:app_name,description: $string:description_application,minAPIVersion: 9,targetAPIVersion: 9,apiReleaseType: Release,debug: false,car: {minAPIVersion: 8,}}, }app.json5配置文件包含以下标签。 表1 app.json5文件配置标签说明 属性名称含义数据类型是否可缺省bundleName标识应用的Bundle名称用于标识应用的唯一性。该标签不可缺省。标签的值命名规则 - 字符串以字母、数字、下划线和符号“.”组成。- 以字母开头。- 最小长度7个字节最大长度127个字节。推荐采用反域名形式命名如com.example.demo建议第一级为域名后缀com第二级为厂商/个人名第三级为应用名也可以多级。字符串该标签不可缺省。bundleType标识应用的Bundle类型用于区分应用或者原子化服务。该标签可选值为app和atomicService - app当前Bundle为普通应用。- atomicService当前Bundle为元服务。字符串该标签可以缺省缺省为app。debug标识应用是否可调试该标签由IDE编译构建时生成。- true可调试。- false不可调试。布尔值该标签可以缺省缺省为false。icon标识应用的图标标签值为图标资源文件的索引。字符串该标签不可缺省。label标识应用的名称标签值为字符串资源的索引。字符串该标签不可缺省。description标识应用的描述信息标签值是字符串类型最大255个字节或对描述内容的字符串资源索引。字符串该标签可缺省缺省值为空。vendor标识对应用开发厂商的描述。该标签的值是字符串类型最大255个字节。字符串该标签可以缺省缺省为空。versionCode标识应用的版本号该标签值为32位非负整数。此数字仅用于确定某个版本是否比另一个版本更新数值越大表示版本越高。开发者可以将该值设置为任何正整数但是必须确保应用的新版本都使用比旧版本更大的值。该标签不可缺省versionCode值应小于2^31次方。数值该标签不可缺省。versionName标识应用版本号的文字描述用于向用户展示。该标签仅由数字和点构成推荐采用“A.B.C.D”四段式的形式。四段式推荐的含义如下所示。第一段主版本号/Major范围0-99重大修改的版本如实现新的大功能或重大变化。第二段次版本号/Minor范围0-99表示实现较突出的特点如新功能添加或大问题修复。第三段特性版本号/Feature范围0-99标识规划的新版本特性。第四段修订版本号/Patch范围0-999表示维护版本修复bug。标签最大字节长度为127。字符串该标签不可缺省。minCompatibleVersionCode标识应用能够兼容的最低历史版本号用于跨设备兼容性判断。说明当前版本暂不支持跨设备能力。数值该标签可缺省缺省值等于versionCode标签值。minAPIVersion标识应用运行需要的SDK的API最小版本。数值由build-profile.json5中的compatibleSdkVersion生成。targetAPIVersion标识应用运行需要的API目标版本。数值由build-profile.json5中的compileSdkVersion生成。apiReleaseType标识应用运行需要的API目标版本的类型采用字符串类型表示。取值为“CanaryN”、“BetaN”或者“Release”其中N代表大于零的整数。- Canary受限发布的版本。- Beta公开发布的Beta版本。- Release公开发布的正式版本。该字段由DevEco Studio读取当前使用的SDK的Stage来生成。字符串该标签可缺省由IDE生成并覆盖。multiProjects标识当前工程是否支持多个工程的联合开发。- true当前工程支持多个工程的联合开发。- false当前工程不支持多个工程的联合开发。多工程开发可以参考文档多工程构建。布尔值可缺省缺省值为false。tablet标识对tablet设备做的特殊配置可以配置的属性字段有上文提到的minAPIVersion、distributedNotificationEnabled。如果使用该属性对tablet设备做了特殊配置则应用在tablet设备中会采用此处配置的属性值并忽略在app.json5公共区域配置的属性值。对象该标签可缺省缺省时tablet设备使用app.json5公共区域配置的属性值。tv标识对tv设备做的特殊配置可以配置的属性字段有上文提到的minAPIVersion、distributedNotificationEnabled。如果使用该属性对tv设备做了特殊配置则应用在tv设备中会采用此处配置的属性值并忽略在app.json5公共区域配置的属性值。对象该标签可缺省缺省时tv设备使用app.json5公共区域配置的属性值。wearable标识对wearable设备做的特殊配置可以配置的属性字段有上文提到的minAPIVersion、distributedNotificationEnabled。如果使用该属性对wearable设备做了特殊配置则应用在wearable设备中会采用此处配置的属性值并忽略在app.json5公共区域配置的属性值。对象该标签可缺省缺省时wearable设备使用app.json5公共区域配置的属性值。car标识对car设备做的特殊配置可以配置的属性字段有上文提到的minAPIVersion、distributedNotificationEnabled。如果使用该属性对car设备做了特殊配置则应用在car设备中会采用此处配置的属性值并忽略在app.json5公共区域配置的属性值。对象该标签可缺省缺省时car设备使用app.json5公共区域配置的属性值。default标识对default设备做的特殊配置可以配置的属性字段有上文提到的minAPIVersion、distributedNotificationEnabled。如果使用该属性对default设备做了特殊配置则应用在default设备中会采用此处配置的属性值并忽略在app.json5公共区域配置的属性值。对象该标签可缺省缺省时default设备使用app.json5公共区域配置的属性值。phone标识对phone设备做的特殊配置可以配置的属性字段有上文提到的minAPIVersion、distributedNotificationEnabled。如果使用该属性对phone设备做了特殊配置则应用在phone设备中会采用此处配置的属性值并忽略在app.json5公共区域配置的属性值。对象该标签可缺省缺省时phone设备使用app.json5公共区域配置的属性值。 module.json5配置文件 先通过一个示例整体认识一下module.json5配置文件。 {module: {name: entry,type: entry,description: $string:module_desc,mainElement: EntryAbility,deviceTypes: [default,tablet],deliveryWithInstall: true,installationFree: false,pages: $profile:main_pages,virtualMachine: ark,metadata: [{name: string,value: string,resource: $profile:distributionFilter_config}],abilities: [{name: EntryAbility,srcEntry: ./ets/entryability/EntryAbility.ts,description: $string:EntryAbility_desc,icon: $media:icon,label: $string:EntryAbility_label,startWindowIcon: $media:icon,startWindowBackground: $color:start_window_background,exported: true,skills: [{entities: [entity.system.home],actions: [ohos.want.action.home]}]}],requestPermissions: [{name: ohos.abilitydemo.permission.PROVIDER,reason: $string:reason,usedScene: {abilities: [FormAbility],when: inuse}}]} }module.json5配置文件包含以下标签。 属性名称含义数据类型是否可缺省name标识当前Module的名称标签值采用字符串表示最大长度31个字节该名称在整个应用要唯一仅支持英文字符。字符串该标签不可缺省。type标识当前Module的类型。类型有两种分别- entry应用的主模块。- feature应用的动态特性模块。字符串该标签不可缺省。srcEntry标识当前Module所对应的代码路径标签值为字符串最长为127字节。字符串该标签可缺省缺省值为空。description标识当前Module的描述信息标签值是字符串类型最长255字节或对描述内容的字符串资源索引。字符串该标签可缺省缺省值为空。process标识当前Module的进程名标签值为字符串类型最长为31个字节。如果在HAP标签下配置了process该应用的所有UIAbility、DataShareExtensionAbility、ServiceExtensionAbility都运行在该进程中。说明- 仅支持系统应用配置三方应用配置不生效。字符串可缺省缺省为app.json5文件下app标签下的bundleName。mainElement标识当前Module的入口UIAbility名称或者ExtensionAbility名称。标签最大字节长度为255。字符串该标签可缺省缺省值为空。deviceTypes标识当前Module可以运行在哪类设备上标签值采用字符串数组的表示。字符串数组该标签不可缺省可以为空值。deliveryWithInstall标识当前Module是否在用户主动安装的时候安装表示该Module对应的HAP是否跟随应用一起安装。- true主动安装时安装。- false主动安装时不安装。布尔值该标签不可缺省。installationFree标识当前Module是否支持免安装特性。- true表示支持免安装特性且符合免安装约束。- false表示不支持免安装特性。说明- 当应用的entry类型Module的该字段配置为true时该应用的feature类型的该字段也需要配置为true。- 当应用的entry类型Module的该字段配置为false时该应用的feature类型的该字段根据业务需求配置true或false。布尔值该标签不可缺省。virtualMachine标识当前Module运行的目标虚拟机类型供云端分发使用如应用市场和分发中心。该标签值为字符串。如果目标虚拟机类型为ArkTS引擎则其值为“ark版本号”。字符串该标签由IDE构建HAP的时候自动插入。pages标识当前Module的profile资源用于列举每个页面信息。该标签最大长度为255个字节。字符串在有UIAbility的场景下该标签不可缺省。metadata标识当前Module的自定义元信息标签值为数组类型只对当前Module、UIAbility、ExtensionAbility生效。对象数组该标签可缺省缺省值为空。abilities标识当前Module中UIAbility的配置信息标签值为数组类型只对当前UIAbility生效。对象该标签可缺省缺省值为空。extensionAbilities标识当前Module中ExtensionAbility的配置信息标签值为数组类型只对当前ExtensionAbility生效。对象该标签可缺省缺省值为空。requestPermissions标识当前应用运行时需向系统申请的权限集合。对象该标签可缺省缺省值为空。testRunner标识当前Module用于支持对测试框架的配置。对象该标签可缺省缺省值为空。 deviceTypes标签 设备类型枚举值说明平板tablet-智慧屏tv-智能手表wearable系统能力较丰富的手表具备电话功能。车机car-手机phone-默认设备default能够使用全部系统能力的HarmonyOS设备。说明当前phone、default两个取值效果相同即应用可以运行在手机设备上。 deviceTypes示例 {module: {name: myHapName,type: feature,deviceTypes : [tablet]} }pages标签 该标签是一个profile文件资源用于指定描述页面信息的配置文件。 {module: {// ...pages: $profile:main_pages, // 通过profile下的资源文件配置} }在开发视图的resources/base/profile下面定义配置文件main_pages.json其中文件名(main_pages)可自定义需要和前文中pages标签指定的信息对应配置文件中列举了当前应用组件中的页面信息。 属性名称含义数据类型是否可缺省src描述有关JavaScript模块中所有页面的路由信息包括页面路径和页面名称。该值是一个字符串数组其中每个元素表示一个页面第一个元素表示主页。字符串数组该标签不可缺省。window用于定义与显示窗口相关的配置。对象该标签可缺省缺省值为空。 属性名称含义数据类型是否可缺省designWidth标识页面设计基准宽度。以此为基准根据实际设备宽度来缩放元素大小。数值可缺省缺省值为720px。autoDesignWidth标识页面设计基准宽度是否自动计算。当配置为true时designWidth将会被忽略设计基准宽度由设备宽度与屏幕密度计算得出。布尔值可缺省缺省值为false。 {src: [pages/index/mainPage,pages/second/payment,pages/third/shopping_cart,pages/four/owner] }metadata标签 该标签标识HAP的自定义元信息标签值为数组类型包含namevalueresource三个子标签。 属性名称含义数据类型是否可缺省name该标签标识数据项的键名称字符串类型最大长度255字节。字符串该标签可缺省缺省值为空。value该标签标识数据项的值标签值为字符串最大长度255字节。字符串该标签可缺省缺省值为空。resource该标签标识定义用户自定义数据格式标签值为标识该数据的资源的索引值。该标签最大字节长度为255字节。字符串该标签可缺省缺省值为空。 {module: {metadata: [{name: module_metadata,value: a test demo for module metadata,resource: $profile:shortcuts_config,}],abilities: [{metadata: [{name: ability_metadata,value: a test demo for ability,resource: $profile:config_file},{name: ability_metadata_2,value: a string test,resource: $profile:config_file}],}],extensionAbilities: [{metadata: [{name: extensionAbility_metadata,value: a test for extensionAbility,resource: $profile:config_file},{name: extensionAbility_metadata_2,value: a string test,resource: $profile:config_file}],}]} }abilities标签 ablities标签描述UIAbility组件的配置信息标签值为数组类型该标签下的配置只对当前UIAbility生效。 属性名称含义数据类型是否可缺省name标识当前UIAbility组件的名称该名称在整个应用要唯一标签值采用字符串表示最大长度127个字节仅支持英文字符。字符串该标签不可缺省。srcEntry该标签标识入口UIAbility的代码路径标签值为字符串最长为127字节。字符串该标签不可缺省。launchType标识当前UIAbility组件的启动模式可选标签值- multiton标准实例模式每次启动创建一个新的实例。- singleton单实例模式仅第一次启动创建新实例。- specified指定实例模式运行时由开发者决定是否创建新实例。字符串可缺省该标签缺省为“singleton”。description标识当前UIAbility组件的描述信息标签值是字符串类型最长255字节或对描述内容的资源索引要求采用资源索引方式以支持多语言。字符串该标签可缺省缺省值为空。icon标识当前UIAbility组件的图标标签值为图标资源文件的索引。字符串该标签可缺省缺省值为空。如果UIAbility被配置为MainElement该标签必须配置。label标识当前UIAbility组件对用户显示的名称标签值配置为该名称的资源索引以支持多语言。如果UIAbility被配置当前Module的mainElement时该标签必须配置且应用内唯一。字符串该标签不可缺省。permissions标识当前UIAbility组件自定义的权限信息。当其他应用访问该UIAbility时需要申请相应的权限信息。一个数组元素为一个权限名称。通常采用反向域名格式最大255字节取值为系统预定义的权限。字符串数组该标签可缺省缺省值为空。metadata标识当前UIAbility组件的元信息。对象数组该标签可缺省缺省值为空。exported标识当前UIAbility组件是否可以被其他应用调用。- true表示可以被其他应用调用。- false表示不可以被其他应用调用。布尔值该标签可缺省缺省值为false。continuable标识当前UIAbility组件是否可以迁移。- true表示可以被迁移。- false表示不可以被迁移。说明当前版本暂不支持跨设备能力。布尔值该标签可缺省缺省值为false。skills标识当前UIAbility组件或ExtensionAbility组件能够接收的Want的特征集为数组格式。配置规则- 对于Entry类型的HAPHarmonyOS应用可以配置多个具有入口能力的skills标签即配置了ohos.want.action.home和entity.system.home。- 对于Feature类型的HAP只有HarmonyOS应用可以配置具有入口能力的skills标签HarmonyOS服务不允许配置。对象数组该标签可缺省缺省值为空。backgroundModes标识当前UIAbility组件的长时任务集合。指定用于满足特定类型的长时任务。长时任务类型有如下- dataTransfer通过网络/对端设备进行数据下载、备份、分享、传输等业务。- audioPlayback音频输出业务。- audioRecording音频输入业务。- location定位、导航业务。- bluetoothInteraction蓝牙扫描、连接、传输业务穿戴。- multiDeviceConnection多设备互联业务。- wifiInteractionWi-Fi扫描、连接、传输业务克隆多屏。- voip音视频电话VoIP业务。- taskKeeping计算业务。字符串数组该标签可缺省缺省值为空。startWindowIcon标识当前UIAbility组件启动页面图标资源文件的索引。取值示例$media:icon。该标签最大字节长度为255。字符串不可缺省。startWindowBackground标识当前UIAbility组件启动页面背景颜色资源文件的索引。取值示例$color:red。该标签最大字节长度为255。字符串不可缺省。removeMissionAfterTerminate标识当前UIAbility组件销毁后是否从任务列表中移除任务为布尔类型- true表示销毁后移除任务。- false表示销毁后不移除任务。布尔值该标签可缺省缺省值为false。orientation标识当前UIAbility组件启动时的方向。该方向的取值范围包括- unspecified未指定方向由系统自动判断显示方向。- landscape横屏。- portrait竖屏。- landscape_inverted反向横屏。- portrait_inverted反向竖屏。- auto_rotation随传感器旋转。- auto_rotation_landscape传感器横屏旋转包括了横屏和反向横屏。- auto_rotation_portrait传感器竖屏旋转包括了竖屏和反向竖屏。- auto_rotation_restricted传感器开关打开方向可随传感器旋转。- auto_rotation_landscape_restricted传感器开关打开方向可随传感器旋转为横屏 包括了横屏和反向横屏。- auto_rotation_portrait_restricted传感器开关打开方向随可传感器旋转为竖屏 包括了横屏和反向横屏。- locked传感器开关关闭方向锁定。字符串该标签可缺省缺省值为unspecified。supportWindowMode标识当前UIAbility组件所支持的窗口模式包含- fullscreen全屏模式。- split分屏模式。- floating悬浮窗模式。字符串数组该标签可缺省缺省值为[“fullscreen”, “split”, “floating”]。priority标识当前UIAbility组件的优先级仅支持系统应用配置三方应用配置不生效。隐式查询时优先级越高UIAbility在返回列表越靠前。该标签取值为integer类型取值范围0-10。数值越大优先级越高。数值该标签可缺省缺省值为0。maxWindowRatio标识当前UIAbility组件支持的最大的宽高比。该标签最小取值为0。数值该标签可缺省缺省值为平台支持的最大的宽高比。minWindowRatio标识当前UIAbility组件支持的最小的宽高比。该标签最小取值为0。数值该标签可缺省缺省值为平台支持的最小的宽高比。maxWindowWidth标识当前UIAbility组件支持的最大的窗口宽度宽度单位为vp。该标签最小取值为0。数值该标签可缺省缺省值为平台支持的最大的窗口宽度。minWindowWidth标识当前UIAbility组件支持的最小的窗口宽度, 宽度单位为vp。该标签最小取值为0。数值该标签可缺省缺省值为平台支持的最小的窗口宽度。maxWindowHeight标识当前UIAbility组件支持的最大的窗口高度, 高度单位为vp。该标签最小取值为0。数值该标签可缺省缺省值为平台支持的最大的窗口高度。minWindowHeight标识当前UIAbility组件支持的最小的窗口高度, 高度单位为vp。该标签最小取值为0。数值该标签可缺省缺省值为平台支持的最小的窗口高度。excludeFromMissions标识当前UIAbility组件是否在最近任务列表中显示。- true表示不在任务列表中显示。- false表示在任务列表中显示。说明- 仅支持系统应用配置三方应用配置不生效。布尔值该标签可缺省缺省值为false。 abilities示例 {abilities: [{name: EntryAbility,srcEntry: ./ets/entryability/EntryAbility.ts,launchType:singleton,description: $string:description_main_ability,icon: $media:icon,label: Login,permissions: [],metadata: [],exported: true,continuable: true,skills: [{actions: [ohos.want.action.home],entities: [entity.system.home],uris: []}],backgroundModes: [dataTransfer,audioPlayback,audioRecording,location,bluetoothInteraction,multiDeviceConnection,wifiInteraction,voip,taskKeeping],startWindowIcon: $media:icon,startWindowBackground: $color:red,removeMissionAfterTerminate: true,orientation: ,supportWindowMode: [fullscreen, split, floating],maxWindowRatio: 3.5,minWindowRatio: 0.5,maxWindowWidth: 2560,minWindowWidth: 1400,maxWindowHeight: 300,minWindowHeight: 200,excludeFromMissions: false}] }skills标签 该标签标识UIAbility组件或者ExtensionAbility组件能够接收的Want的特征。 属性名称含义数据类型是否可缺省actions标识能够接收的Want的Action值的集合取值通常为系统预定义的action值也允许自定义。字符串数组可缺省缺省值为空。entities标识能够接收Want的Entity值的集合。字符串数组可缺省缺省值为空。uris标识与Want中URIUniform Resource Identifier相匹配的集合。对象数组可缺省缺省值为空。 属性名称含义数据类型是否可缺省scheme标识URI的协议名部分常见的有http、https、file、ftp等。字符串uris中仅配置type时可以缺省缺省值为空否则不可缺省。host标识URI的主机地址部分该字段要在schema存在时才有意义。常见的方式- 域名方式如example.com。- IP地址方式如10.10.10.1。字符串可缺省缺省值为空。port标识URI的端口部分。如http默认端口为80https默认端口是443ftp默认端口是21。该字段要在schema和host都存在时才有意义。字符串可缺省缺省值为空。path | pathStartWith | pathRegex标识URI的路径部分path、pathStartWith和pathRegex配置时三选一。path标识URI与want中的路径部分全匹配pathStartWith标识URI与want中的路径部分允许前缀匹配pathRegex标识URI与want中的路径部分允许正则匹配。该字段要在schema和host都存在时才有意义。字符串可缺省缺省值为空。type标识与Want相匹配的数据类型使用MIMEMultipurpose Internet Mail Extensions类型规范。可与schema同时配置也可以单独配置。字符串可缺省缺省值为空。 skills示例 {abilities: [{skills: [{actions: [ohos.want.action.home],entities: [entity.system.home],uris: [{scheme:http,host:example.com,port:80,path:path,type: text/*}]}]}] }extensionAbilities标签 描述extensionAbilities的配置信息标签值为数组类型该标签下的配置只对当前extensionAbilities生效。 属性名称含义数据类型是否可缺省name标识当前ExtensionAbility组件的名称标签值最大长度为127个字节该名称在整个应用要唯一。字符串该标签不可缺省。srcEntry标识当前ExtensionAbility组件所对应的代码路径标签值最大长度为127字节。字符串该标签不可缺省。description标识当前ExtensionAbility组件的描述标签值最大长度为255字节标签也可以是描述内容的资源索引用于支持多语言。字符串该标签可缺省缺省值为空。icon标识当前ExtensionAbility组件的图标标签值为资源文件的索引。如果ExtensionAbility组件被配置为MainElement该标签必须配置。字符串该标签可缺省缺省值为空。label标识当前ExtensionAbility组件对用户显示的名称标签值配置为该名称的资源索引以支持多语言。说明- 如果ExtensionAbility被配置当前Module的mainElement时该标签必须配置且应用内唯一。字符串该标签不可缺省。type标识当前ExtensionAbility组件的类型取值为- form卡片的ExtensionAbility。- workScheduler延时任务的ExtensionAbility。- inputMethod输入法的ExtensionAbility。- service后台运行的service组件。- accessibility辅助能力的ExtensionAbility。- dataShare数据共享的ExtensionAbility。- fileShare文件共享的ExtensionAbility。- staticSubscriber静态广播的ExtensionAbility。- wallpaper壁纸的ExtensionAbility。- backup数据备份的ExtensionAbility。- window该ExtensionAbility会在启动过程中创建一个window为开发者提供界面开发。开发者开发出来的界面将通过abilityComponent控件组合到其他应用的窗口中。- thumbnail获取文件缩略图的ExtensionAbility开发者可以对自定义文件类型的文件提供缩略。- preview该ExtensionAbility会将文件解析后在一个窗口中显示开发者可以通过将此窗口组合到其他应用窗口中。说明- 其中service和dataShare类型仅支持系统应用配置三方应用配置不生效。字符串该标签不可缺省。permissions标识当前ExtensionAbility组件自定义的权限信息。当其他应用访问该ExtensionAbility时需要申请相应的权限信息。一个数组元素为一个权限名称。通常采用反向域名格式最大255字节可以是系统预定义的权限也可以是该应用自定义的权限。如果是后者需与defPermissions标签中定义的某个权限的name标签值一致。字符串数组该标签可缺省缺省值为空。uri标识当前ExtensionAbility组件提供的数据URI为字符数组类型最大长度255用反向域名的格式表示。说明- 该标签在type为dataShare类型的ExtensionAbility时不可缺省。字符串该标签可缺省缺省值为空。skills标识当前ExtensionAbility组件能够接收的Want的特征集为数组格式。配置规则entry包可以配置多个具有入口能力的skills标签配置了ohos.want.action.home和entity.system.home的ExtensionAbility其中第一个配置了skills标签的ExtensionAbility中的label和icon作为HarmonyOS服务或应用的label和icon。说明- HarmonyOS服务的Feature包不能配置具有入口能力的skills标签。- HarmonyOS应用的Feature包可以配置具有入口能力的skills标签。数组该标签可缺省缺省值为空。metadata标识当前ExtensionAbility组件的元信息。对象该标签可缺省缺省值为空。exported标识当前ExtensionAbility组件是否可以被其他应用调用为布尔类型。- true表示可以被其他应用调用。- false表示不可以被其他应用调用。布尔值该标签可缺省缺省值为false。 extensionAbilities示例 {extensionAbilities: [{name: FormName,srcEntry: ./form/MyForm.ts,icon: $media:icon,label : $string:extension_name,description: $string:form_description,type: form, permissions: [ohos.abilitydemo.permission.PROVIDER],readPermission: ,writePermission: ,exported: true,uri:scheme://authority/path/query,skills: [{actions: [],entities: [],uris: []}],metadata: [{name: ohos.extension.form,resource: $profile:form_config, }]}] }requestPermissions标签 该标签标识应用运行时需向系统申请的权限集合。 说明 在requestPermissions标签中配置的权限项将在应用级别生效即该权限适用于整个应用程序。如果应用需要订阅自己发布的事件而且应用在extensionAbilities标签中的permissions字段中设置了访问该应用所需要的权限那么应用也需要在requestPermissions标签中注册相关权限才能收到该事件。 属性含义类型取值范围默认值name必须填写需要使用的权限名称。字符串自定义。无。reason可选当申请的权限为user_grant权限时此字段必填用于描述申请权限的原因。说明- 当申请的权限为user_grant权限时如果未填写该字段则不允许在应用市场上架并且需要进行多语种适配。字符串使用string类资源引用。格式为$string: ***。空。usedScene可选当申请的权限为user_grant权限时此字段必填。描述权限使用的场景由abilities和when组成。其中abilities可以配置为多个UIAbility组件when表示调用时机。说明- 默认为可选当申请的权限为user_grant权限时abilities标签必填when标签可选。abilitiesUIAbility或者ExtensionAbility名称的字符串数组when字符串abilitiesUIAbility或者ExtensionAbility组件的名称。wheninuse使用时、always始终。abilities空。when空。 requestPermissions示例 {module : {requestPermissions: [{name: ohos.abilitydemo.permission.PROVIDER,reason: $string:reason,usedScene: {abilities: [EntryFormAbility],when: inuse}}]} }shortcuts标签 shortcuts标识应用的快捷方式信息。标签值为数组最多可以配置四个快捷方式。其包含四个子标签shortcutId、label、icon、wants。 metadata中指定shortcut信息其中 name指定shortcuts的名称。使用ohos.ability.shortcuts作为shortcuts信息的标识。resource指定shortcuts信息的资源位置。 属性含义类型默认值shortcutId标识快捷方式的ID。字符串的最大长度为63字节。字符串该标签不可缺省。label标识快捷方式的标签信息即快捷方式对外显示的文字描述信息。取值可以是描述性内容也可以是标识label的资源索引。字符串最大长度为255字节。字符串该标签可缺省缺省值为空。icon标识快捷方式的图标标签值为资源文件的索引。字符串该标签可缺省缺省值为空。wants标识快捷方式内定义的目标wants信息集合每个wants可配置bundleName和abilityName两个子标签。bundleName表示快捷方式的目标Bundle名称字符串类型。abilityName表示快捷方式的目标组件名字符串类型。对象该标签可缺省缺省为空。 在/resource/base/profile/目录下配置shortcuts_config.json配置文件。 {shortcuts: [{shortcutId: id_test1,label: $string:shortcut,icon: $media:aa_icon,wants: [{bundleName: com.ohos.hello,abilityName: EntryAbility}]}] }在module.json5配置文件的abilities标签中针对需要添加快捷方式的UIAbility进行配置metadata标签使shortcut配置文件对该UIAbility生效。 {module: {// ...abilities: [{name: EntryAbility,srcEntry: ./ets/entryability/EntryAbility.ts,// ...skills: [{entities: [entity.system.home],actions: [ohos.want.action.home]}],metadata: [{name: ohos.ability.shortcuts,resource: $profile:shortcuts_config}]}]} }distributionFilter标签 该标签下的子标签均为可选字段用于定义HAP对应的细分设备规格的分发策略以便应用市场在云端分发HAP时做精准匹配。该标签需要配置在/resource/profile资源目录下在进行分发时通过deviceType与下表属性的匹配关系唯一确定一个用于分发到设备的HAP。 属性名称含义数据类型是否可缺省screenShape标识屏幕形状的支持策略。对象数组该标签可缺省缺省值为空。screenWindow标识应用运行时窗口的分辨率支持策略。该字段仅支持对轻量级智能穿戴设备进行配置。对象数组该标签可缺省缺省值为空。screenDensity标识屏幕的像素密度dpiDot Per Inch。该字段可选如果配置了该字段取值必须合法。该标签为字符串数组字符串范围如下。- sdpi表示小规模的屏幕密度Small-scale Dots per Inch适用于dpi取值为(0,120]的设备。- mdpi表示中规模的屏幕密度Medium-scale Dots Per Inch适用于dpi取值为(120,160]的设备。- ldpi表示大规模的屏幕密度Large-scale Dots Per Inch适用于dpi取值为(160,240]的设备。- xldpi表示大规模的屏幕密度Extra Large-scale Dots Per Inch适用于dpi取值为(240,320]的设备。- xxldpi表示大规模的屏幕密度Extra Extra Large-scale Dots Per Inch适用于dpi取值为(320480]的设备。- xxxldpi表示大规模的屏幕密度Extra Extra Extra Large-scale Dots Per Inch适用于dpi取值为(480, 640]的设备。对象数组该标签可缺省缺省值为空。countryCode表示应用需要分发的国家地区码具体值以ISO-3166-1标准为准。支持多个国家和地区枚举定义。对象数组该标签可缺省缺省值为空。 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示需要排除的value属性。- include表示需要包含的value属性。字符串该标签不可缺省。value支持的取值为circle圆形、rect矩形。场景示例针对智能穿戴设备可为圆形表盘和矩形表盘分别提供不同的HAP。字符串数组该标签不可缺省。 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示该字段取值不包含value枚举值匹配规则的匹配该属性。- include表示该字段取值满足value枚举值匹配规则的匹配该属性。字符串该标签不可缺省。value单个字符串的取值格式为“宽 * 高”取值为整数像素值例如“454 * 454”。字符串数组该标签不可缺省。 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示需要排除的value属性。- include表示需要包含的value属性。字符串该标签不可缺省。value该标签标识屏幕的像素密度dpi :Dot Per Inch。字符串数组该标签不可缺省。 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示需要排除的value属性。- include表示需要包含的value属性。字符串该标签不可缺省。value标识应用需要分发的国家地区码。字符串数组该标签不可缺省。 在开发视图的resources/base/profile下面定义配置文件distro_filter_config.json文件名可以自定义。 {distributionFilter: {screenShape: {policy: include,value: [circle,rect]},screenWindow: {policy: include,value: [454*454,466*466]},screenDensity: {policy: exclude,value: [ldpi,xldpi]},countryCode: { // 支持中国和香港地区分发policy: include,value: [CN,HK]}} }在module.json5配置文件的module标签中定义metadata信息。 {module: {// ...metadata: [{name: ohos.module.distro,resource: $profile:distro_filter_config,}]} }testRunner标签 此标签用于支持对测试框架的配置。 属性名称含义数据类型是否可缺省name标识测试框架对象名称。该标签最大字节长度为255个字节。字符串不可缺省。srcPath标识测试框架代码路径。该标签最大字节长度为255个字节。字符串不可缺省。 testRunner标签示例 {module: {// ...testRunner: {name: myTestRunnerName,srcPath: etc/test/TestRunner.ts}} }应用配置文件FA模型 应用配置文件概述FA模型 每个应用项目必须在项目的代码目录下加入配置文件这些配置文件会向HarmonyOS的编译工具、HarmonyOS操作系统和应用市场提供描述应用的基本信息。 应用配置文件需申明以下内容 应用的软件包名称应用的开发厂商版本号等应用的基本配置信息这些信息被要求设置在app这个字段下。应用的组件的基本信息包括所有的Ability设备类型组件的类型以及当前组件所使用的语法类型。应用在具体设备上的配置信息这些信息会影响应用在设备上的具体功能。 在FA模型的应用开发过程中需要在config.json配置文件中对应用的包结构进行声明。 配置文件的内部结构 config.json由app、deviceConfig和module三个部分组成缺一不可。 属性名称含义数据类型是否可缺省app标识应用的全局配置信息。同一个应用的不同HAP的app配置必须保持一致。对象不可缺省。deviceConfig标识应用在具体设备上的配置信息。对象不可缺省。module标识HAP的配置信息。该标签下的配置只对当前HAP生效。对象不可缺省。 config.json示例 {app: {vendor: example,bundleName: com.example.demo,version: {code: 1000000,name: 1.0.0}},deviceConfig: {},module: {mainAbility: .MainAbility_entry,deviceType: [tablet],commonEvents: [{name: .MainAbility,permission: ohos.permission.GET_BUNDLE_INFO,data: [com.example.demo,100],events: [install,update]}],abilities: [{skills: [{entities: [entity.system.home],actions: [action.system.home]}],orientation: unspecified,visible: true,srcPath: MainAbility_entry,name: .MainAbility_entry,srcLanguage: ets,icon: $media:icon,// $string:MainAbility_entry_desc为资源索引description: $string:MainAbility_entry_desc,formsEnabled: false,// $string:MainAbility_entry_label为资源索引label: $string:MainAbility_entry_label,type: page,launchType: standard}],distro: {moduleType: entry,installationFree: false,deliveryWithInstall: true,moduleName: myapplication},package: com.example.myapplication,srcPath: ,name: .myapplication,js: [{mode: {syntax: ets,type: pageAbility},pages: [pages/index],name: .MainAbility_entry,window: {designWidth: 720,autoDesignWidth: false}}]} }app对象内部结构 app对象包含应用全局配置信息内部结构如下 表1 app对象内部结构说明 属性名称含义数据类型是否可缺省bundleName标识应用的Bundle名称用于标识应用的唯一性。Bundle名称是由字母、数字、下划线_和点号.组成的字符串必须以字母开头。支持的字符串长度为7~127字节。Bundle名称通常采用反向域名形式表示例如“com.example.myapplication”。建议第一级为域名后缀com第二级为厂商/个人名也可以采用多级。字符串不可缺省。vendor标识对应用开发厂商的描述。字符串长度不超过255字节。字符串可缺省缺省值为空。version标识应用的版本信息。对象不可缺省。apiVersion标识应用程序所依赖的HarmonyOS API版本。对象可缺省缺省值为空。smartWindowSize标识应用在模拟器中运行时使用的屏幕尺寸。字符串可缺省缺省值为空。smartWindowDeviceType标识应用在模拟器中运行时可以模拟的设备。字符串数组可缺省缺省值为空。 version对象内部结构 表2 version对象内部结构说明 属性名称含义数据类型是否可缺省name标识应用的版本号用于向应用的终端用户呈现。取值可以自定义长度不超过127字节。自定义规则如下API5及更早的版本推荐使用三段数字版本号也兼容两段式版本号如A.B.C(也兼容A.B)其中A、B、C取值为0-999范围内的整数。除此之外不支持其他格式。A段一般表示主版本号(Major)。B段一般表示次版本号(Minor)。C段一般表示修订版本号(Patch)。API6版本起推荐采用四段式数字版本号如A.B.C.D其中A、B、C取值为0-99范围内的整数D的取值为0-999范围内的整数。A段一般表示主版本号(Major)。B段一般表示次版本号(Minor)。C段一般表示特性版本号(Feature)。D段一般表示修订版本号(Patch)。数值不可缺省。code标识应用的版本号仅用于HarmonyOS管理该应用不对应用的终端用户呈现。取值规则如下API5及更早版本二进制32位以内的非负整数需要从version.name的值转换得到。转换规则为code值A * 1,000,000 B * 1,000 C例如version.name字段取值为2.2.1则code值为2002001。API6版本起code的取值不与version.name字段的取值关联开发者可自定义code取值取值范围为2^31以内的非负整数但是每次应用版本的更新均需要更新code字段的值新版本code取值必须大于旧版本code的值。数值不可缺省。minCompatibleVersionCode标识应用可兼容的最低版本号用于跨设备场景下判断其他设备上该应用的版本是否兼容。格式与version.code字段的格式要求相同。说明当前版本暂不支持跨设备能力。数值可缺省缺省值为code标签值。 apiVersion内部结构 表3 apiVersion内部结构说明 属性名称含义数据类型是否可缺省compatible运行应用所需要的最低API版本取值范围为0~2147483647。数值配置在build.profile中打包时由IDE填充到config.json中。target用于标识应用运行时使用的API版本取值范围为0~2147483647。数值配置在build.profile中打包时由IDE填充到config.json中。releaseType用于标识应用运行时SDK的状态。canary面向特定开发者早期预览版本不承诺质量不承诺API稳定。beta公开发布的Beta版本早期Beta版本不承诺API稳定经历若干次发布后通过Release Notes对开发者声明该Beta版本为API稳定里程碑后续版本的API冻结。release正式发布版本承诺质量API不可变更。当版本处于此状态时版本号中不呈现Stage字段。字符串配置在build.profile中打包时由IDE填充到config.json中。 app对象示例 app: {bundleName: com.example.myapplication,vendor: example,version: {code: 8,name: 8.0.1},apiVersion: {compatible: 8,target: 9,releaseType: Beta1}}deviceConfig内部结构 deviceConfig包含设备上的应用配置信息可以包含defaultphonetvcarwearable等属性。default标签内的配置适用于所有通用设备其他设备类型如果有特殊的需求则需要在该设备类型的标签下进行配置。 deviceConfig对象内部结构 表1 deviceConfig对象内部结构说明 属性名称含义数据类型是否可缺省default能够使用全部系统能力的HarmonyOS设备。对象可缺省缺省值为空。phone标识手机的应用配置信息。对象可缺省缺省值为空。tablet标识平板的应用配置信息。对象可缺省缺省值为空。tv标识智慧屏特有的应用配置信息。对象可缺省缺省值为空。car标识车机特有的应用配置信息。对象可缺省缺省值为空。wearable标识智能穿戴特有的应用配置信息。对象可缺省缺省值为空。 上表中各类设备对象的内部结构说明请见表2。 deviceConfig设备对象内部结构 表2 deviceConfig设备对象内部结构说明 属性名称含义数据类型是否可缺省process标识应用或者Ability的进程名。如果在deviceConfig标签下配置了process标签则该应用的所有Ability都运行在这个进程中。如果在abilities标签下也为某个Ability配置了process标签则该Ability就运行在这个进程中。该标签最大长度为31。字符串可缺省缺省值为空。keepAlive标识应用是否始终保持运行状态仅支持系统应用配置三方应用配置不生效。该标签为布尔类型可缺省缺省值为false如果配置为true应用将始终保持为运行状态并在系统启动的时候被系统驱动起来应用进程退出后系统也会重新启动应用进程。布尔值可缺省缺省值为false。supportBackup标识应用是否支持备份和恢复。如果配置为false则不支持为该应用执行备份或恢复操作。布尔值可缺省缺省值为false。compressNativeLibs标识libs库是否以压缩存储的方式打包到HAP。如果配置为false则libs库以不压缩的方式存储HAP在安装时无需解压libs运行时会直接从HAP内加载libs库。布尔值可缺省缺省值为true。network标识网络安全性配置。该标签允许应用通过配置文件的安全声明来自定义其网络安全无需修改应用代码。对象可缺省缺省值为空。 network对象的内部结构 表3 network对象的内部结构说明 属性名称含义数据类型是否可缺省cleartextTraffic标识是否允许应用使用明文网络流量例如明文HTTP。true允许应用使用明文流量的请求。false拒绝应用使用明文流量的请求。布尔值可缺省缺省值为false。securityConfig标识应用的网络安全配置信息。对象可缺省缺省为空。 securityConfig对象的内部结构 表4 securityConfig对象的内部结构说明 属性名称含义数据类型是否可缺省domainSettings标识自定义的网域范围的安全配置支持多层嵌套即一个domainSettings对象中允许嵌套更小网域范围的domainSettings对象。对象类型可缺省缺省为空。 domainSettings对象内部结构 表5 domainSettings对象内部结构说明 属性名称含义数据类型是否可缺省cleartextPermitted标识自定义的网域范围内是否允许明文流量传输。当cleartextTraffic和security同时存在时自定义网域是否允许明文流量传输以cleartextPermitted的取值为准。true允许明文流量传输。false拒绝明文流量传输。布尔类型可缺省缺省值为空。domains标识域名配置信息包含两个参数subdomains和name。subdomains(布尔类型)表示是否包含子域名。如果为true此网域规则将与相应网域及所有子网域包括子网域的子网域匹配。否则该规则仅适用于精确匹配项。name(字符串)表示域名名称。对象数组可缺省缺省值为空。 deviceConfig示例 deviceConfig: {default: {process: com.example.test.example,supportBackup: false,network: {cleartextTraffic: true,securityConfig: {domainSettings: {cleartextPermitted: true,domains: [{subdomains: true,name: example.ohos.com}]}}}} }module对象内部结构 module对象包含HAP的配置信息。 表1 module对象内部结构说明 属性名称含义数据类型是否可缺省mainAbility服务中心图标露出的Ability常驻进程拉起时会启动mainAbility。字符串可缺省缺省值为空。package标识HAP的包结构名称在应用内保证唯一性。采用反向域名格式建议与HAP的工程目录保持一致。字符串长度为1-127个字节。字符串不可缺省。name标识HAP的类名。采用反向域名方式标识前缀要与同级的package标签指定的包名一致也可采用.开头的命名方式。字符串长度不超过255字节。字符串可缺省缺省值为空。description标识HAP的描述信息。字符串长度不超过255字节。如果字符串超出长度或者需要支持多语言可以采用资源索引的方式添加描述内容。字符串可缺省缺省值为空。supportedModes标识应用支持的运行模式当前只定义了驾驶模式drive。该标签只适用于车机。字符串数组可缺省缺省值为空。deviceType标识允许Ability运行的设备类型。系统预定义的设备类型包括phone手机、tablet(平板)、tv智慧屏、car(车机)、wearable(智能穿戴)等。字符串数组不可缺省。distro标识HAP发布的具体描述。对象不可缺省。metaData标识HAP的元信息。对象可缺省缺省值为空。abilities标识当前模块内的所有Ability。采用对象数据格式。对象数组可缺省缺省值为空。js标识基于ArkUI框架开发的JS模块集合其中的每个元素代表一个JS模块的信息。对象数组可缺省缺省值为空。shortcuts标识应用的快捷方式信息。采用对象数组格式其中的每个元素表示一个快捷方式对象。对象数组可缺省缺省值为空。reqPermissions标识应用运行时向系统申请的权限。对象数组可缺省缺省值为空。colorMode标识应用自身的颜色模式目前支持如下三种模式- dark表示按照深色模式选取资源。- light表示按照浅色模式选取资源。- auto表示跟随系统的颜色模式值选取资源。字符串可缺省缺省值为auto。distroFilter标识应用的分发规则。该标签用于定义HAP对应的细分设备规格的分发策略以便在应用市场进行云端分发应用包时做精准匹配。该标签可配置的分发策略维度包括API Version、屏幕形状、屏幕分辨率。在进行分发时通过deviceType与这三个属性的匹配关系唯一确定一个用于分发到设备的HAP。对象可缺省缺省值为空。但当应用中包含多个entry模块时必须配置该标签。commonEvents定义了公共事件静态订阅者的信息该字段中需要声明静态订阅者的名称、权限要求及订阅事件列表信息当订阅的公共事件发送时该公共事件静态订阅者将被拉起。这里的静态订阅者区分于常用的动态订阅者前者无需在业务代码中主动调用订阅事件的接口在公共事件发布时可能未被拉起而动态订阅者则在业务代码中主动调用公共事件订阅的相关API因此需要应用处于活动状态。对象数组可缺省缺省为空。entryTheme此标签标识HarmonyOS内部主题的关键字。将标记值设置为名称的资源索引。字符串可缺省缺省值为空。testRunner此标签用于支持对测试框架的配置。对象可缺省缺省值为空。 module示例 {module: {mainAbility: .EntryAbility,deviceType: [default,tablet],abilities: [{skills: [{entities: [entity.system.home],actions: [action.system.home]}],orientation: unspecified,visible: true,srcPath: EntryAbility,name: .EntryAbility,srcLanguage: ets,icon: $media:icon,description: $string:MainAbility_desc,formsEnabled: false,label: $string:MainAbility_label,type: page,launchType: standard}],distro: {moduleType: entry,installationFree: false,deliveryWithInstall: true,moduleName: entry},package: com.example.entry,srcPath: ,name: .entry,js: [{mode: {syntax: ets,type: pageAbility},pages: [pages/Index],name: .EntryAbility,window: {designWidth: 720,autoDesignWidth: false}}]} }distro对象内部结构 表2 distro对象内部结构说明 属性名称含义数据类型是否可缺省moduleName标识当前HAP的名称最大长度为31个字节。字符串不可缺省。moduleType标识当前HAP的类型包括三种类型entry、feature和har。字符串不可缺省。installationFree标识当前HAP是否支持免安装特性。true表示支持免安装特性且符合免安装约束。false表示不支持免安装特性。另外还需注意当entry.hap该字段配置为true时与该entry.hap相关的所有feature.hap该字段也需要配置为true。当entry.hap该字段配置为false时与该entry.hap相关的各feature.hap该字段可按业务需求配置true或false。布尔值不可缺省。deliveryWithInstall标识当前HAP是否在用户主动安装HAP所在应用的时候一起安装。true 安装应用时当前HAP随应用一起下载安装。false安装应用时当前HAP并不下载安装后续使用是按需下载。布尔值不可缺省。 distro示例 distro: {moduleName: ohos_entry,moduleType: entry,installationFree: true,deliveryWithInstall: true }metadata对象内部结构 表3 metadata对象内部结构说明 属性名称含义数据类型是否可缺省parameters标识调用Ability时所有调用参数的元信息。每个调用参数的元信息由以下三个标签组成description、name、type。对象数组可缺省缺省值为空results标识Ability返回值的元信息。每个返回值的元信息由以下三个标签组成description、name、type。对象数组可缺省缺省值为空。customizeData该标签标识父级组件的自定义元信息Parameters和results在application不可配。对象数组可缺省缺省值为空。 parameters对象内部结构 表4 parameters对象内部结构说明 属性名称含义数据类型是否可缺省description标识对调用参数的描述可以是表示描述内容的字符串也可以是对描述内容的资源索引以支持多语言。该标签最大长度为255个字节。字符串可缺省缺省值为空。name标识调用参数的名称。该标签最大长度为255个字节。字符串不可缺省。type标识调用参数的类型如Integer。字符串不可缺省。 results对象内部结构 表5 results对象内部结构说明 属性名称含义数据类型是否可缺省description标识对返回值的描述可以是表示描述内容的字符串也可以是对描述内容的资源索引以支持多语言。该标签最大长度为255个字节。字符串可缺省缺省值为空。name标识返回值的名字。该标签最大长度为255个字节。字符串可缺省缺省值为空。type标识返回值的类型如Integer。字符串不可缺省 customizeData对象的内部结构 表6 customizeData对象的内部结构说明 属性名称含义数据类型是否可缺省name标识数据项的键名称字符串类型最大长度255字节。字符串可缺省缺省值为空。value标识数据项的值名称字符串类型最大长度255字节。字符串可缺省缺省值为空。extra标识用户自定义数据格式标签值为标识该数据的资源的索引值。字符串可缺省缺省值为空。 metadata对象示例 metaData: {parameters : [{name : a test for metadata parameter,type : Float,// $string:parameters_description为文件资源索引值description : $string:parameters_description}],results : [{name : a test for metadata result,type : Float,description : $string:results_description}],customizeData : [{name : a customizeData,value : string,extra : $string:customizeData_description}] }deviceType标签 表7 deviceType标签配置说明 设备类型枚举值说明平板tablet-智慧屏tv-智能手表wearable系统能力较丰富的手表具备电话功能。车机car-手机phone-默认设备default能够使用全部系统能力的HarmonyOS设备。说明当前phone、default两个取值效果相同即应用可以运行在手机设备上。 abilities对象的内部结构 表8 abilities对象的内部结构说明 属性名称含义数据类型是否可缺省process运行应用程序或Ability的进程名称。如果在deviceConfig标记中配置了进程则应用程序的所有能力都在此进程中运行。您还可以为特定能力设置流程属性以便该能力可以在此流程中运行。如果此属性设置为与其他应用程序相同的进程名称则所有这些应用程序可以在同一进程中运行前提是他们具有相同的联合用户ID和相同的签名。该标签最大字节数为31个字节。字符串可缺省缺省值为空。name标识Ability名称。取值可采用反向域名方式表示由包名和类名组成如com.example.myapplication.EntryAbility也可采用.“开头的类名方式表示如”.EntryAbility。Ability的名称需在一个应用的范围内保证唯一。说明在使用DevEco Studio新建项目时默认生成首个Ability的配置即config.json中EntryAbility的配置。如使用其他IDE工具可自定义名称。该标签最大长度为127个字节。字符串不可缺省description标识对Ability的描述。取值可以是描述性内容也可以是对描述性内容的资源索引以支持多语言。该标签最大长度为255个字节。字符串可缺省缺省值为空。icon标识Ability图标资源文件的索引。取值示例$media:ability_icon。如果在该Ability的skills属性中actions的取值包含 “action.system.home”entities取值中包含entity.system.home则该Ability的icon将同时作为应用的icon。如果存在多个符合条件的Ability则取位置靠前的Ability的icon作为应用的icon。说明应用的icon和label是用户可感知配置项需要区别于当前所有已有的应用icon或label至少有一个不同。字符串可缺省缺省值为空。label标识Ability对用户显示的名称。取值可以是Ability名称也可以是对该名称的资源索引以支持多语言。如果在该Ability的skills属性中actions的取值包含 “action.system.home”entities取值中包含entity.system.home则该Ability的label将同时作为应用的label。如果存在多个符合条件的Ability则取位置靠前的Ability的label作为应用的label。说明 应用的icon和label是用户可感知配置项需要区别于当前所有已有的应用icon或label至少有一个不同。该标签为资源文件中定义的字符串的引用或以{}包括的字符串。该标签最大长度为255个字节。字符串可缺省缺省值为空。uri标识Ability的统一资源标识符。该标签最大长度为255个字节。字符串可缺省对于data类型的Ability不可缺省。launchType标识Ability的启动模式支持standard和singleton两种模式standard表示该Ability可以有多实例。该模式适用于大多数应用场景。singleton表示该Ability在所有任务栈中仅可以有一个实例。例如具有全局唯一性的呼叫来电界面即采用singleton模式。该标签仅适用于默认设备、平板、智慧屏、车机、智能穿戴。字符串可缺省缺省值为singleton。visible标识Ability是否可以被其他应用调用。true可以被其他应用调用。false不能被其他应用调用。布尔类型可缺省缺省值为false。permissions标识其他应用的Ability调用此Ability时需要申请的权限集合一个数组元素为一个权限名称。通常采用反向域名格式最大255字节取值为系统预定义的权限。字符串数组可缺省缺省值为空。skills标识Ability能够接收的want的特征。对象数组可缺省缺省值为空。deviceCapability标识Ability运行时要求设备具有的能力采用字符串数组的格式表示。该标签为数组支持最多配置512个元素单个元素最大字节长度为64。字符串数组可缺省缺省值为空。metaData元数据。对象可缺省缺省值为空。type标识Ability的类型。取值范围如下page表示基于Page模板开发的FA用于提供与用户交互的能力。service表示基于Service模板开发的PA用于提供后台运行任务的能力。data表示基于Data模板开发的PA用于对外部提供统一的数据访问对象。CA表示支持其他应用以窗口方式调起该Ability。字符串不可缺省。orientation标识该Ability的显示模式。该标签仅适用于page类型的Ability。取值范围如下unspecified由系统自动判断显示方向。landscape横屏模式。portrait竖屏模式。followRecent跟随栈中最近的应用。字符串可缺省缺省值为unspecified。backgroundModes标识后台服务的类型可以为一个服务配置多个后台服务类型。该标签仅适用于service类型的Ability。取值范围如下dataTransfer通过网络/对端设备进行数据下载、备份、分享、传输等业务。audioPlayback音频输出业务。audioRecording音频输入业务。pictureInPicture画中画、小窗口播放视频业务。voip音视频电话、VOIP业务。location定位、导航业务。bluetoothInteraction蓝牙扫描、连接、传输业务。wifiInteractionWLAN扫描、连接、传输业务。screenFetch录屏、截屏业务。multiDeviceConnection多设备互联业务字符串数组可缺省缺省值为空。grantPermission指定是否可以向Ability内任何数据授予权限。布尔值可缺省缺省值为空。readPermission标识读取Ability的数据所需的权限。该标签仅适用于data类型的Ability。取值为长度不超过255字节的字符串。该标签仅适用于默认设备、平板、智慧屏、车机、智能穿戴。字符串可缺省缺省为空。writePermission标识向Ability写数据所需的权限。该标签仅适用于data类型的Ability。取值为长度不超过255字节的字符串。字符串可缺省缺省为空。configChanges标识Ability关注的系统配置集合。当已关注的配置发生变更后Ability会收到onConfigurationUpdated回调。取值范围mcc表示IMSI移动设备国家/地区代码MCC发生变更。典型场景检测到SIM并更新MCC。mncIMSI移动设备网络代码MNC发生变更。典型场景检测到SIM并更新MNC。locale表示语言区域发生变更。典型场景用户已为设备文本的文本显示选择新的语言类型。layout表示屏幕布局发生变更。典型场景当前有不同的显示形态都处于活跃状态。fontSize表示字号发生变更。典型场景用户已设置新的全局字号。orientation表示屏幕方向发生变更。典型场景用户旋转设备。density表示显示密度发生变更。典型场景用户可能指定不同的显示比例或当前有不同的显示形态同时处于活跃状态。size显示窗口大小发生变更。smallestSize显示窗口较短边的边长发生变更。colorMode颜色模式发生变更。字符串数组可缺省缺省为空。mission标识Ability指定的任务栈。该标签仅适用于page类型的Ability。默认情况下应用中所有Ability同属一个任务栈。字符串可缺省缺省为应用的包名。targetAbility标识当前Ability重用的目标Ability。该标签仅适用于page类型的Ability。如果配置了targetAbility属性则当前Ability即别名Ability的属性中仅name、icon、label、visible、permissions、skills生效其他属性均沿用targetAbility中的属性值。目标Ability必须与别名Ability在同一应用中且在配置文件中目标Ability必须在别名之前进行声明。字符串可缺省缺省值为空。表示当前Ability不是一个别名Ability。formsEnabled标识Ability是否支持卡片forms功能。该标签仅适用于page类型的Ability。true支持卡片能力。false不支持卡片能力。布尔值可缺省缺省值为false。forms标识服务卡片的属性。该标签仅当formsEnabled为true时才能生效。对象数组可缺省缺省值为空。srcLanguageAbility开发语言的类型开发者创建工程时由开发者手动选择开发语言。字符串可缺省缺省值为“js”。srcPath该标签标识Ability对应的JS组件代码路径该标签最大长度为127字节。字符串不可缺省。uriPermission标识该Ability有权访问的应用程序数据。此属性由模式和路径子属性组成。此属性仅对类型提供者的能力有效。对象可缺省缺省值为空。startWindowIcon标识该Ability启动页面图标资源文件的索引。该标签仅适用于page类型的Ability。取值示例$media:icon。字符串可缺省缺省值为空。startWindowBackground标识该Ability启动页面背景颜色资源文件的索引。该标签仅适用于page类型的Ability。取值示例$color:red。字符串可缺省缺省值为空。removeMissionAfterTerminate该标签标识Ability销毁后是否从任务列表中移除任务。该标签仅适用于page类型的Ability。true表示销毁后移除任务 false表示销毁后不移除任务。布尔值可缺省缺省值为false。 uriPermission对象的内部结构 表9 uriPermission对象的内部结构说明 属性名称含义数据类型是否可缺省pathuriPermission标识的路径该标签最大字节长度为255个字节。字符串不可缺省。modeuriPermission的匹配模式。字符串可缺省缺省值为default。 abilities示例 abilities: [{name: .EntryAbility,description: test main ability,// $media:ic_launcher 为媒体类资源icon: $media:ic_launcher,// $string:example 为字符串类资源label: $string:example,launchType: standard,orientation: unspecified,permissions: [], visible: true,skills: [{actions: [action.system.home],entities: [entity.system.home]}],configChanges: [locale, layout, fontSize, orientation], type: page,startWindowIcon: $media:icon,startWindowBackground: $color:red,removeMissionAfterTerminate: true},{name: .PlayService,description: example play ability,icon: $media:ic_launcher,label: $string:example,launchType: standard,orientation: unspecified,visible: false,skills: [{actions: [action.play.music,action.stop.music],entities: [entity.audio]}],type: service,backgroundModes: [audioPlayback]},{name: .UserADataAbility,type: data,uri: dataability://com.example.world.test.UserADataAbility,visible: true} ]skills对象的内部结构 表10 skills对象的内部结构说明 属性名称含义数据类型是否可缺省actions标识能够接收的want的action值可以包含一个或多个action。取值通常为系统预定义的action值。字符串数组可缺省缺省值为空。entities标识能够接收的want的Ability的类别如视频、桌面应用等可以包含一个或多个entity。字符串数组可缺省缺省值为空。uris该标签标识向want过滤器添加数据规范集合。该规范可以是只有数据类型mimeType属性可以是只有URI也可以是既有数据类型又有URI。URI由其各个部分的单独属性指定/:[ ||]。该标签可缺省缺省值为空。其中scheme字段配置为uri时必配当只设置数据类型mimeType时则scheme字段为非必配项。 对象数组可缺省缺省值为空。 uris对象的内部结构 表11 uris对象的内部结构说明 属性名称含义数据类型是否可缺省scheme标识uri的scheme值。字符串不可缺省。host标识uri的host值。字符串可缺省缺省值为空。port标识uri的port值。字符串可缺省缺省值为空。pathStartWith标识uri的pathStartWith值。字符串可缺省缺省值为空。path标识uri的path值。字符串可缺省缺省值为空。pathRegx标识uri的pathRegx值。字符串可缺省缺省值为空。type标识uri的type值。type为MIME-TYPE属性为资源的媒体类型常见的类型有audio/aactext/css等。字符串可缺省缺省值为空。 skills示例 skills: [{actions: [action.system.home], entities: [entity.system.home],uris: [{scheme: http,host: www.example.com,port: 8080,path: query/student/name,type: text/*}]} ]reqPermissions权限申请 表12 reqPermissions权限申请字段说明 属性名称含义数据类型是否可缺省name需要使用的权限名称。字符串否reason描述申请权限的原因。需要做多语种适配。字符串分情况当申请的权限为user_grant时必须填写此字段否则不允许在应用市场上架其他权限可缺省缺省为空usedScene描述权限使用的场景和时机。场景类型如下两种- abilityability的名称可配置多个。- when调用时机可填的值有inuse使用时、always始终。对象可缺省缺省值为空。when可缺省缺省值为inuse usedScene对象内部结构 表13 usedScene对象内部结构说明 属性名称含义数据类型是否可缺省ability标识哪些Ability需要此权限里面配置Ability的名称。字符串数组可以缺省缺省表示所有Ability都需要此权限。when标识此权限的使用时间inuse: 使用时需要此权限。always: 所有时间都需要此权限。枚举值可缺省缺省值为空。 js对象的内部结构 表14 js对象的内部结构说明 属性名称含义数据类型是否可缺省name标识JS Component的名字。该标签不可缺省默认值为default。字符串不可缺省。pages标识JS Component的页面用于列举JS Component中每个页面的路由信息[页面路径页面名称]。该标签不可缺省取值为数组数组第一个元素代表JS FA首页。字符串数组不可缺省。window用于定义与显示窗口相关的配置。对象可缺省缺省值见表15。type标识JS应用的类型。取值范围如下normal标识该JS Component为应用实例。form标识该JS Component为卡片实例。字符串可缺省缺省值为normal。mode定义JS组件的开发模式。对象可缺省缺省值为空。 window对象的内部结构 表15 window对象的内部结构说明 属性名称含义数据类型是否可缺省designWidth标识页面设计基准宽度。以此为基准根据实际设备宽度来缩放元素大小。数值可缺省缺省值为720px。autoDesignWidth标识页面设计基准宽度是否自动计算。当配置为true时designWidth将会被忽略设计基准宽度由设备宽度与屏幕密度计算得出。布尔值可缺省缺省值为false。 mode对象的内部结构 表16 mode对象的内部结构说明 属性名称含义数据类型是否可缺省type定义JS组件的功能类型。字符串取值为pageAbility、“form”可缺省缺省值为pageAbility。syntax定义JS组件的语法类型。字符串取值为hml“ets”可缺省默认值为hml。 js示例 js: [{name: default, pages: [ pages/index/index,pages/detail/detail], window: {designWidth: 720,autoDesignWidth: false},type: form} ]shortcuts对象的内部结构 表17 shortcuts对象的内部结构说明 属性名称含义数据类型是否可缺省shortcutId标识快捷方式的ID。字符串的最大长度为63字节。字符串不可缺省。label标识快捷方式的标签信息即快捷方式对外显示的文字描述信息。取值可以是描述性内容也可以是标识label的资源索引。字符串最大长度为63字节。字符串可缺省缺省为空。icon标识快捷方式的图标信息。取值为表示icon的资源索引。字符串可缺省缺省为空。intents标识快捷方式内定义的目标intent信息集合每个intent可配置两个子标签targetClass, targetBundle。对象数组可缺省缺省为空。 intents对象的内部结构 表18 intents对象的内部结构说明 属性名称含义数据类型是否可缺省targetClass标识快捷方式目标类名。字符串可缺省缺省值为空。targetBundle标识快捷方式目标Ability所在应用的包名。字符串可缺省缺省值为空。 shortcuts示例 shortcuts: [{shortcutId: id,// $string:shortcut 为配置的字符串资源值label: $string:shortcut,intents: [{targetBundle: com.example.world.test,targetClass: com.example.world.test.entry.EntryAbility}]} ]forms对象的内部结构 表19 forms对象的内部结构说明 属性名称含义数据类型是否可缺省name标识卡片的类名。字符串最大长度为127字节。字符串不可缺省。description标识卡片的描述。取值可以是描述性内容也可以是对描述性内容的资源索引以支持多语言。字符串最大长度为255字节。字符串可缺省缺省为空。isDefault标识该卡片是否为默认卡片每个Ability有且只有一个默认卡片。true默认卡片。false非默认卡片。布尔值不可缺省。type标识卡片的类型。取值范围如下JSJS卡片。字符串不可缺省。colorMode标识卡片的主题样式取值范围如下auto自适应。dark深色主题。light浅色主题。字符串可缺省缺省值为auto。supportDimensions标识卡片支持的外观规格取值范围1 * 2表示1行2列的二宫格。2 * 1表示2行1列的二宫格。2 * 2表示2行2列的四宫格。2 * 4表示2行4列的八宫格。4 * 4表示4行4列的十六宫格。字符串数组不可缺省。defaultDimension标识卡片的默认外观规格取值必须在该卡片supportDimensions配置的列表中。字符串不可缺省。updateEnabled标识卡片是否支持周期性刷新取值范围true表示支持周期性刷新可以在定时刷新updateDuration和定点刷新scheduledUpdateTime两种方式任选其一优先选择定时刷新。false表示不支持周期性刷新。布尔类型不可缺省。scheduledUpdateTime标识卡片的定点刷新的时刻采用24小时制精确到分钟。字符串可缺省缺省值为0:0。updateDuration标识卡片定时刷新的更新周期单位为30分钟取值为自然数。当取值为0时表示该参数不生效。当取值为正整数N时表示刷新周期为30*N分钟。数值可缺省缺省值为0。formConfigAbility标识用于调整卡片的设施或活动的名称。字符串可缺省缺省值为空。jsComponentName标识JS卡片的Component名称。字符串最大长度为127字节。仅当卡片类型为JS卡片时需要配置该标签。字符串不可缺省。metaData标识卡片的自定义信息包含customizeData数组标签。对象可缺省缺省值为空。customizeData标识自定义的卡片信息。对象数组可缺省缺省值为空。 customizeData对象内部结构 表20 customizeData对象内部结构说明 属性名称含义数据类型是否可缺省name标识数据项的键名称。字符串最大长度为255字节。字符串可缺省缺省值为空。value标识数据项的值。字符串最大长度为255字节。字符串可缺省缺省值为空。extra标识当前custom数据的格式取值为表示extra的资源值。字符串可缺省缺省值为空。 forms示例 forms: [{name: Form_Js,description: Its Js Form,type: JS,jsComponentName: card,colorMode: auto,isDefault: true,updateEnabled: true,scheduledUpdateTime: 11:00,updateDuration: 1,defaultDimension: 2*2,supportDimensions: [2*2,2*4,4*4]},{name: Form_Js,description: Its JS Form,type: Js,colorMode: auto,isDefault: false,updateEnabled: true,scheduledUpdateTime: 21:05,updateDuration: 1,defaultDimension: 1*2,supportDimensions: [1*2],landscapeLayouts: [$layout:ability_form],portraitLayouts: [$layout:ability_form],formConfigAbility: ability://com.example.myapplication.fa/.EntryAbility,metaData: {customizeData: [{name: originWidgetName,value: com.example.weather.testWidget}]}} ]distroFilter对象的内部结构 表21 distroFilter对象的内部结构说明 属性名称含义数据类型是否可缺省apiVersion标识支持的apiVersion范围。对象数组可缺省缺省值为空。screenShape标识屏幕形状的支持策略。对象数组可缺省缺省值为空。screenWindow标识应用运行时窗口的分辨率支持策略。该字段仅支持对轻量级智能穿戴设备进行配置。对象数组可缺省缺省值为空。screenDensity标识屏幕的像素密度dpiDots Per Inch。对象数组可缺省缺省值为空。countryCode标识分发应用时的国家码。具体值参考ISO-3166-1的标准支持多个国家和地区的枚举定义。对象数组可缺省缺省值为空。 apiVersion对象的内部结构 表22 apiVersion对象的内部结构说明 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示需要排除的value属性。- include表示需要包含的value属性。字符串不可缺省。value支持的取值为API Version存在的整数值例如4、5、6。场景示例某应用针对相同设备型号同时在网的为使用API 5和API 6开发的两个软件版本则允许上架2个entry类型的安装包分别支持到对应设备侧软件版本的分发。数组不可缺省。 screenShape对象的内部结构 表23 screenShape对象的内部结构说明 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示需要排除的value属性。- include表示需要包含的value属性。字符串不可缺省。value支持的取值为API Version存在的整数值例如4、5、6。场景示例某应用针对相同设备型号同时在网的为使用API 5和API 6开发的两个软件版本则允许上架2个entry类型的安装包分别支持到对应设备侧软件版本的分发。数组不可缺省。 screenWindow对象的内部结构 表24 screenWindow对象的内部结构说明 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示需要排除的value属性。- include表示需要包含的value属性。字符串不可缺省。value支持的取值为API Version存在的整数值例如4、5、6。场景示例某应用针对相同设备型号同时在网的为使用API 5和API 6开发的两个软件版本则允许上架2个entry类型的安装包分别支持到对应设备侧软件版本的分发。数组不可缺省。 screenDensity对象的内部结构 表25 screenDensity对象的内部结构说明 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示需要排除的value属性。- include表示需要包含的value属性。字符串不可缺省。value取值范围如下sdpi表示小规模的屏幕密度Small-scale Dots Per Inch适用于dpi取值为0,120]的设备。mdpi表示中规模的屏幕密度(Medium-scale Dots Per Inch)适用于dpi取值为120,160]的设备。ldpi表示大规模的屏幕密度(Large-scale Dots Per Inch)适用于dpi取值为160,240]的设备。xldpi表示特大规模的屏幕密度(Extra Large-scale Dots Per Inch)适用于dpi取值为240,320]的设备。xxldpi表示超大规模的屏幕密度(Extra Extra Large-scale Dots Per Inch)适用于dpi取值为320,480]的设备。xxxldpi表示超特大规模的屏幕密度(Extra Extra Extra Large-scale Dots Per Inch)适用于dpi取值为480,640]的设备。数组不可缺省。 countryCode对象的内部结构 表26 countryCode对象的内部结构说明 属性名称含义数据类型是否可缺省policy标识该子属性取值规则。配置为“exclude”或“include”。- exclude表示需要排除的value属性。- include表示需要包含的value属性。字符串不可缺省。value该标签标识应用需要分发的国家码标签为字符串数组子串表示支持的国家或地区由两个大写字母表示。字符串数组不可缺省。 distroFilter示例 distroFilter: {apiVersion: {policy: include,value: [4,5]},screenShape: {policy: include,value: [circle,rect]},screenWindow: {policy: include,value: [454*454,466*466]},screenDensity:{policy: exclude,value: [ldpi,xldpi]},countryCode: {policy:include,value:[CN,HK]} }commonEvents对象的内部结构 表27 commonEvents对象的内部结构说明 属性名称含义数据类型是否可缺省name标识静态公共事件名称该标签最大长度为127字节。字符串不可缺省。permission此标签标识实现静态公共事件所需要申请的权限该标签最大长度为255字节。字符串可缺省缺省值为空。data标识配置当前静态公共事件要携带的附加数据数组。字符串数组可缺省缺省值为空。type该标签用于配置当前静态公共事件的分类数组。字符串数组可缺省缺省值为空。events此标签标识可接收的意图的一组事件值。一般由系统预定义也可以自定义。字符串数组不可缺省。 commonEvents示例 commonEvents: [{name: .EntryAbility,permission: ohos.permission.GET_BUNDLE_INFO,data: [com.example.demo,100],events: [install,update]} ]testRunner对象的内部结构 表28 testRunner对象的内部结构说明 属性名称含义数据类型是否可缺省name标识测试框架对象名称该标签最大长度为255字节。字符串不可缺省。srcPath标识测试框架代码路径该标签最大长度为255字节。字符串不可缺省。 testRunner: {name: myTestRunnerName,srcPath: etc/test/TestRunner.ts }definePermission仅支持系统应用配置三方应用配置不生效。 definePermissions对象内部结构 表29 definePermissions对象内部结构说明 属性名称含义数据类型是否可缺省name标识权限的名称该标签最大长度为255字节。字符串不可缺省。grantMode标识权限的授予方式支持如下两种授予模式如下- system_grant安装后系统自动授予该权限。- user_grant使用时动态申请用户授权后才可使用。字符串可缺省缺省值为system_grant。availableLevel标识权限限制类别可选值如下- system_core系统核心权限。- system_basic系统基础权限。- normal普通权限。所有应用允许申请的权限。字符串可缺省缺省值为normal。provisionEnable标识权限是否支持证书方式申请权限包括高级别的权限。配置为true标识开发者可以通过provision方式申请权限。布尔值可缺省缺省值为true。distributedSceneEnabled标识权限是否支持分布式场景下使用该权限。布尔值可缺省缺省值为false。label标识权限的简短描述配置为对描述内容的资源索引。字符串可缺省缺省值为空。description标识权限的详细描述可以是字符串最大长度为255字节或者为对描述内容的资源索引。字符串可缺省缺省值为空。 资源分类与访问 应用开发过程中经常需要用到颜色、字体、间距、图片等资源在不同的设备或配置中这些资源的值可能不同。 应用资源借助资源文件能力开发者在应用中自定义资源自行管理这些资源在不同的设备或配置中的表现。系统资源开发者直接使用系统预置的资源定义即分层参数同一资源ID在设备类型、深浅色等不同配置下有不同的取值。 资源分类 resources目录 应用开发中使用的各类资源文件需要放入特定子目录中存储管理。resources目录包括三大类目录一类为base目录一类为限定词目录还有一类为rawfile目录。stage模型多工程情况下共有的资源文件放到AppScope下的resources目录。 base目录默认存在而限定词目录需要开发者自行创建。应用使用某资源时系统会根据当前设备状态优先从相匹配的限定词目录中寻找该资源。只有当resources目录中没有与设备状态匹配的限定词目录或者在限定词目录中找不到该资源时才会去base目录中查找。rawfile是原始文件目录不会根据设备状态去匹配不同的资源。 资源目录示例 resources |---base | |---element | | |---string.json | |---media | | |---icon.png | |---profile | | |---test_profile.json |---en_US // 默认存在的目录设备语言环境是美式英文时优先匹配此目录下资源 | |---element | | |---string.json | |---media | | |---icon.png | |---profile | | |---test_profile.json |---zh_CN // 默认存在的目录设备语言环境是简体中文时优先匹配此目录下资源 | |---element | | |---string.json | |---media | | |---icon.png | |---profile | | |---test_profile.json |---en_GB-vertical-car-mdpi // 自定义多限定词目录示例由开发者创建 | |---element | | |---string.json | |---media | | |---icon.png | |---profile | | |---test_profile.json |---rawfile // 其他类型文件原始文件形式保存不会被集成到resources.index文件中。文件名可自定义。表1 resources目录分类 分类base目录限定词目录rawfile目录组织形式base目录是默认存在的目录。当应用的resources目录中没有与设备状态匹配的限定词目录时会自动引用该目录中的资源文件。base目录的二级子目录为资源组目录用于存放字符串、颜色、布尔值等基础元素以及媒体、动画、布局等资源文件具体要求参见资源组目录。en_US和zh_CN是默认存在的两个限定词目录其余限定词目录需要开发者自行创建。目录名称由一个或多个表征应用场景或设备特征的限定词组合而成具体要求参见限定词目录。限定词目录的二级子目录为资源组目录用于存放字符串、颜色、布尔值等基础元素以及媒体、动画、布局等资源文件具体要求参见资源组目录。支持创建多层子目录目录名称可以自定义文件夹内可以自由放置各类资源文件。rawfile目录的文件不会根据设备状态去匹配不同的资源。编译方式目录中的资源文件会被编译成二进制文件并赋予资源文件ID。目录中的资源文件会被编译成二进制文件并赋予资源文件ID。目录中的资源文件会被直接打包进应用不经过编译也不会被赋予资源文件ID。引用方式通过指定资源类型type和资源名称name来引用。通过指定资源类型type和资源名称name来引用。通过指定文件路径和文件名来引用。 限定词目录 限定词目录可以由一个或多个表征应用场景或设备特征的限定词组合而成包括移动国家码和移动网络码、语言、文字、国家或地区、横竖屏、设备类型、颜色模式和屏幕密度等维度限定词之间通过下划线_或者中划线-连接。开发者在创建限定词目录时需要掌握限定词目录的命名要求以及限定词目录与设备状态的匹配规则。 限定词目录的命名要求 限定词的组合顺序移动国家码_移动网络码-语言_文字_国家或地区-横竖屏-设备类型-颜色模式-屏幕密度。开发者可以根据应用的使用场景和设备特征选择其中的一类或几类限定词组成目录名称。限定词的连接方式语言、文字、国家或地区之间采用下划线连接移动国家码和移动网络码之间也采用下划线连接除此之外的其他限定词之间均采用中划线-连接。例如zh_Hant_CN、zh_CN-car-ldpi。限定词的取值范围每类限定词的取值必须符合限定词取值要求表中的条件否则将无法匹配目录中的资源文件。 表2 限定词取值要求 限定词类型含义与取值说明移动国家码和移动网络码移动国家码MCC和移动网络码MNC的值取自设备注册的网络。MCC后面可以跟随MNC使用下划线_连接也可以单独使用。例如mcc460表示中国mcc460_mnc00表示中国_中国移动。详细取值范围请查阅ITU-T E.212国际电联相关标准。语言表示设备使用的语言类型由2~3个小写字母组成。例如zh表示中文en表示英语mai表示迈蒂利语。详细取值范围请查阅ISO 639ISO制定的语言编码标准。文字表示设备使用的文字类型由1个大写字母首字母和3个小写字母组成。例如Hans表示简体中文Hant表示繁体中文。详细取值范围请查阅ISO 15924ISO制定的文字编码标准。国家或地区表示用户所在的国家或地区由2~3个大写字母或者3个数字组成。例如CN表示中国GB表示英国。详细取值范围请查阅ISO 3166-1ISO制定的国家和地区编码标准。横竖屏表示设备的屏幕方向取值如下- vertical竖屏- horizontal横屏设备类型表示设备的类型取值如下- car车机- tv智慧屏- wearable智能穿戴颜色模式表示设备的颜色模式取值如下- dark深色模式- light浅色模式屏幕密度表示设备的屏幕密度单位为dpi取值如下- sdpi表示小规模的屏幕密度Small-scale Dots Per Inch适用于dpi取值为(0, 120]的设备。- mdpi表示中规模的屏幕密度Medium-scale Dots Per Inch适用于dpi取值为(120, 160]的设备。- ldpi表示大规模的屏幕密度Large-scale Dots Per Inch适用于dpi取值为(160, 240]的设备。- xldpi表示特大规模的屏幕密度Extra Large-scale Dots Per Inch适用于dpi取值为(240, 320]的设备。- xxldpi表示超大规模的屏幕密度Extra Extra Large-scale Dots Per Inch适用于dpi取值为(320, 480]的设备。- xxxldpi表示超特大规模的屏幕密度Extra Extra Extra Large-scale Dots Per Inch适用于dpi取值为(480, 640]的设备。 限定词目录与设备状态的匹配规则 在为设备匹配对应的资源文件时限定词目录匹配的优先级从高到低依次为移动国家码和移动网络码 区域可选组合语言、语言_文字、语言_国家或地区、语言_文字_国家或地区 横竖屏 设备类型 颜色模式 屏幕密度。如果限定词目录中包含移动国家码和移动网络码、语言、文字、横竖屏、设备类型、颜色模式限定词则对应限定词的取值必须与当前的设备状态完全一致该目录才能够参与设备的资源匹配。例如限定词目录“zh_CN-car-ldpi”不能参与“en_US”设备的资源匹配。 资源组目录 base目录与限定词目录下面可以创建资源组目录包括element、media、profile用于存放特定类型的资源文件详见资源组目录说明。 表3 资源组目录说明 资源组目录目录说明资源文件element表示元素资源以下每一类数据都采用相应的JSON文件来表征目录下只支持文件类型。- boolean布尔型- color颜色- float浮点型- intarray整型数组- integer整型- pattern样式- plural复数形式- strarray字符串数组- string字符串element目录中的文件名称建议与下面的文件名保持一致。每个文件中只能包含同一类型的数据。- boolean.json- color.json- float.json- intarray.json- integer.json- pattern.json- plural.json- strarray.json- string.jsonmedia表示媒体资源包括图片、音频、视频等非文本格式的文件目录下只支持文件类型。文件名可自定义例如icon.png。profile表示自定义配置文件其文件内容可通过包管理接口获取目录下只支持文件类型。文件名可自定义例如test_profile.json。 媒体资源类型说明 表4 图片资源类型说明 格式文件后缀名JPEG.jpgPNG.pngGIF.gifSVG.svgWEBP.webpBMP.bmp 表5 音视频资源类型说明 格式支持的文件类型H.263.3gp.mp4H.264 AVCBaseline Profile (BP).3gp.mp4MPEG-4 SP.3gpVP8.webm.mkv 资源文件示例 color.json文件的内容如下 {color: [{name: color_hello,value: #ffff0000},{name: color_world,value: #ff0000ff}] }float.json文件的内容如下 {float:[{name:font_hello,value:28.0fp},{name:font_world,value:20.0fp}] }string.json文件的内容如下 {string:[{name:string_hello,value:Hello},{name:string_world,value:World},{name:message_arrive,value:We will arrive at %s.}] }plural.json文件的内容如下 {plural:[{name:eat_apple,value:[{quantity:one,value:%d apple},{quantity:other,value:%d apples}]}] }资源访问 应用资源 创建资源文件 在resources目录下可按照限定词目录和资源组目录的说明创建子目录和目录内的文件。 同时DevEco Studio也提供了创建资源目录和资源文件的界面。 创建资源目录及资源文件 在resources目录右键菜单选择“New Resource File”此时可同时创建目录和文件。文件默认创建在base目录的对应资源组下。如果选择了限定词则会按照命名规范自动生成限定词资源组目录并将文件创建在目录中。图中Avaliable qualifiers为供选择的限定词目录通过右边的小箭头可添加或者删除。File name为需要创建的文件名Resource type为资源组类型默认是element。Root Element为资源类型。创建的目录名自动生成格式固定为“限定词.资源组”例如创建一个限定词为dark的element目录自动生成的目录名称为“dark.element”。 [外链图片转存中…(img-9gFoXWGv-1704894464561)] 创建资源目录 在resources目录右键菜单选择“New Resource Directory”此时可创建资源目录。资源目录创建的是base目录也可根据需求创建其它限定词目录。确定限定词后选择资源组类型当前资源组类型支持Element、Media、Profile三种创建后自动生成目录名称。 [外链图片转存中…(img-4fdcbkLK-1704894464563)] 创建资源文件 在资源目录的右键菜单选择“New XXX Resource File”即可创建对应资源组目录的资源文件。例如在element目录下可新建Element Resource File。 [外链图片转存中…(img-OQOTGyVK-1704894464563)] 访问应用资源 在工程中通过$r(‘app.type.name’)的形式引用应用资源。app代表是应用内resources目录中定义的资源type代表资源类型或资源的存放位置可以取“color”、“float”、“string”、“plural”、“media”name代表资源命名由开发者定义资源时确定。 引用rawfile下资源时使用$rawfile(‘filename’)“的形式filename需要表示为rawfile目录下的文件相对路径文件名需要包含后缀路径开头不可以以”/开头。 访问rawfile文件的descriptor时可使用资源管理getRawFd接口其返回值descriptor.fd为hap包的fd访问此rawfile文件需要结合{fd, offset, length}一起使用。 说明 资源描述符不能拼接使用仅支持普通字符串如’app.type.name’。 $r返回值为Resource对象可通过getStringValue 方法获取对应的字符串。 在xxx.ets文件中可以使用在resources目录中定义的资源。资源分类中资源组目录下的“资源文件示例”显示了.json文件内容包含color.json文件、float.json文件、string.json和plural.json文件。应用资源的具体使用方法如下 Text($r(app.string.string_hello)).fontColor($r(app.color.color_hello)).fontSize($r(app.float.font_hello))Text($r(app.string.string_world)).fontColor($r(app.color.color_world)).fontSize($r(app.float.font_world))// 引用string.json资源。Text中$r的第一个参数指定string资源第二个参数用于替换string.json文件中的%s。 //如下示例代码value为We will arrive at five of the clock。 Text($r(app.string.message_arrive, five of the clock)).fontColor($r(app.color.color_hello)).fontSize($r(app.float.font_hello))// 引用plural$资源。Text中$r的第一个指定plural资源第二个参数用于指定单复数在中文单复数均使用other。在英文one代表单数取值为1other代表复数取值为大于等于1的整数第三个参数用于替换%d // 如下示例代码为复数value为5 apples。 Text($r(app.plural.eat_apple, 5, 5)).fontColor($r(app.color.color_world)).fontSize($r(app.float.font_world))Image($r(app.media.my_background_image)) // media资源的$r引用Image($rawfile(test.png)) // rawfile$r引用rawfile目录下图片Image($rawfile(newDir/newTest.png)) // rawfile$r引用rawfile目录下图片系统资源 系统资源包含色彩、圆角、字体、间距、字符串及图片等。通过使用系统资源不同的开发者可以开发出具有相同视觉风格的应用。 开发者可以通过“$r(‘sys.type.resource_id’)”的形式引用系统资源。sys代表是系统资源type代表资源类型可以取“color”、“float”、“string”、“media”resource_id代表资源id。 说明 仅声明式开发范式支持使用系统资源类Web开发范式不支持。 Text(Hello).fontColor($r(sys.color.ohos_id_color_emphasize)).fontSize($r(sys.float.ohos_id_text_size_headline1)).fontFamily($r(sys.string.ohos_id_text_font_family_medium)).backgroundColor($r(sys.color.ohos_id_color_palette_aux1))Image($r(sys.media.ohos_app_icon)).border({color: $r(sys.color.ohos_id_color_palette_aux1),radius: $r(sys.float.ohos_id_corner_radius_button), width: 2}).margin({top: $r(sys.float.ohos_id_elements_margin_horizontal_m),bottom: $r(sys.float.ohos_id_elements_margin_horizontal_l)}).height(200).width(300)学习ArkTS语言 初识ArkTS语言 ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript简称TS生态基础上做了进一步扩展继承了TS的所有特性是TS的超集。因此在学习ArkTS语言之前建议开发者具备TS语言开发能力。 当前ArkTS在TS的基础上主要扩展了如下能力 基本语法ArkTS定义了声明式UI描述、自定义组件和动态扩展UI元素的能力再配合ArkUI开发框架中的系统组件及其相关的事件方法、属性方法等共同构成了UI开发的主体。状态管理ArkTS提供了多维度的状态管理机制。在UI开发框架中与UI相关联的数据可以在组件内使用也可以在不同组件层级间传递比如父子组件之间、爷孙组件之间还可以在应用全局范围内传递或跨设备传递。另外从数据的传递形式来看可分为只读的单向传递和可变更的双向传递。开发者可以灵活的利用这些能力来实现数据和UI的联动。渲染控制ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据并在每次迭代过程中创建相应的组件。数据懒加载从数据源中按需迭代数据并在每次迭代过程中创建相应的组件。 未来ArkTS会结合应用开发/运行的需求持续演进逐步提供并行和并发能力增强、系统类型增强、分布式开发范式等更多特性。 基本语法 基本语法概述 在初步了解了ArkTS语言之后我们以一个具体的示例来说明ArkTS的基本组成。如下图所示当开发者点击按钮时文本内容从“Hello World”变为“Hello ArkUI”。 **图1 **示例效果图 [外链图片转存中…(img-WuX8OyCD-1704894464564)] 本示例中ArkTS的基本组成如下所示。 **图2 **ArkTS的基本组成 [外链图片转存中…(img-lIyYWTV9-1704894464565)] 装饰器 用于装饰类、结构、方法以及变量并赋予其特殊的含义。如上述示例中Entry、Component和State都是装饰器Component表示自定义组件Entry表示该自定义组件为入口组件State表示组件中的状态变量状态变量变化会触发UI刷新。UI描述以声明式的方式来描述UI的结构例如build()方法中的代码块。自定义组件可复用的UI单元可组合其他组件如上述被Component装饰的struct Hello。系统组件ArkUI框架中默认内置的基础和容器组件可直接被开发者调用比如示例中的Column、Text、Divider、Button。属性方法组件可以通过链式调用配置多项属性如fontSize()、width()、height()、backgroundColor()等。事件方法组件可以通过链式调用设置多个事件的响应逻辑如跟随在Button后面的onClick()。 除此之外ArkTS扩展了多种语法范式来使开发更加便捷 Builder/BuilderParam特殊的封装UI描述的方法细粒度的封装和复用UI描述。Extend/Style扩展内置组件和封装属性样式更灵活地组合内置组件。stateStyles多态样式可以依据组件的内部状态的不同设置不同样式。 声明式UI描述 ArkTS以声明方式组合和扩展组件来描述应用程序的UI同时还提供了基本的属性、事件和子组件配置方法帮助开发者实现应用交互逻辑。 创建组件 根据组件构造方法的不同创建组件包含有参数和无参数两种方式。 说明 创建组件时不需要new运算符。 无参数 如果组件的接口定义没有包含必选构造参数则组件后面的“()”不需要配置任何内容。例如Divider组件不包含构造参数 Column() {Text(item 1)Divider()Text(item 2) }有参数 如果组件的接口定义包含构造参数则在组件后面的“()”配置相应参数。 Image组件的必选参数src。 Image(https://xyz/test.jpg)Text组件的非必选参数content。 // string类型的参数 Text(test) // $r形式引入应用资源可应用于多语言场景 Text($r(app.string.title_value)) // 无参数形式 Text()变量或表达式也可以用于参数赋值其中表达式返回的结果类型必须满足参数类型要求。 Image(this.imagePath) Image(https:// this.imageUrl) Text(count: ${this.count})配置属性 属性方法以“.”链式调用的方式配置系统组件的样式和其他属性建议每个属性方法单独写一行。 配置Text组件的字体大小。 Text(test).fontSize(12)配置组件的多个属性。 Image(test.jpg).alt(error.jpg) .width(100) .height(100)除了直接传递常量参数外还可以传递变量或表达式。 Text(hello).fontSize(this.size) Image(test.jpg).width(this.count % 2 0 ? 100 : 200) .height(this.offset 100)对于系统组件ArkUI还为其属性预定义了一些枚举类型供开发者调用枚举类型可以作为参数传递但必须满足参数类型要求。 Text(hello).fontSize(20).fontColor(Color.Red).fontWeight(FontWeight.Bold)配置事件 事件方法以“.”链式调用的方式配置系统组件支持的事件建议每个事件方法单独写一行。 使用lambda表达式配置组件的事件方法。 Button(Click me).onClick(() {this.myText ArkUI;})使用匿名函数表达式配置组件的事件方法要求使用bind以确保函数体中的this指向当前组件。 Button(add counter).onClick(function(){this.counter 2;}.bind(this))使用组件的成员函数配置组件的事件方法。 myClickHandler(): void {this.counter 2; } ... Button(add counter).onClick(this.myClickHandler.bind(this))配置子组件 如果组件支持子组件配置则需在尾随闭包{…}中为组件添加子组件的UI描述。Column、Row、Stack、Grid、List等组件都是容器组件。 以下是简单的Column组件配置子组件的示例。 Column() {Text(Hello).fontSize(100)Divider()Text(this.myText).fontSize(100).fontColor(Color.Red) }容器组件均支持子组件配置可以实现相对复杂的多级嵌套。 Column() {Row() {Image(test1.jpg).width(100).height(100)Button(click 1).onClick(() {console.info(1 clicked!);})} }自定义组件 创建自定义组件 在ArkUI中UI显示的内容均为组件由框架直接提供的称为系统组件由开发者定义的称为自定义组件。在进行 UI 界面开发时通常不是简单的将系统组件进行组合使用而是需要考虑代码可复用性、业务逻辑与UI分离后续版本演进等因素。因此将UI和部分业务逻辑封装成自定义组件是不可或缺的能力。 自定义组件具有以下特点 可组合允许开发者组合使用系统组件、及其属性和方法。可重用自定义组件可以被其他组件重用并作为不同的实例在不同的父组件或容器中使用。数据驱动UI更新通过状态变量的改变来驱动UI的刷新。 以下示例展示了自定义组件的基本用法。 Component struct HelloComponent {State message: string Hello, World!;build() {// HelloComponent自定义组件组合系统组件Row和TextRow() {Text(this.message).onClick(() {// 状态变量message的改变驱动UI刷新UI从Hello, World!刷新为Hello, ArkUI!this.message Hello, ArkUI!;})}} }HelloComponent可以在其他自定义组件中的build()函数中多次创建实现自定义组件的重用。 Entry Component struct ParentComponent {build() {Column() {Text(ArkUI message)HelloComponent({ message: Hello, World! });Divider()HelloComponent({ message: 你好! });}} }要完全理解上面的示例需要了解自定义组件的以下概念定义本文将在后面的小节中介绍 自定义组件的基本结构成员函数/变量自定义组件的参数规定build()函数自定义组件通用样式 自定义组件的基本结构 struct自定义组件基于struct实现struct 自定义组件名 {…}的组合构成自定义组件不能有继承关系。对于struct的实例化可以省略new。 说明 自定义组件名、类名、函数名不能和系统组件名相同。 ComponentComponent装饰器仅能装饰struct关键字声明的数据结构。struct被Component装饰后具备组件化的能力需要实现build方法描述UI一个struct只能被一个Component装饰。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 Component struct MyComponent { }build()函数build()函数用于定义自定义组件的声明式UI描述自定义组件必须定义build()函数。 Component struct MyComponent {build() {} }EntryEntry装饰的自定义组件将作为UI页面的入口。在单个UI页面中最多可以使用Entry装饰一个自定义组件。Entry可以接受一个可选的 LocalStorage 的参数。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 Entry Component struct MyComponent { }成员函数/变量 自定义组件除了必须要实现build()函数外还可以实现其他成员函数成员函数具有以下约束 不支持静态函数。成员函数的访问始终是私有的。 自定义组件可以包含成员变量成员变量具有以下约束 不支持静态成员变量。所有成员变量都是私有的变量的访问规则与成员函数的访问规则相同。自定义组件的成员变量本地初始化有些是可选的有些是必选的。具体是否需要本地初始化是否需要从父组件通过参数传递初始化子组件的成员变量请参考状态管理。 自定义组件的参数规定 从上文的示例中我们已经了解到可以在build方法或者Builder装饰的函数里创建自定义组件在创建的过程中参数可以被提供给组件。 Component struct MyComponent {private countDownFrom: number 0;private color: Color Color.Blue;build() {} }Entry Component struct ParentComponent {private someColor: Color Color.Pink;build() {Column() {// 创建MyComponent实例并将创建MyComponent成员变量countDownFrom初始化为10将成员变量color初始化为this.someColorMyComponent({ countDownFrom: 10, color: this.someColor })}} }build()函数 所有声明在build()函数的语言我们统称为UI描述语言UI描述语言需要遵循以下规则 Entry装饰的自定义组件其build()函数下的根节点唯一且必要且必须为容器组件其中ForEach禁止作为根节点。 Component装饰的自定义组件其build()函数下的根节点唯一且必要可以为非容器组件其中ForEach禁止作为根节点。 Entry Component struct MyComponent {build() {// 根节点唯一且必要必须为容器组件Row() {ChildComponent() }} }Component struct ChildComponent {build() {// 根节点唯一且必要可为非容器组件Image(test.jpg)} }不允许声明本地变量反例如下。 build() {// 反例不允许声明本地变量let a: number 1; }不允许在UI描述里直接使用console.info但允许在方法或者函数里使用反例如下。 build() {// 反例不允许console.infoconsole.info(print debug log); }不允许创建本地的作用域反例如下。 build() {// 反例不允许本地作用域{...} }不允许调用除了被Builder装饰以外的方法允许系统组件的参数是TS方法的返回值。 Component struct ParentComponent {doSomeCalculations() {}calcTextValue(): string {return Hello World;}Builder doSomeRender() {Text(Hello World)}build() {Column() {// 反例不能调用没有用Builder装饰的方法this.doSomeCalculations();// 正例可以调用this.doSomeRender();// 正例参数可以为调用TS方法的返回值Text(this.calcTextValue())}} }不允许switch语法如果需要使用条件判断请使用if。反例如下。 build() {Column() {// 反例不允许使用switch语法switch (expression) {case 1:Text(...)break;case 2:Image(...)break;default:Text(...)break;}} }不允许使用表达式反例如下。 build() {Column() {// 反例不允许使用表达式(this.aVar 10) ? Text(...) : Image(...)} }自定义组件通用样式 自定义组件通过“.”链式调用的形式设置通用样式。 Component struct MyComponent2 {build() {Button(Hello World)} }Entry Component struct MyComponent {build() {Row() {MyComponent2().width(200).height(300).backgroundColor(Color.Red)}} }说明 ArkUI给自定义组件设置样式时相当于给MyComponent2套了一个不可见的容器组件而这些样式是设置在容器组件上的而非直接设置给MyComponent2的Button组件。通过渲染结果我们可以很清楚的看到背景颜色红色并没有直接生效在Button上而是生效在Button所处的开发者不可见的容器组件上。 页面和自定义组件生命周期 在开始之前我们先明确自定义组件和页面的关系 自定义组件Component装饰的UI单元可以组合多个系统组件实现UI的复用。页面即应用的UI页面。可以由一个或者多个自定义组件组成Entry装饰的自定义组件为页面的入口组件即页面的根节点一个页面有且仅能有一个Entry。只有被Entry装饰的组件才可以调用页面的生命周期。 页面生命周期即被Entry装饰的组件生命周期提供以下生命周期接口 onPageShow页面每次显示时触发。onPageHide页面每次隐藏时触发一次。onBackPress当用户点击返回按钮时触发。 组件生命周期即一般用Component装饰的自定义组件的生命周期提供以下生命周期接口 aboutToAppear组件即将出现时回调该接口具体时机为在创建自定义组件的新实例后在执行其build()函数之前执行。aboutToDisappear在自定义组件即将析构销毁时执行。 生命周期流程如下图所示下图展示的是被Entry装饰的组件首页生命周期。 [外链图片转存中…(img-XSdhN2GC-1704894464566)] 根据上面的流程图我们从自定义组件的初始创建、重新渲染和删除来详细解释。 自定义组件的创建和渲染流程 自定义组件的创建自定义组件的实例由ArkUI框架创建。 初始化自定义组件的成员变量通过本地默认值或者构造方法传递参数来初始化自定义组件的成员变量初始化顺序为成员变量的定义顺序。 如果开发者定义了aboutToAppear则执行aboutToAppear方法。 在首次渲染的时候执行build方法渲染系统组件如果子组件为自定义组件则创建自定义组件的实例。在执行build()函数的过程中框架会观察每个状态变量的读取状态将保存两个map 状态变量 - UI组件包括ForEach和if。UI组件 - 此组件的更新函数即一个lambda方法作为build()函数的子集创建对应的UI组件并执行其属性方法示意如下。 build() {...this.observeComponentCreation(() {Button.create();})this.observeComponentCreation(() {Text.create();})... }当应用在后台启动时此时应用进程并没有销毁所以仅需要执行onPageShow。 自定义组件重新渲染 当事件句柄被触发比如设置了点击事件即触发点击事件改变了状态变量时或者LocalStorage / AppStorage中的属性更改并导致绑定的状态变量更改其值时 框架观察到了变化将启动重新渲染。根据框架持有的两个map自定义组件的创建和渲染流程中第4步框架可以知道该状态变量管理了哪些UI组件以及这些UI组件对应的更新函数。执行这些UI组件的更新函数实现最小化更新。 自定义组件的删除 如果if组件的分支改变或者ForEach循环渲染中数组的个数改变组件将被删除 在删除组件之前将调用其aboutToDisappear生命周期函数标记着该节点将要被销毁。ArkUI的节点删除机制是后端节点直接从组件树上摘下后端节点被销毁对前端节点解引用当前端节点已经没有引用时将被JS虚拟机垃圾回收。自定义组件和它的变量将被删除如果其有同步的变量比如Link、Prop、StorageLink将从同步源上取消注册。 不建议在生命周期aboutToDisappear内使用async await如果在生命周期的aboutToDisappear使用异步操作Promise或者回调方法自定义组件将被保留在Promise的闭包中直到回调方法被执行完这个行为阻止了自定义组件的垃圾回收。 以下示例展示了生命周期的调用时机 // Index.ets import router from ohos.router;Entry Component struct MyComponent {State showChild: boolean true;// 只有被Entry装饰的组件才可以调用页面的生命周期onPageShow() {console.info(Index onPageShow);}// 只有被Entry装饰的组件才可以调用页面的生命周期onPageHide() {console.info(Index onPageHide);}// 只有被Entry装饰的组件才可以调用页面的生命周期onBackPress() {console.info(Index onBackPress);}// 组件生命周期aboutToAppear() {console.info(MyComponent aboutToAppear);}// 组件生命周期aboutToDisappear() {console.info(MyComponent aboutToDisappear);}build() {Column() {// this.showChild为true创建Child子组件执行Child aboutToAppearif (this.showChild) {Child()}// this.showChild为false删除Child子组件执行Child aboutToDisappearButton(create or delete Child).onClick(() {this.showChild false;})// push到Page2页面执行onPageHideButton(push to next page).onClick(() {router.pushUrl({ url: pages/Page2 });})}} }Component struct Child {State title: string Hello World;// 组件生命周期aboutToDisappear() {console.info([lifeCycle] Child aboutToDisappear)}// 组件生命周期aboutToAppear() {console.info([lifeCycle] Child aboutToAppear)}build() {Text(this.title).fontSize(50).onClick(() {this.title Hello ArkUI;})} }以上示例中Index页面包含两个自定义组件一个是被Entry装饰的MyComponent也是页面的入口组件即页面的根节点一个是Child是MyComponent的子组件。只有Entry装饰的节点才可以生效页面的生命周期方法所以MyComponent中声明了当前Index页面的页面生命周期函数。MyComponent和其子组件Child也同时也声明了组件的生命周期函数。 应用冷启动的初始化流程为MyComponent aboutToAppear -- MyComponent build -- Child aboutToAppear -- Child build -- Child build执行完毕 -- MyComponent build执行完毕 -- Index onPageShow。 点击“delete Child”if绑定的this.showChild变成false删除Child组件会执行Child aboutToDisappear方法。 点击“push to next page”调用router.pushUrl接口跳转到另外一个页面当前Index页面隐藏执行页面生命周期Index onPageHide。此处调用的是router.pushUrl接口Index页面被隐藏并没有销毁所以只调用onPageHide。跳转到新页面后执行初始化新页面的生命周期的流程。 如果调用的是router.replaceUrl则当前Index页面被销毁执行的生命周期流程将变为Index onPageHide -- MyComponent aboutToDisappear -- Child aboutToDisappear。上文已经提到组件的销毁是从组件树上直接摘下子树所以先调用父组件的aboutToDisappear再调用子组件的aboutToDisappear然后执行初始化新页面的生命周期流程。 点击返回按钮触发页面生命周期Index onBackPress且触发返回一个页面后会导致当前Index页面被销毁。 最小化应用或者应用进入后台触发Index onPageHide。当前Index页面没有被销毁所以并不会执行组件的aboutToDisappear。应用回到前台执行Index onPageShow。 退出应用执行Index onPageHide -- MyComponent aboutToDisappear -- Child aboutToDisappear。 Builder装饰器自定义构建函数 前面章节介绍了如何创建一个自定义组件。该自定义组件内部UI结构固定仅与使用方进行数据传递。ArkUI还提供了一种更轻量的UI元素复用机制BuilderBuilder所装饰的函数遵循build()函数语法规则开发者可以将重复使用的UI元素抽象成一个方法在build方法里调用。 为了简化语言我们将Builder装饰的函数也称为“自定义构建函数”。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 装饰器使用说明 自定义组件内自定义构建函数 定义的语法 Builder MyBuilderFunction({ ... })使用方法 this.MyBuilderFunction({ ... })允许在自定义组件内定义一个或多个自定义构建函数该函数被认为是该组件的私有、特殊类型的成员函数。自定义构建函数可以在所属组件的build方法和其他自定义构建函数中调用但不允许在组件外调用。在自定义函数体中this指代当前所属组件组件的状态变量可以在自定义构建函数内访问。建议通过this访问自定义组件的状态变量而不是参数传递。 全局自定义构建函数 定义的语法 Builder function MyGlobalBuilderFunction({ ... })使用方法 MyGlobalBuilderFunction()全局的自定义构建函数可以被整个应用获取不允许使用this和bind方法。如果不涉及组件状态变化建议使用全局的自定义构建方法。 参数传递规则 自定义构建函数的参数传递有按值传递和按引用传递两种均需遵守以下规则 参数的类型必须与参数声明的类型一致不允许undefined、null和返回undefined、null的表达式。在自定义构建函数内部不允许改变参数值。如果需要改变参数值且同步回调用点建议使用Link。Builder内UI语法遵循UI语法规则。 按引用传递参数 按引用传递参数时传递的参数可为状态变量且状态变量的改变会引起Builder方法内的UI刷新。ArkUI提供$$作为按引用传递参数的范式。 ABuilder( $$ : { paramA1: string, paramB1 : string } );Builder function ABuilder($$: { paramA1: string }) {Row() {Text(UseStateVarByReference: ${$$.paramA1} )} } Entry Component struct Parent {State label: string Hello;build() {Column() {// 在Parent组件中调用ABuilder的时候将this.label引用传递给ABuilderABuilder({ paramA1: this.label })Button(Click me).onClick(() {// 点击“Click me”后UI从“Hello”刷新为“ArkUI”this.label ArkUI;})}} }按值传递参数 调用Builder装饰的函数默认按值传递。当传递的参数为状态变量时状态变量的改变不会引起Builder方法内的UI刷新。所以当使用状态变量的时候推荐使用按引用传递。 Builder function ABuilder(paramA1: string) {Row() {Text(UseStateVarByValue: ${paramA1} )} } Entry Component struct Parent {label: string Hello;build() {Column() {ABuilder(this.label)}} }BuilderParam装饰器引用Builder函数 当开发者创建了自定义组件并想对该组件添加特定功能时例如在自定义组件中添加一个点击跳转操作。若直接在组件内嵌入事件方法将会导致所有引入该自定义组件的地方均增加了该功能。为解决此问题ArkUI引入了BuilderParam装饰器BuilderParam用来装饰指向Builder方法的变量开发者可在初始化自定义组件时对此属性进行赋值为自定义组件增加特定的功能。该装饰器用于声明任意UI描述的一个元素类似slot占位符。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 装饰器使用说明 初始化BuilderParam装饰的方法 BuildParam装饰的方法只能被自定义构建函数Builder装饰的方法初始化。 使用所属自定义组件的自定义构建函数或者全局的自定义构建函数在本地初始化BuilderParam。 Builder function GlobalBuilder0() {}Component struct Child {Builder doNothingBuilder() {};BuilderParam aBuilder0: () void this.doNothingBuilder;BuilderParam aBuilder1: () void GlobalBuilder0;build(){} }用父组件自定义构建函数初始化子组件BuildParam装饰的方法。 Component struct Child {BuilderParam aBuilder0: () void;build() {Column() {this.aBuilder0()}} }Entry Component struct Parent {Builder componentBuilder() {Text(Parent builder )}build() {Column() {Child({ aBuilder0: this.componentBuilder })}} }需注意this指向正确。 说明 开发者谨慎使用bind改变函数调用的上下文可能会使this指向混乱。 Component struct Child {label: string ChildBuilderParam aBuilder0: () void;build() {Column() {this.aBuilder0()}} }Entry Component struct Parent {label: string ParentBuilder componentBuilder() {Text(${this.label})}build() {Column() {this.componentBuilder()Child({ aBuilder0: this.componentBuilder })}} }使用场景 参数初始化组件 BuilderParam装饰的方法可以是有参数和无参数的两种形式需与指向的Builder方法类型匹配。BuilderParam装饰的方法类型需要和Builder方法类型一致。 Builder function GlobalBuilder1($$ : {label: string }) {Text($$.label).width(400).height(50).backgroundColor(Color.Blue) }Component struct Child {label: string Child// 无参数类指向的componentBuilder也是无参数类型BuilderParam aBuilder0: () void;// 有参数类型指向的GlobalBuilder1也是有参数类型的方法BuilderParam aBuilder1: ($$ : { label : string}) void;build() {Column() {this.aBuilder0()this.aBuilder1({label: global Builder label } )}} }Entry Component struct Parent {label: string ParentBuilder componentBuilder() {Text(${this.label})}build() {Column() {this.componentBuilder()Child({ aBuilder0: this.componentBuilder, aBuilder1: GlobalBuilder1 })}} }尾随闭包初始化组件 在自定义组件中使用BuilderParam装饰的属性时也可通过尾随闭包进行初始化。在初始化自定义组件时组件后紧跟一个大括号“{}”形成尾随闭包场景。 说明 此场景下自定义组件内有且仅有一个使用BuilderParam装饰的属性。 开发者可以将尾随闭包内的内容看做Builder装饰的函数传给BuilderParam。示例如下 // xxx.ets Component struct CustomContainer {Prop header: string;BuilderParam closer: () voidbuild() {Column() {Text(this.header).fontSize(30)this.closer()}} }Builder function specificParam(label1: string, label2: string) {Column() {Text(label1).fontSize(30)Text(label2).fontSize(30)} }Entry Component struct CustomContainerUser {State text: string header;build() {Column() {// 创建CustomContainer在创建CustomContainer时通过其后紧跟一个大括号“{}”形成尾随闭包// 作为传递给子组件CustomContainer BuilderParam closer: () void的参数CustomContainer({ header: this.text }) {Column() {specificParam(testA, testB)}.backgroundColor(Color.Yellow).onClick(() {this.text changeHeader;})}}} }Styles装饰器定义组件重用样式 如果每个组件的样式都需要单独设置在开发过程中会出现大量代码在进行重复样式设置虽然可以复制粘贴但为了代码简洁性和后续方便维护我们推出了可以提炼公共样式进行复用的装饰器Styles。 Styles装饰器可以将多条样式设置提炼成一个方法直接在组件声明的位置调用。通过Styles装饰器可以快速定义并复用自定义样式。用于快速定义并复用自定义样式。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 装饰器使用说明 当前Styles仅支持通用属性和通用事件。 Styles方法不支持参数反例如下。 // 反例 Styles不支持参数 Styles function globalFancy (value: number) {.width(value) }Styles可以定义在组件内或全局在全局定义时需在方法名前面添加function关键字组件内定义时则不需要添加function关键字。 // 全局 Styles function functionName() { ... }// 在组件内 Component struct FancyUse {Styles fancy() {.height(100)} }定义在组件内的Styles可以通过this访问组件的常量和状态变量并可以在Styles里通过事件来改变状态变量的值示例如下 Component struct FancyUse {State heightValue: number 100Styles fancy() {.height(this.heightValue).backgroundColor(Color.Yellow).onClick(() {this.heightValue 200})} }组件内Styles的优先级高于全局Styles。 框架优先找当前组件内的Styles如果找不到则会全局查找。 使用场景 以下示例中演示了组件内Styles和全局Styles的用法。 // 定义在全局的Styles封装的样式 Styles function globalFancy () {.width(150).height(100).backgroundColor(Color.Pink) }Entry Component struct FancyUse {State heightValue: number 100// 定义在组件内的Styles封装的样式Styles fancy() {.width(200).height(this.heightValue).backgroundColor(Color.Yellow).onClick(() {this.heightValue 200})}build() {Column({ space: 10 }) {// 使用全局的Styles封装的样式Text(FancyA).globalFancy ().fontSize(30)// 使用组件内的Styles封装的样式Text(FancyB).fancy().fontSize(30)}} }Extend装饰器定义扩展组件样式 在前文的示例中可以使用Styles用于样式的扩展在Styles的基础上我们提供了Extend用于扩展原生组件样式。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 装饰器使用说明 语法 Extend(UIComponentName) function functionName { ... }使用规则 和Styles不同Extend仅支持定义在全局不支持在组件内部定义。 和Styles不同Extend支持封装指定的组件的私有属性和私有事件和预定义相同组件的Extend的方法。 // Extend(Text)可以支持Text的私有属性fontColor Extend(Text) function fancy () {.fontColor(Color.Red) } // superFancyText可以调用预定义的fancy Extend(Text) function superFancyText(size:number) {.fontSize(size).fancy() }和Styles不同Extend装饰的方法支持参数开发者可以在调用时传递参数调用遵循TS方法传值调用。 // xxx.ets Extend(Text) function fancy (fontSize: number) {.fontColor(Color.Red).fontSize(fontSize) }Entry Component struct FancyUse {build() {Row({ space: 10 }) {Text(Fancy).fancy(16)Text(Fancy).fancy(24)}} }Extend装饰的方法的参数可以为function作为Event事件的句柄。 Extend(Text) function makeMeClick(onClick: () void) {.backgroundColor(Color.Blue).onClick(onClick) }Entry Component struct FancyUse {State label: string Hello World;onClickHandler() {this.label Hello ArkUI;}build() {Row({ space: 10 }) {Text(${this.label}).makeMeClick(this.onClickHandler.bind(this))}} }Extend的参数可以为 状态变量当状态变量改变时UI可以正常的被刷新渲染。 Extend(Text) function fancy (fontSize: number) {.fontColor(Color.Red).fontSize(fontSize) }Entry Component struct FancyUse {State fontSizeValue: number 20build() {Row({ space: 10 }) {Text(Fancy).fancy(this.fontSizeValue).onClick(() {this.fontSizeValue 30})}} }使用场景 以下示例声明了3个Text组件每个Text组件均设置了fontStyle、fontWeight和backgroundColor样式。 Entry Component struct FancyUse {State label: string Hello Worldbuild() {Row({ space: 10 }) {Text(${this.label}).fontStyle(FontStyle.Italic).fontWeight(100).backgroundColor(Color.Blue)Text(${this.label}).fontStyle(FontStyle.Italic).fontWeight(200).backgroundColor(Color.Pink)Text(${this.label}).fontStyle(FontStyle.Italic).fontWeight(300).backgroundColor(Color.Orange)}.margin(20%)} }Extend将样式组合复用示例如下。 Extend(Text) function fancyText(weightValue: number, color: Color) {.fontStyle(FontStyle.Italic).fontWeight(weightValue).backgroundColor(color) }通过Extend组合样式后使得代码更加简洁增强可读性。 Entry Component struct FancyUse {State label: string Hello Worldbuild() {Row({ space: 10 }) {Text(${this.label}).fancyText(100, Color.Blue)Text(${this.label}).fancyText(200, Color.Pink)Text(${this.label}).fancyText(300, Color.Orange)}.margin(20%)} }stateStyles多态样式 Styles和Extend仅仅应用于静态页面的样式复用stateStyles可以依据组件的内部状态的不同快速设置不同样式。这就是我们本章要介绍的内容stateStyles又称为多态样式。 概述 stateStyles是属性方法可以根据UI内部状态来设置样式类似于css伪类但语法不同。ArkUI提供以下四种状态 focused获焦态。normal正常态。pressed按压态。disabled不可用态。 使用场景 基础场景 下面的示例展示了stateStyles最基本的使用场景。Button处于第一个组件默认获焦生效focused指定的粉色样式。按压时显示为pressed态指定的黑色。如果在Button前再放一个组件使其不处于获焦态就会生效normal态的黄色。 Entry Component struct StateStylesSample {build() {Column() {Button(Click me).stateStyles({focused: {.backgroundColor(Color.Pink)},pressed: {.backgroundColor(Color.Black)},normal: {.backgroundColor(Color.Yellow)}})}.margin(30%)} }**图1 **获焦态和按压态 [外链图片转存中…(img-aDso9RTP-1704894464566)] Styles和stateStyles联合使用 以下示例通过Styles指定stateStyles的不同状态。 Entry Component struct MyComponent {Styles normalStyle() {.backgroundColor(Color.Gray)}Styles pressedStyle() {.backgroundColor(Color.Red)}build() {Column() {Text(Text1).fontSize(50).fontColor(Color.White).stateStyles({normal: this.normalStyle,pressed: this.pressedStyle,})}} }**图2 **正常态和按压态 [外链图片转存中…(img-9I6RQ2hy-1704894464567)] 在stateStyles里使用常规变量和状态变量 stateStyles可以通过this绑定组件内的常规变量和状态变量。 Entry Component struct CompWithInlineStateStyles {State focusedColor: Color Color.Red;normalColor: Color Color.Greenbuild() {Button(clickMe).height(100).width(100).stateStyles({normal: {.backgroundColor(this.normalColor)},focused: {.backgroundColor(this.focusedColor)}}).onClick(() {this.focusedColor Color.Pink}).margin(30%)} }Button默认获焦显示红色点击事件触发后获焦态变为粉色。 **图3 **点击改变获焦态样式 [外链图片转存中…(img-lw4ubPoI-1704894464568)] 状态管理 状态管理概述 在前文的描述中我们构建的页面多为静态界面。如果希望构建一个动态的、有交互的界面就需要引入“状态”的概念。 **图1 **效果图 [外链图片转存中…(img-NWFB8i5a-1704894464570)] 上面的示例中用户与应用程序的交互触发了文本状态变更状态变更引起了UI渲染UI从“Hello World”变更为“Hello ArkUI”。 在声明式UI编程框架中UI是程序状态的运行结果用户构建了一个UI模型其中应用的运行时的状态是参数。当参数改变时UI作为返回结果也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染在ArkUI中统称为状态管理机制。 自定义组件拥有变量变量必须被装饰器装饰才可以成为状态变量状态变量的改变会引起UI的渲染刷新。如果不使用状态变量UI只能在初始化时渲染后续将不会再刷新。 下图展示了State和ViewUI之间的关系。 [外链图片转存中…(img-8nRPmC7s-1704894464570)] View(UI)UI渲染一般指自定义组件的build方法和Builder装饰的方法内的UI描述。State状态一般指的是装饰器装饰的数据。用户通过触发组件的事件方法改变状态数据。状态数据的改变引起UI的重新渲染。 基本概念 状态变量被状态装饰器装饰的变量改变会引起UI的渲染更新。 常规变量没有状态的变量通常应用于辅助计算。它的改变永远不会引起UI的刷新。 数据源/同步源状态变量的原始来源可以同步给不同的状态数据。通常意义为父组件传给子组件的数据。 命名参数机制父组件通过指定参数传递给子组件的状态变量为父子传递同步参数的主要手段。示例CompA: ({ aProp: this.aProp })。 从父组件初始化父组件使用命名参数机制将指定参数传递给子组件。本地初始化的默认值在有父组件传值的情况下会被覆盖。示例 Component struct MyComponent {State count: number 0;private increaseBy: number 1;build() {} }Component struct Parent {build() {Column() {// 从父组件初始化覆盖本地定义的默认值MyComponent({ count: 1, increaseBy: 2 })}} }初始化子节点组件中状态变量可以传递给子组件初始化子组件对应的状态变量。示例同上。 本地初始化变量声明的时候赋值作为初始化的默认值。示例State count: number 0。 装饰器总览 ArkUI提供了多种装饰器通过使用这些装饰器状态变量不仅可以观察在组件内的改变还可以在不同组件层级间传递比如父子组件、跨组件层级也可以观察全局范围内的变化。根据状态变量的影响范围将所有的装饰器可以大致分为 管理组件拥有状态的装饰器组件级别的状态管理可以观察组件内变化和不同组件层级的变化但需要唯一观察同一个组件树上即同一个页面内。管理应用拥有状态的装饰器应用级别的状态管理可以观察不同页面甚至不同UIAbility的状态变化是应用内全局的状态管理。 从数据的传递形式和同步类型层面看装饰器也可分为 只读的单向传递可变更的双向传递。 图示如下具体装饰器的介绍可详见管理组件拥有的状态和管理应用拥有的状态。开发者可以灵活地利用这些能力来实现数据和UI的联动。 [外链图片转存中…(img-NZbFOEUJ-1704894464571)] 上图中Components部分的装饰器为组件级别的状态管理Application部分为应用的状态管理。开发者可以通过StorageLink/LocalStorageLink和StorageProp/LocalStorageProp实现应用和组件状态的双向和单向同步。图中箭头方向为数据同步方向单箭头为单向同步双箭头为双向同步。 管理组件拥有的状态即图中Components级别的状态管理 StateState装饰的变量拥有其所属组件的状态可以作为其子组件单向和双向同步的数据源。当其数值改变时会引起相关组件的渲染刷新。 PropProp装饰的变量可以和父组件建立单向同步关系Prop装饰的变量是可变的但修改不会同步回父组件。 LinkLink装饰的变量和父组件构建双向同步关系的状态变量父组件会接受来自Link装饰的变量的修改的同步父组件的更新也会同步给Link装饰的变量。 Provide/ConsumeProvide/Consume装饰的变量用于跨组件层级多层组件同步状态变量可以不需要通过参数命名机制传递通过alias别名或者属性名绑定。 ObservedObserved装饰class需要观察多层嵌套场景的class需要被Observed装饰。单独使用Observed没有任何作用需要和ObjectLink、Prop连用。 ObjectLinkObjectLink装饰的变量接收Observed装饰的class的实例应用于观察多层嵌套场景和父组件的数据源构建双向同步。 说明 仅Observed/ObjectLink可以观察嵌套场景其他的状态变量仅能观察第一层详情见各个装饰器章节的“观察变化和行为表现”小节。 管理应用拥有的状态即图中Application级别的状态管理 AppStorage是应用程序中的一个特殊的单例LocalStorage对象是应用级的数据库和进程绑定通过StorageProp和StorageLink装饰器可以和组件联动。AppStorage是应用状态的“中枢”需要和组件UI交互的数据存入AppStorage比如持久化数据PersistentStorage和环境变量Environment。UI再通过AppStorage提供的装饰器或者API接口访问这些数据框架还提供了LocalStorageAppStorage是LocalStorage特殊的单例。LocalStorage是应用程序声明的应用状态的内存“数据库”通常用于页面级的状态共享通过LocalStorageProp和LocalStorageLink装饰器可以和UI联动。 其他状态管理功能 Watch用于监听状态变量的变化。 $$运算符给内置组件提供TS变量的引用使得TS变量和内置组件的内部状态保持同步。 管理组件拥有的状态 State装饰器组件内状态 State装饰的变量或称为状态变量一旦变量拥有了状态属性就和自定义组件的渲染绑定起来。当状态改变时UI会发生对应的渲染改变。 在状态变量相关装饰器中State是最基础的使变量拥有状态属性的装饰器它也是大部分状态变量的数据源。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 概述 State装饰的变量与声明式范式中的其他被装饰变量一样是私有的只能从组件内部访问在声明时必须指定其类型和本地初始化。初始化也可选择使用命名参数机制从父组件完成初始化。 State装饰的变量拥有以下特点 State装饰的变量与子组件中的Prop、Link或ObjectLink装饰变量之间建立单向或双向数据同步。State装饰的变量生命周期与其所属自定义组件的生命周期相同。 装饰器使用规则说明 State变量装饰器说明装饰器参数无同步类型不与父组件中任何类型的变量同步。允许装饰的变量类型Object、class、string、number、boolean、enum类型以及这些类型的数组。嵌套类型的场景请参考观察变化。类型必须被指定。不支持any不支持简单类型和复杂类型的联合类型不允许使用undefined和null。说明建议不要装饰Date类型应用可能会产生异常行为。不支持Length、ResourceStr、ResourceColor类型Length、ResourceStr、ResourceColor为简单类型和复杂类型的联合类型。被装饰变量的初始值必须本地初始化。 变量的传递/访问规则说明 传递/访问说明从父组件初始化可选从父组件初始化或者本地初始化。如果从父组件初始化将会覆盖本地初始化。支持父组件中常规变量、State、Link、Prop、Provide、Consume、ObjectLink、StorageLink、StorageProp、LocalStorageLink和LocalStorageProp装饰的变量初始化子组件的State。用于初始化子组件State装饰的变量支持初始化子组件的常规变量、State、Link、Prop、Provide。是否支持组件外访问不支持只能在组件内访问。 **图1 **初始化规则图示 [外链图片转存中…(img-JWn3Vt8y-1704894464572)] 观察变化和行为表现 并不是状态变量的所有更改都会引起UI的刷新只有可以被框架观察到的修改才会引起UI刷新。该小节去介绍什么样的修改才能被观察到以及观察到变化后框架的是怎么引起UI刷新的即框架的行为表现是什么。 观察变化 当装饰的数据类型为boolean、string、number类型时可以观察到数值的变化。 // for simple type State count: number 0; // value changing can be observed this.count 1;当装饰的数据类型为class或者Object时可以观察到自身的赋值的变化和其属性赋值的变化即Object.keys(observedObject)返回的所有属性。例子如下。 class ClassA {public value: string;constructor(value: string) {this.value value;} }class Model {public value: string;public name: ClassA;constructor(value: string, a: ClassA) {this.value value;this.name a;} }State装饰的类型是Model // class类型 State title: Model new Model(Hello, new ClassA(World));对State装饰变量的赋值。 // class类型赋值 this.title new Model(Hi, new ClassA(ArkUI));对State装饰变量的属性赋值。 // class属性的赋值 this.title.value Hi嵌套属性的赋值观察不到。 // 嵌套的属性赋值观察不到 this.title.name.value ArkUI当装饰的对象是array时可以观察到数组本身的赋值和添加、删除、更新数组的变化。例子如下。 声明ClassA和Model类。 class Model {public value: number;constructor(value: number) {this.value value;} }State装饰的对象为Model类型数组时。 State title: Model[] [new Model(11), new Model(1)]数组自身的赋值可以观察到。 this.title [new Model(2)]数组项的赋值可以观察到。 this.title[0] new Model(2)删除数组项可以观察到。 this.title.pop()新增数组项可以观察到。 this.title.push(new Model(12))框架行为 当状态变量被改变时查询依赖该状态变量的组件执行依赖该状态变量的组件的更新方法组件更新渲染和该状态变量不相关的组件或者UI描述不会发生重新渲染从而实现页面渲染的按需更新。 使用场景 装饰简单类型的变量 以下示例为State装饰的简单类型count被State装饰成为状态变量count的改变引起Button组件的刷新 当状态变量count改变时查询到只有Button组件关联了它执行Button组件的更新方法实现按需刷新。 Entry Component struct MyComponent {State count: number 0;build() {Button(click times: ${this.count}).onClick(() {this.count 1;})} }装饰class对象类型的变量 自定义组件MyComponent定义了被State装饰的状态变量count和title其中title的类型为自定义类Model。如果count或title的值发生变化则查询MyComponent中使用该状态变量的UI组件并进行重新渲染。EntryComponent中有多个MyComponent组件实例第一个MyComponent内部状态的更改不会影响第二个MyComponent。 class Model {public value: string;constructor(value: string) {this.value value;} }Entry Component struct EntryComponent {build() {Column() {// 此处指定的参数都将在初始渲染时覆盖本地定义的默认值并不是所有的参数都需要从父组件初始化MyComponent({ count: 1, increaseBy: 2 })MyComponent({ title: new Model(Hello, World 2), count: 7 })}} }Component struct MyComponent {State title: Model new Model(Hello World);State count: number 0;private increaseBy: number 1;build() {Column() {Text(${this.title.value})Button(Click to change title).onClick(() {// State变量的更新将触发上面的Text组件内容更新this.title.value this.title.value Hello ArkUI ? Hello World : Hello ArkUI;})Button(Click to increase count${this.count}).onClick(() {// State变量的更新将触发该Button组件的内容更新this.count this.increaseBy;})}} }从该示例中我们可以了解到State变量首次渲染的初始化流程 使用默认的本地初始化 State title: Model new Model(Hello World); State count: number 0;对于State来说命名参数机制传递的值并不是必选的如果没有命名参数传值则使用本地初始化的默认值 MyComponent({ count: 1, increaseBy: 2 })Prop装饰器父子单向同步 Prop装饰的变量可以和父组件建立单向的同步关系。Prop装饰的变量是可变的但是变化不会同步回其父组件。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 概述 Prop装饰的变量和父组件建立单向的同步关系 Prop变量允许在本地修改但修改后的变化不会同步回父组件。当父组件中的数据源更改时与之相关的Prop装饰的变量都会自动更新。如果子组件已经在本地修改了Prop装饰的相关变量值而在父组件中对应的State装饰的变量被修改后子组件本地修改的Prop装饰的相关变量值将被覆盖。 装饰器使用规则说明 Prop变量装饰器说明装饰器参数无同步类型单向同步对父组件状态变量值的修改将同步给子组件Prop装饰的变量子组件Prop变量的修改不会同步到父组件的状态变量上允许装饰的变量类型string、number、boolean、enum类型。不支持any不允许使用undefined和null。必须指定类型。在父组件中传递给Prop装饰的值不能为undefined或者null反例如下所示。CompA ({ aProp: undefined })CompA ({ aProp: null })Prop和数据源类型需要相同有以下三种情况数据源以State为例Prop装饰的变量和父组件状态变量类型相同即Prop : S和State : S示例请参考父组件State到子组件Prop简单数据类型同步。当父组件的状态变量为数组时Prop装饰的变量和父组件状态变量的数组项类型相同即Prop : S和State : Array示例请参考父组件State数组中的项到子组件Prop简单数据类型同步当父组件状态变量为Object或者class时Prop装饰的变量和父组件状态变量的属性类型相同即Prop : S和State : { propA: S }示例请参考从父组件中的State类对象属性到Prop简单类型的同步。被装饰变量的初始值允许本地初始化。 变量的传递/访问规则说明 传递/访问说明从父组件初始化如果本地有初始化则是可选的。没有的话则必选支持父组件中的常规变量、State、Link、Prop、Provide、Consume、ObjectLink、StorageLink、StorageProp、LocalStorageLink和LocalStorageProp去初始化子组件中的Prop变量。用于初始化子组件Prop支持去初始化子组件中的常规变量、State、Link、Prop、Provide。是否支持组件外访问Prop装饰的变量是私有的只能在组件内访问。 **图1 **初始化规则图示 [外链图片转存中…(img-weCmvH0x-1704894464572)] 观察变化和行为表现 观察变化 Prop装饰的数据可以观察到以下变化。 当装饰的类型是允许的类型即string、number、boolean、enum类型都可以观察到的赋值变化 // 简单类型 Prop count: number; // 赋值的变化可以被观察到 this.count 1;对于State和Prop的同步场景 使用父组件中State变量的值初始化子组件中的Prop变量。当State变量变化时该变量值也会同步更新至Prop变量。Prop装饰的变量的修改不会影响其数据源State装饰变量的值。除了State数据源也可以用Link或Prop装饰对Prop的同步机制是相同的。数据源和Prop变量的类型需要相同。 框架行为 要理解Prop变量值初始化和更新机制有必要了解父组件和拥有Prop变量的子组件初始渲染和更新流程。 初始渲染 执行父组件的build()函数将创建子组件的新实例将数据源传递给子组件初始化子组件Prop装饰的变量。 更新 子组件Prop更新时更新仅停留在当前子组件不会同步回父组件当父组件的数据源更新时子组件的Prop装饰的变量将被来自父组件的数据源重置所有Prop装饰的本地的修改将被父组件的更新覆盖。 使用场景 父组件State到子组件Prop简单数据类型同步 以下示例是State到子组件Prop简单数据同步父组件ParentComponent的状态变量countDownStartValue初始化子组件CountDownComponent中Prop装饰的count点击“Try again”count的修改仅保留在CountDownComponent不会同步给父组件ParentComponent。 ParentComponent的状态变量countDownStartValue的变化将重置CountDownComponent的count。 Component struct CountDownComponent {Prop count: number;costOfOneAttempt: number 1;build() {Column() {if (this.count 0) {Text(You have ${this.count} Nuggets left)} else {Text(Game over!)}// Prop装饰的变量不会同步给父组件Button(Try again).onClick(() {this.count - this.costOfOneAttempt;})}} }Entry Component struct ParentComponent {State countDownStartValue: number 10;build() {Column() {Text(Grant ${this.countDownStartValue} nuggets to play.)// 父组件的数据源的修改会同步给子组件Button(1 - Nuggets in New Game).onClick(() {this.countDownStartValue 1;})// 父组件的修改会同步给子组件Button(-1 - Nuggets in New Game).onClick(() {this.countDownStartValue - 1;})CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })}} }在上面的示例中 CountDownComponent子组件首次创建时其Prop装饰的count变量将从父组件State装饰的countDownStartValue变量初始化按“1”或“-1”按钮时父组件的State装饰的countDownStartValue值会变化这将触发父组件重新渲染在父组件重新渲染过程中会刷新使用countDownStartValue状态变量的UI组件并单向同步更新CountDownComponent子组件中的count值更新count状态变量值也会触发CountDownComponent的重新渲染在重新渲染过程中评估使用count状态变量的if语句条件this.count 0并执行true分支中的使用count状态变量的UI组件相关描述来更新Text组件的UI显示当按下子组件CountDownComponent的“Try again”按钮时其Prop变量count将被更改但是count值的更改不会影响父组件的countDownStartValue值父组件的countDownStartValue值会变化时父组件的修改将覆盖掉子组件CountDownComponent中count本地的修改。 父组件State数组项到子组件Prop简单数据类型同步 父组件中State如果装饰的数组其数组项也可以初始化Prop。以下示例中父组件Index中State装饰的数组arr将其数组项初始化子组件Child中Prop装饰的value。 Component struct Child {Prop value: number;build() {Text(${this.value}).fontSize(50).onClick((){this.value})} }Entry Component struct Index {State arr: number[] [1,2,3];build() {Row() {Column() {Child({value: this.arr[0]})Child({value: this.arr[1]})Child({value: this.arr[2]})Divider().height(5)ForEach(this.arr, item {Child({value: item})}, item item.toString())Text(replace entire arr).fontSize(50).onClick((){// 两个数组都包含项“3”。this.arr this.arr[0] 1 ? [3,4,5] : [1,2,3];})}}} }初始渲染创建6个子组件实例每个Prop装饰的变量初始化都在本地拷贝了一份数组项。子组件onclick事件处理程序会更改局部变量值。 假设我们点击了多次所有变量的本地取值都是“7”。 7 7 7 ---- 7 7 7单击replace entire arr后屏幕将显示以下信息为什么 3 4 5 ---- 7 4 5在子组件Child中做的所有的修改都不会同步回父组件Index组件所以即使6个组件显示都为7但在父组件Index中this.arr保存的值依旧是[1,2,3]。 点击replace entire arrthis.arr[0] 1成立将this.arr赋值为[3, 4, 5] 因为this.arr[0]已更改Child({value: this.arr[0]})组件将this.arr[0]更新同步到实例Prop装饰的变量。Child({value: this.arr[1]})和Child({value: this.arr[2]})的情况也类似。 this.arr的更改触发ForEach更新this.arr更新的前后都有数值为3的数组项[3, 4, 5] 和[1, 2, 3]。根据diff机制数组项“3”将被保留删除“1”和“2”的数组项添加为“4”和“5”的数组项。这就意味着数组项“3”的组件不会重新生成而是将其移动到第一位。所以“3”对应的组件不会更新此时“3”对应的组件数值为“7”ForEach最终的渲染结果是“7”“4”“5”。 从父组件中的State类对象属性到Prop简单类型的同步 如果图书馆有一本图书和两位用户每位用户都可以将图书标记为已读此标记行为不会影响其它读者用户。从代码角度讲对Prop图书对象的本地更改不会同步给图书馆组件中的State图书对象。 class Book {public title: string;public pages: number;public readIt: boolean false;constructor(title: string, pages: number) {this.title title;this.pages pages;} }Component struct ReaderComp {Prop title: string;Prop readIt: boolean;build() {Row() {Text(this.title)Text(... ${this.readIt ? I have read : I have not read it}).onClick(() this.readIt true)}} }Entry Component struct Library {State book: Book new Book(100 secrets of C, 765);build() {Column() {ReaderComp({ title: this.book.title, readIt: this.book.readIt })ReaderComp({ title: this.book.title, readIt: this.book.readIt })}} }Prop本地初始化不和父组件同步 为了支持Component装饰的组件复用场景Prop支持本地初始化这样可以让Prop是否与父组件建立同步关系变得可选。当且仅当Prop有本地初始化时从父组件向子组件传递Prop的数据源才是可选的。 下面的示例中子组件包含两个Prop变量 Prop customCounter没有本地初始化所以需要父组件提供数据源去初始化Prop并当父组件的数据源变化时Prop也将被更新 Prop customCounter2有本地初始化在这种情况下Prop依旧允许但非强制父组件同步数据源给Prop。 Component struct MyComponent {Prop customCounter: number;Prop customCounter2: number 5;build() {Column() {Row() {Text(From Main: ${this.customCounter}).width(90).height(40).fontColor(#FF0010)}Row() {Button(Click to change locally !).width(180).height(60).margin({ top: 10 }).onClick(() {this.customCounter2})}.height(100).width(180)Row() {Text(Custom Local: ${this.customCounter2}).width(90).height(40).fontColor(#FF0010)}}} }Entry Component struct MainProgram {State mainCounter: number 10;build() {Column() {Row() {Column() {Button(Click to change number).width(480).height(60).margin({ top: 10, bottom: 10 }).onClick(() {this.mainCounter})}}Row() {Column()// customCounter必须从父组件初始化因为MyComponent的customCounter成员变量缺少本地初始化此处customCounter2可以不做初始化。MyComponent({ customCounter: this.mainCounter })// customCounter2也可以从父组件初始化父组件初始化的值会覆盖子组件customCounter2的本地初始化的值MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })}}} }Link装饰器父子双向同步 子组件中被Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 概述 Link装饰的变量与其父组件中的数据源共享相同的值。 装饰器使用规则说明 Link变量装饰器说明装饰器参数无同步类型双向同步。父组件中State, StorageLink和Link 和子组件Link可以建立双向数据同步反之亦然。允许装饰的变量类型Object、class、string、number、boolean、enum类型以及这些类型的数组。嵌套类型的场景请参考观察变化。类型必须被指定且和双向绑定状态变量的类型相同。不支持any不支持简单类型和复杂类型的联合类型不允许使用undefined和null。说明不支持Length、ResourceStr、ResourceColor类型Length、ResourceStr、ResourceColor为简单类型和复杂类型的联合类型。被装饰变量的初始值无禁止本地初始化。 变量的传递/访问规则说明 传递/访问说明从父组件初始化和更新必选。与父组件State, StorageLink和Link 建立双向绑定。允许父组件中State、Link、Prop、Provide、Consume、ObjectLink、StorageLink、StorageProp、LocalStorageLink和LocalStorageProp装饰变量初始化子组件Link。从API version 9开始Link子组件从父组件初始化State的语法为Comp({ aLink: this.aState })。同样Comp({aLink: $aState})也支持。用于初始化子组件允许可用于初始化常规变量、State、Link、Prop、Provide。是否支持组件外访问私有只能在所属组件内访问。 **图1 **初始化规则图示 [外链图片转存中…(img-S4whAt8F-1704894464573)] 观察变化和行为表现 观察变化 当装饰的数据类型为boolean、string、number类型时可以同步观察到数值的变化示例请参考简单类型和类对象类型的Link。当装饰的数据类型为class或者Object时可以观察到赋值和属性赋值的变化即Object.keys(observedObject)返回的所有属性示例请参考简单类型和类对象类型的Link。当装饰的对象是array时可以观察到数组添加、删除、更新数组单元的变化示例请参考数组类型的Link。 框架行为 Link装饰的变量和其所述的自定义组件共享生命周期。 为了了解Link变量初始化和更新机制有必要先了解父组件和拥有Link变量的子组件的关系初始渲染和双向更新的流程以父组件为State为例。 初始渲染执行父组件的build()函数后将创建子组件的新实例。初始化过程如下 必须指定父组件中的State变量用于初始化子组件的Link变量。子组件的Link变量值与其父组件的数据源变量保持同步双向数据同步。父组件的State状态变量包装类通过构造函数传给子组件子组件的Link包装类拿到父组件的State的状态变量后将当前Link包装类this指针注册给父组件的State变量。 Link的数据源的更新即父组件中状态变量更新引起相关子组件的Link的更新。处理步骤 通过初始渲染的步骤可知子组件Link包装类把当前this指针注册给父组件。父组件State变量变更后会遍历更新所有依赖它的系统组件elementid和状态变量比如Link包装类。通知Link包装类更新后子组件中所有依赖Link状态变量的系统组件elementId都会被通知更新。以此实现父组件对子组件的状态数据同步。 Link的更新当子组件中Link更新后处理步骤如下以父组件为State为例 Link更新后调用父组件的State包装类的set方法将更新后的数值同步回父组件。子组件Link和父组件State分别遍历依赖的系统组件进行对应的UI的更新。以此实现子组件Link同步回父组件State。 使用场景 简单类型和类对象类型的Link 以下示例中点击父组件ShufflingContainer中的“Parent View: Set yellowButton”和“Parent View: Set GreenButton”可以从父组件将变化同步给子组件子组件GreenButton和YellowButton中Link装饰变量的变化也会同步给其父组件。 class GreenButtonState {width: number 0;constructor(width: number) {this.width width;} } Component struct GreenButton {Link greenButtonState: GreenButtonState;build() {Button(Green Button).width(this.greenButtonState.width).height(150.0).backgroundColor(#00ff00).onClick(() {if (this.greenButtonState.width 700) {// 更新class的属性变化可以被观察到同步回父组件this.greenButtonState.width 125;} else {// 更新class变化可以被观察到同步回父组件this.greenButtonState new GreenButtonState(100);}})} } Component struct YellowButton {Link yellowButtonState: number;build() {Button(Yellow Button).width(this.yellowButtonState).height(150.0).backgroundColor(#ffff00).onClick(() {// 子组件的简单类型可以同步回父组件this.yellowButtonState 50.0;})} } Entry Component struct ShufflingContainer {State greenButtonState: GreenButtonState new GreenButtonState(300);State yellowButtonProp: number 100;build() {Column() {// 简单类型从父组件State向子组件Link数据同步Button(Parent View: Set yellowButton).onClick(() {this.yellowButtonProp (this.yellowButtonProp 700) ? this.yellowButtonProp 100 : 100;})// class类型从父组件State向子组件Link数据同步Button(Parent View: Set GreenButton).onClick(() {this.greenButtonState.width (this.greenButtonState.width 700) ? this.greenButtonState.width 100 : 100;})// class类型初始化LinkGreenButton({ greenButtonState: $greenButtonState })// 简单类型初始化LinkYellowButton({ yellowButtonState: $yellowButtonProp })}} }数组类型的Link Component struct Child {Link items: number[];build() {Column() {Button(Button1: push).onClick(() {this.items.push(this.items.length 1);})Button(Button2: replace whole item).onClick(() {this.items [100, 200, 300];})}} }Entry Component struct Parent {State arr: number[] [1, 2, 3];build() {Column() {Child({ items: $arr })ForEach(this.arr,item {Text(${item})},item item.toString())}} }上文所述ArkUI框架可以观察到数组元素的添加删除和替换。在该示例中State和Link的类型是相同的number[]不允许将Link定义成number类型Link item : number并在父组件中用State数组中每个数据项创建子组件。如果要使用这个场景可以参考Prop和Observed。 Provide装饰器和Consume装饰器与后代组件双向同步 Provide和Consume应用于与后代组件的双向数据同步应用于状态数据在多个层级之间传递的场景。不同于上文提到的父子组件之间通过命名参数机制传递Provide和Consume摆脱参数传递机制的束缚实现跨层级传递。 其中Provide装饰的变量是在祖先节点中可以理解为被“提供”给后代的状态变量。Consume装饰的变量是在后代组件中去“消费绑定”祖先节点提供的变量。 说明 从API version 9开始这两个装饰器支持在ArkTS卡片中使用。 概述 Provide/Consume装饰的状态变量有以下特性 Provide装饰的状态变量自动对其所有后代组件可用即该变量被“provide”给他的后代组件。由此可见Provide的方便之处在于开发者不需要多次在组件之间传递变量。后代通过使用Consume去获取Provide提供的变量建立在Provide和Consume之间的双向数据同步与State/Link不同的是前者可以在多层级的父子组件之间传递。Provide和Consume可以通过相同的变量名或者相同的变量别名绑定变量类型必须相同。 // 通过相同的变量名绑定 Provide a: number 0; Consume a: number;// 通过相同的变量别名绑定 Provide(a) b: number 0; Consume(a) c: number;Provide和Consume通过相同的变量名或者相同的变量别名绑定时Provide修饰的变量和Consume修饰的变量是一对多的关系。不允许在同一个自定义组件内包括其子组件中声明多个同名或者同别名的Provide装饰的变量。 装饰器说明 State的规则同样适用于Provide差异为Provide还作为多层后代的同步源。 Provide变量装饰器说明装饰器参数别名常量字符串可选。如果指定了别名则通过别名来绑定变量如果未指定别名则通过变量名绑定变量。同步类型双向同步。从Provide变量到所有Consume变量以及相反的方向的数据同步。双向同步的操作与State和Link的组合相同。允许装饰的变量类型Object、class、string、number、boolean、enum类型以及这些类型的数组。嵌套类型的场景请参考观察变化。不支持any不支持简单类型和复杂类型的联合类型不允许使用undefined和null。必须指定类型。Provide变量的Consume变量的类型必须相同。说明不支持Length、ResourceStr、ResourceColor类型Length、ResourceStr、ResourceColor为简单类型和复杂类型的联合类型。被装饰变量的初始值必须指定。 Consume变量装饰器说明装饰器参数别名常量字符串可选。如果提供了别名则必须有Provide的变量和其有相同的别名才可以匹配成功否则则需要变量名相同才能匹配成功。同步类型双向从Provide变量具体请参见Provide到所有Consume变量以及相反的方向。双向同步操作与State和Link的组合相同。允许装饰的变量类型Object、class、string、number、boolean、enum类型以及这些类型的数组。嵌套类型的场景请参考观察变化。不支持any不允许使用undefined和null。必须指定类型。Provide变量的Consume变量的类型必须相同。说明Consume装饰的变量在其父节点或者祖先节点上必须有对应的属性和别名的Provide装饰的变量。被装饰变量的初始值无禁止本地初始化。 变量的传递/访问规则说明 Provide传递/访问说明从父组件初始化和更新可选允许父组件中常规变量、State、Link、Prop、Provide、Consume、ObjectLink、StorageLink、StorageProp、LocalStorageLink和LocalStorageProp装饰的变量装饰变量初始化子组件Provide。用于初始化子组件允许可用于初始化State、Link、Prop、Provide。和父组件同步否。和后代组件同步和Consume双向同步。是否支持组件外访问私有仅可以在所属组件内访问。 **图1 **Provide初始化规则图示 [外链图片转存中…(img-IKCA0DsS-1704894464574)] Consume传递/访问说明从父组件初始化和更新禁止。通过相同的变量名和alias别名从Provide初始化。用于初始化子组件允许可用于初始化State、Link、Prop、Provide。和祖先组件同步和Provide双向同步。是否支持组件外访问私有仅可以在所属组件内访问 **图2 **Consume初始化规则图示 [外链图片转存中…(img-2QZX7tnQ-1704894464575)] 观察变化和行为表现 观察变化 当装饰的数据类型为boolean、string、number类型时可以观察到数值的变化。当装饰的数据类型为class或者Object的时候可以观察到赋值和属性赋值的变化属性为Object.keys(observedObject)返回的所有属性。当装饰的对象是array的时候可以观察到数组的添加、删除、更新数组单元。 框架行为 初始渲染 Provide装饰的变量会以map的形式传递给当前Provide所属组件的所有子组件子组件中如果使用Consume变量则会在map中查找是否有该变量名/alias别名对应的Provide的变量如果查找不到框架会抛出JS ERROR;在初始化Consume变量时和State/Link的流程类似Consume变量会保存在map中查找到的Provide变量并把自己注册给Provide。 当Provide装饰的数据变化时 通过初始渲染的步骤可知子组件Consume已把自己注册给父组件。父组件Provide变量变更后会遍历更新所有依赖它的系统组件elementid和状态变量Consume通知Consume更新后子组件所有依赖Consume的系统组件elementId都会被通知更新。以此实现Provide对Consume状态数据同步。 当Consume装饰的数据变化时 通过初始渲染的步骤可知子组件Consume持有Provide的实例。在Consume更新后调用Provide的更新方法将更新的数值同步回Provide以此实现Consume向Provide的同步更新。 使用场景 在下面的示例是与后代组件双向同步状态Provide和Consume场景。当分别点击CompA和CompD组件内Button时reviewVotes 的更改会双向同步在CompA和CompD中。 Component struct CompD {// Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的Provide装饰的变量Consume reviewVotes: number;build() {Column() {Text(reviewVotes(${this.reviewVotes}))Button(reviewVotes(${this.reviewVotes}), give 1).onClick(() this.reviewVotes 1)}.width(50%)} }Component struct CompC {build() {Row({ space: 5 }) {CompD()CompD()}} }Component struct CompB {build() {CompC()} }Entry Component struct CompA {// Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件Provide reviewVotes: number 0;build() {Column() {Button(reviewVotes(${this.reviewVotes}), give 1).onClick(() this.reviewVotes 1)CompB()}} }Observed装饰器和ObjectLink装饰器嵌套类对象属性变化 上文所述的装饰器仅能观察到第一层的变化但是在实际应用开发中应用会根据开发需要封装自己的数据模型。对于多层嵌套的情况比如二维数组或者数组项class或者class的属性是class他们的第二层的属性变化是无法观察到的。这就引出了Observed/ObjectLink装饰器。 说明 从API version 9开始这两个装饰器支持在ArkTS卡片中使用。 概述 ObjectLink和Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步 被Observed装饰的类可以被观察到属性的变化子组件中ObjectLink装饰器装饰的状态变量用于接收Observed装饰的类的实例和父组件中对应的状态变量建立双向数据绑定。这个实例可以是数组中的被Observed装饰的项或者是class object中的属性这个属性同样也需要被Observed装饰。单独使用Observed是没有任何作用的需要搭配ObjectLink或者Prop使用。 限制条件 使用Observed装饰class会改变class原始的原型链Observed和其他类装饰器装饰同一个class可能会带来问题。 装饰器说明 Observed类装饰器说明装饰器参数无类装饰器装饰class。需要放在class的定义前使用new创建类对象。 ObjectLink变量装饰器说明装饰器参数无同步类型不与父组件中的任何类型同步变量。允许装饰的变量类型必须为被Observed装饰的class实例必须指定类型。不支持简单类型可以使用Prop。ObjectLink的属性是可以改变的但是变量的分配是不允许的也就是说这个装饰器装饰变量是只读的不能被改变。被装饰变量的初始值不允许。 ObjectLink装饰的数据为可读示例。 // 允许ObjectLink装饰的数据属性赋值 this.objLink.a ... // 不允许ObjectLink装饰的数据自身赋值 this.objLink ...说明 ObjectLink装饰的变量不能被赋值如果要使用赋值操作请使用Prop。 Prop装饰的变量和数据源的关系是是单向同步Prop装饰的变量在本地拷贝了数据源所以它允许本地更改如果父组件中的数据源有更新Prop装饰的变量本地的修改将被覆盖ObjectLink装饰的变量和数据源的关系是双向同步ObjectLink装饰的变量相当于指向数据源的指针。如果一旦发生ObjectLink装饰的变量的赋值则同步链将被打断。 变量的传递/访问规则说明 ObjectLink传递/访问说明从父组件初始化必须指定。初始化ObjectLink装饰的变量必须同时满足以下场景类型必须是Observed装饰的class。初始化的数值需要是数组项或者class的属性。同步源的class或者数组必须是StateLinkProvideConsume或者ObjectLink装饰的数据。同步源是数组项的示例请参考对象数组。初始化的class的示例请参考嵌套对象。与源对象同步双向。可以初始化子组件允许可用于初始化常规变量、State、Link、Prop、Provide **图1 **初始化规则图示 [外链图片转存中…(img-tgImgGlz-1704894464575)] 观察变化和行为表现 观察的变化 Observed装饰的类如果其属性为非简单类型比如class、Object或者数组也需要被Observed装饰否则将观察不到其属性的变化。 class ClassA {public c: number;constructor(c: number) {this.c c;} }Observed class ClassB {public a: ClassA;public b: number;constructor(a: ClassA, b: number) {this.a a;this.b b;} }以上示例中ClassB被Observed装饰其成员变量的赋值的变化是可以被观察到的但对于ClassA没有被Observed装饰其属性的修改不能被观察到。 ObjectLink b: ClassB// 赋值变化可以被观察到 this.b.a new ClassA(5) this.b.b 5// ClassA没有被Observed装饰其属性的变化观察不到 this.b.a.c 5ObjectLinkObjectLink只能接收被Observed装饰class的实例可以观察到 其属性的数值的变化其中属性是指Object.keys(observedObject)返回的所有属性示例请参考嵌套对象。 如果数据源是数组则可以观察到数组item的替换如果数据源是class可观察到class的属性的变化示例请参考对象数组。 框架行为 初始渲染 Observed装饰的class的实例会被不透明的代理对象包装代理了class上的属性的setter和getter方法子组件中ObjectLink装饰的从父组件初始化接收被Observed装饰的class的实例ObjectLink的包装类会将自己注册给Observed class。 属性更新当Observed装饰的class属性改变时会走到代理的setter和getter然后遍历依赖它的ObjectLink包装类通知数据更新。 使用场景 嵌套对象 以下是嵌套类对象的数据结构。 // objectLinkNestedObjects.ets let NextID: number 1;Observed class ClassA {public id: number;public c: number;constructor(c: number) {this.id NextID;this.c c;} }Observed class ClassB {public a: ClassA;constructor(a: ClassA) {this.a a;} }以下组件层次结构呈现的是嵌套类对象的数据结构。 Component struct ViewA {label: string ViewA1;ObjectLink a: ClassA;build() {Row() {Button(ViewA [${this.label}] this.a.c${this.a.c} 1).onClick(() {this.a.c 1;})}} }Entry Component struct ViewB {State b: ClassB new ClassB(new ClassA(0));build() {Column() {ViewA({ label: ViewA #1, a: this.b.a })ViewA({ label: ViewA #2, a: this.b.a })Button(ViewB: this.b.a.c 1).onClick(() {this.b.a.c 1;})Button(ViewB: this.b.a new ClassA(0)).onClick(() {this.b.a new ClassA(0);})Button(ViewB: this.b new ClassB(ClassA(0))).onClick(() {this.b new ClassB(new ClassA(0));})}} }ViewB中的事件句柄 this.b.a new ClassA(0) 和this.b new ClassB(new ClassA(0)) 对State装饰的变量b和其属性的修改。this.b.a.c … 该变化属于第二层的变化State无法观察到第二层的变化但是ClassA被Observed装饰ClassA的属性c的变化可以被ObjectLink观察到。 ViewA中的事件句柄 this.a.c 1对ObjectLink变量a的修改将触发Button组件的刷新。ObjectLink和Prop不同ObjectLink不拷贝来自父组件的数据源而是在本地构建了指向其数据源的引用。ObjectLink变量是只读的this.a new ClassA(…)是不允许的因为一旦赋值操作发生指向数据源的引用将被重置同步将被打断。 对象数组 对象数组是一种常用的数据结构。以下示例展示了数组对象的用法。 Component struct ViewA {// 子组件ViewA的ObjectLink的类型是ClassAObjectLink a: ClassA;label: string ViewA1;build() {Row() {Button(ViewA [${this.label}] this.a.c ${this.a.c} 1).onClick(() {this.a.c 1;})}} }Entry Component struct ViewB {// ViewB中有State装饰的ClassA[]State arrA: ClassA[] [new ClassA(0), new ClassA(0)];build() {Column() {ForEach(this.arrA,(item) {ViewA({ label: #${item.id}, a: item })},(item) item.id.toString())// 使用State装饰的数组的数组项初始化ObjectLink其中数组项是被Observed装饰的ClassA的实例ViewA({ label: ViewA this.arrA[first], a: this.arrA[0] })ViewA({ label: ViewA this.arrA[last], a: this.arrA[this.arrA.length-1] })Button(ViewB: reset array).onClick(() {this.arrA [new ClassA(0), new ClassA(0)];})Button(ViewB: push).onClick(() {this.arrA.push(new ClassA(0))})Button(ViewB: shift).onClick(() {this.arrA.shift()})Button(ViewB: chg item property in middle).onClick(() {this.arrA[Math.floor(this.arrA.length / 2)].c 10;})Button(ViewB: chg item property in middle).onClick(() {this.arrA[Math.floor(this.arrA.length / 2)] new ClassA(11);})}} }this.arrA[Math.floor(this.arrA.length/2)] new ClassA(…) 该状态变量的改变触发2次更新 ForEach数组项的赋值导致ForEach的itemGenerator被修改因此数组项被识别为有更改ForEach的item builder将执行创建新的ViewA组件实例。ViewA({ label: ViewA this.arrA[first], a: this.arrA[0] })上述更改改变了数组中第一个元素所以绑定this.arrA[0]的ViewA将被更新 this.arrA.push(new ClassA(0)) 将触发2次不同效果的更新 ForEach新添加的ClassA对象对于ForEach是未知的itemGeneratorForEach的item builder将执行创建新的ViewA组件实例。ViewA({ label: ViewA this.arrA[last], a: this.arrA[this.arrA.length-1] })数组的最后一项有更改因此引起第二个ViewA的实例的更改。对于ViewA({ label: ViewA this.arrA[first], a: this.arrA[0] })数组的更改并没有触发一个数组项更改的改变所以第一个ViewA不会刷新。 this.arrA[Math.floor(this.arrA.length/2)].cState无法观察到第二层的变化但是ClassA被Observed装饰ClassA的属性的变化将被ObjectLink观察到。 二维数组 使用Observed观察二维数组的变化。可以声明一个被Observed装饰的继承Array的子类。 Observed class StringArray extends ArrayString { }使用new StringArray()来构造StringArray的实例new运算符使得Observed生效Observed观察到StringArray的属性变化。 声明一个从Array扩展的类class StringArray extends Array {}并创建StringArray的实例。Observed装饰的类需要使用new运算符来构建class实例。 Observed class StringArray extends ArrayString { }Component struct ItemPage {ObjectLink itemArr: StringArray;build() {Row() {Text(ItemPage).width(100).height(100)ForEach(this.itemArr,item {Text(item).width(100).height(100)},item item)}} }Entry Component struct IndexPage {State arr: ArrayStringArray [new StringArray(), new StringArray(), new StringArray()];build() {Column() {ItemPage({ itemArr: this.arr[0] })ItemPage({ itemArr: this.arr[1] })ItemPage({ itemArr: this.arr[2] })Divider()ForEach(this.arr,itemArr {ItemPage({ itemArr: itemArr })},itemArr itemArr[0])Divider()Button(update).onClick(() {console.error(Update all items in arr);if (this.arr[0][0] ! undefined) {// 正常情况下需要有一个真实的ID来与ForEach一起使用但此处没有// 因此需要确保推送的字符串是唯一的。this.arr[0].push(${this.arr[0].slice(-1).pop()}${this.arr[0].slice(-1).pop()});this.arr[1].push(${this.arr[1].slice(-1).pop()}${this.arr[1].slice(-1).pop()});this.arr[2].push(${this.arr[2].slice(-1).pop()}${this.arr[2].slice(-1).pop()});} else {this.arr[0].push(Hello);this.arr[1].push(World);this.arr[2].push(!);}})}} }管理应用拥有的状态 管理应用拥有的状态概述 上一个章节中介绍的装饰器仅能在页面内即一个组件树上共享状态变量。如果开发者要实现应用级的或者多个页面的状态数据共享就需要用到应用级别的状态管理的概念。ArkTS根据不同特性提供了多种应用状态管理的能力 LocalStorage页面级UI状态存储通常用于UIAbility内、页面间的状态共享。AppStorage特殊的单例LocalStorage对象由UI框架在应用程序启动时创建为应用程序UI状态属性提供中央存储PersistentStorage持久化存储UI状态通常和AppStorage配合使用选择AppStorage存储的数据写入磁盘以确保这些属性在应用程序重新启动时的值与应用程序关闭时的值相同Environment应用程序运行的设备的环境参数环境参数会同步到AppStorage中可以和AppStorage搭配使用。 LocalStorage页面级UI状态存储 LocalStorage是页面级的UI状态存储通过Entry装饰器接收的参数可以在页面内共享同一个LocalStorage实例。LocalStorage也可以在UIAbility内页面间共享状态。 本文仅介绍LocalStorage使用场景和相关的装饰器LocalStorageProp和LocalStorageLink。 说明 本模块从API version 9开始支持。 概述 LocalStorage是ArkTS为构建页面级别状态变量提供存储的内存内“数据库”。 应用程序可以创建多个LocalStorage实例LocalStorage实例可以在页面内共享也可以通过GetShared接口获取在UIAbility里创建的GetShared实现跨页面、UIAbility内共享。组件树的根节点即被Entry装饰的Component可以被分配一个LocalStorage实例此组件的所有子组件实例将自动获得对该LocalStorage实例的访问权限被Component装饰的组件最多可以访问一个LocalStorage实例和AppStorage未被Entry装饰的组件不可被独立分配LocalStorage实例只能接受父组件通过Entry传递来的LocalStorage实例。一个LocalStorage实例在组件树上可以被分配给多个组件。LocalStorage中的所有属性都是可变的。 应用程序决定LocalStorage对象的生命周期。当应用释放最后一个指向LocalStorage的引用时比如销毁最后一个自定义组件LocalStorage将被JS Engine垃圾回收。 LocalStorage根据与Component装饰的组件的同步类型不同提供了两个装饰器 LocalStoragePropLocalStorageProp装饰的变量和与LocalStorage中给定属性建立单向同步关系。LocalStorageLinkLocalStorageLink装饰的变量和在Component中创建与LocalStorage中给定属性建立双向同步关系。 限制条件 LocalStorage创建后命名属性的类型不可更改。后续调用Set时必须使用相同类型的值。LocalStorage是页面级存储GetShared接口仅能获取当前Stage通过windowStage.loadContent传入的LocalStorage实例否则返回undefined。例子可见将LocalStorage实例从UIAbility共享到一个或多个视图。 LocalStorageProp 在上文中已经提到如果要建立LocalStorage和自定义组件的联系需要使用LocalStorageProp和LocalStorageLink装饰器。使用LocalStorageProp(key)/LocalStorageLink(key)装饰组件内的变量key标识了LocalStorage的属性。 当自定义组件初始化的时候LocalStorageProp(key)/LocalStorageLink(key)装饰的变量会通过给定的key绑定LocalStorage对应的属性完成初始化。本地初始化是必要的因为无法保证LocalStorage一定存在给定的key这取决于应用逻辑是否在组件初始化之前在LocalStorage实例中存入对应的属性。 说明 从API version 9开始该装饰器支持在ArkTS卡片中使用。 LocalStorageProp(key)是和LocalStorage中key对应的属性建立单向数据同步我们允许本地改变的发生但是对于LocalStorageProp本地的修改永远不会同步回LocalStorage中相反如果LocalStorage给定key的属性发生改变改变会被同步给LocalStorageProp并覆盖掉本地的修改。 装饰器使用规则说明 LocalStorageProp变量装饰器说明装饰器参数key常量字符串必填字符串需要有引号。允许装饰的变量类型Object、class、string、number、boolean、enum类型以及这些类型的数组。嵌套类型的场景请参考观察变化和行为表现。类型必须被指定且必须和LocalStorage中对应属性相同。不支持any不允许使用undefined和null。同步类型单向同步从LocalStorage的对应属性到组件的状态变量。组件本地的修改是允许的但是LocalStorage中给定的属性一旦发生变化将覆盖本地的修改。被装饰变量的初始值必须指定如果LocalStorage实例中不存在属性则作为初始化默认值并存入LocalStorage中。 变量的传递/访问规则说明 传递/访问说明从父节点初始化和更新禁止LocalStorageProp不支持从父节点初始化只能从LocalStorage中key对应的属性初始化如果没有对应key的话将使用本地默认值初始化。初始化子节点支持可用于初始化State、Link、Prop、Provide。是否支持组件外访问否。 **图1 **LocalStorageProp初始化规则图示 [外链图片转存中…(img-y1AU1DE6-1704894464576)] 观察变化和行为表现 观察变化 当装饰的数据类型为boolean、string、number类型时可以观察到数值的变化。当装饰的数据类型为class或者Object时可以观察到赋值和属性赋值的变化即Object.keys(observedObject)返回的所有属性。当装饰的对象是array时可以观察到数组添加、删除、更新数组单元的变化。 框架行为 当LocalStorageProp(key)装饰的数值改变被观察到时修改不会被同步回LocalStorage对应属性键值key的属性中。当前LocalStorageProp(key)单向绑定的数据会被修改即仅限于当前组件的私有成员变量改变其他的绑定该key的数据不会同步改变。当LocalStorageProp(key)装饰的数据本身是状态变量它的改变虽然不会同步回LocalStorage中但是会引起所属的自定义组件的重新渲染。当LocalStorage中key对应的属性发生改变时会同步给所有LocalStorageProp(key)装饰的数据LocalStorageProp(key)本地的修改将被覆盖。 LocalStorageLink 如果我们需要将自定义组件的状态变量的更新同步回LocalStorage就需要用到LocalStorageLink。 LocalStorageLink(key)是和LocalStorage中key对应的属性建立双向数据同步 本地修改发生该修改会被回LocalStorage中LocalStorage中的修改发生后该修改会被同步到所有绑定LocalStorage对应key的属性上包括单向LocalStorageProp和通过prop创建的单向绑定变量、双向LocalStorageLink和通过link创建的双向绑定变量变量。 装饰器使用规则说明 LocalStorageLink变量装饰器说明装饰器参数key常量字符串必填字符串需要有引号。允许装饰的变量类型Object、class、string、number、boolean、enum类型以及这些类型的数组。嵌套类型的场景请参考观察变化和行为表现。类型必须被指定且必须和LocalStorage中对应属性相同。不支持any不允许使用undefined和null。同步类型双向同步从LocalStorage的对应属性到自定义组件从自定义组件到LocalStorage对应属性。被装饰变量的初始值必须指定如果LocalStorage实例中不存在属性则作为初始化默认值并存入LocalStorage中。 变量的传递/访问规则说明 传递/访问说明从父节点初始化和更新禁止LocalStorageLink不支持从父节点初始化只能从LocalStorage中key对应的属性初始化如果没有对应key的话将使用本地默认值初始化。初始化子节点支持可用于初始化State、Link、Prop、Provide。是否支持组件外访问否。 **图2 **LocalStorageLink初始化规则图示 [外链图片转存中…(img-huppyUGm-1704894464577)] 观察变化和行为表现 观察变化 当装饰的数据类型为boolean、string、number类型时可以观察到数值的变化。当装饰的数据类型为class或者Object时可以观察到赋值和属性赋值的变化即Object.keys(observedObject)返回的所有属性。当装饰的对象是array时可以观察到数组添加、删除、更新数组单元的变化。 框架行为 当LocalStorageLink(key)装饰的数值改变被观察到时修改将被同步回LocalStorage对应属性键值key的属性中。LocalStorage中属性键值key对应的数据一旦改变属性键值key绑定的所有的数据包括双向LocalStorageLink和单向LocalStorageProp都将同步修改当LocalStorageLink(key)装饰的数据本身是状态变量它的改变不仅仅会同步回LocalStorage中还会引起所属的自定义组件的重新渲染。 使用场景 应用逻辑使用LocalStorage let storage new LocalStorage({ PropA: 47 }); // 创建新实例并使用给定对象初始化 let propA storage.get(PropA) // propA 47 let link1 storage.link(PropA); // link1.get() 47 let link2 storage.link(PropA); // link2.get() 47 let prop storage.prop(PropA); // prop.get() 47 link1.set(48); // two-way sync: link1.get() link2.get() prop.get() 48 prop.set(1); // one-way sync: prop.get()1; but link1.get() link2.get() 48 link1.set(49); // two-way sync: link1.get() link2.get() prop.get() 49从UI内部使用LocalStorage 除了应用程序逻辑使用LocalStorage还可以借助LocalStorage相关的两个装饰器LocalStorageProp和LocalStorageLink在UI组件内部获取到LocalStorage实例中存储的状态变量。 本示例以LocalStorage为例展示了 使用构造函数创建LocalStorage实例storage 使用Entry装饰器将storage添加到CompA顶层组件中 LocalStorageLink绑定LocalStorage对给定的属性建立双向数据同步。 // 创建新实例并使用给定对象初始化 let storage new LocalStorage({ PropA: 47 });Component struct Child {// LocalStorageLink变量装饰器与LocalStorage中的PropA属性建立双向绑定LocalStorageLink(PropA) storLink2: number 1;build() {Button(Child from LocalStorage ${this.storLink2})// 更改将同步至LocalStorage中的PropA以及Parent.storLink1.onClick(() this.storLink2 1)} } // 使LocalStorage可从Component组件访问 Entry(storage) Component struct CompA {// LocalStorageLink变量装饰器与LocalStorage中的PropA属性建立双向绑定LocalStorageLink(PropA) storLink1: number 1;build() {Column({ space: 15 }) {Button(Parent from LocalStorage ${this.storLink1}) // initial value from LocalStorage will be 47, because PropA initialized already.onClick(() this.storLink1 1)// Component子组件自动获得对CompA LocalStorage实例的访问权限。Child()}} }LocalStorageProp和LocalStorage单向同步的简单场景 在下面的示例中CompA 组件和Child组件分别在本地创建了与storage的’PropA’对应属性的单向同步的数据我们可以看到 CompA中对this.storProp1的修改只会在CompA中生效并没有同步回storage Child组件中Text绑定的storProp2 依旧显示47。 // 创建新实例并使用给定对象初始化 let storage new LocalStorage({ PropA: 47 }); // 使LocalStorage可从Component组件访问 Entry(storage) Component struct CompA {// LocalStorageProp变量装饰器与LocalStorage中的PropA属性建立单向绑定LocalStorageProp(PropA) storProp1: number 1;build() {Column({ space: 15 }) {// 点击后从47开始加1只改变当前组件显示的storProp1不会同步到LocalStorage中Button(Parent from LocalStorage ${this.storProp1}).onClick(() this.storProp1 1)Child()}} }Component struct Child {// LocalStorageProp变量装饰器与LocalStorage中的PropA属性建立单向绑定LocalStorageProp(PropA) storProp2: number 2;build() {Column({ space: 15 }) {// 当CompA改变时当前storProp2不会改变显示47Text(Parent from LocalStorage ${this.storProp2})}} }LocalStorageLink和LocalStorage双向同步的简单场景 下面的示例展示了LocalStorageLink装饰的数据和LocalStorage双向同步的场景 // 构造LocalStorage实例 let storage new LocalStorage({ PropA: 47 }); // 调用link9接口构造PropA的双向同步数据linkToPropA 是全局变量 let linkToPropA storage.link(PropA);Entry(storage) Component struct CompA {// LocalStorageLink(PropA)在CompA自定义组件中创建PropA的双向同步数据初始值为47因为在构造LocalStorage已经给“PropA”设置47LocalStorageLink(PropA) storLink: number 1;build() {Column() {Text(incr LocalStorageLink variable)// 点击“incr LocalStorageLink variable”this.storLink加1改变同步回storage全局变量linkToPropA也会同步改变 .onClick(() this.storLink 1)// 并不建议在组件内使用全局变量linkToPropA.get()因为可能会有生命周期不同引起的错误。Text(LocalStorageLink: ${this.storLink} - linkToPropA: ${linkToPropA.get()})}} }兄弟节点之间同步状态变量 下面的示例展示了通过LocalStorageLink双向同步兄弟节点之间的状态。 先看Parent自定义组件中发生的变化 点击“playCount ${this.playCount} dec by 1”this.playCount减1修改同步回LocalStorage中Child组件中的playCountLink绑定的组件会同步刷新点击“countStorage ${this.playCount} incr by 1”调用LocalStorage的set接口更新LocalStorage中“countStorage”对应的属性Child组件中的playCountLink绑定的组件会同步刷新Text组件“playCount in LocalStorage for debug ${storage.get(‘countStorage’)}”没有同步刷新因为storage.get(‘countStorage’)返回的是常规变量常规变量的更新并不会引起Text组件的重新渲染。 Child自定义组件中的变化 playCountLink的刷新会同步回LocalStorage并且引起兄弟组件和父组件相应的刷新。 let storage new LocalStorage({ countStorage: 1 });Component struct Child {// 子组件实例的名字label: string no name;// 和LocalStorage中“countStorage”的双向绑定数据LocalStorageLink(countStorage) playCountLink: number 0;build() {Row() {Text(this.label).width(50).height(60).fontSize(12)Text(playCountLink ${this.playCountLink}: inc by 1).onClick(() {this.playCountLink 1;}).width(200).height(60).fontSize(12)}.width(300).height(60)} }Entry(storage) Component struct Parent {LocalStorageLink(countStorage) playCount: number 0;build() {Column() {Row() {Text(Parent).width(50).height(60).fontSize(12)Text(playCount ${this.playCount} dec by 1).onClick(() {this.playCount - 1;}).width(250).height(60).fontSize(12)}.width(300).height(60)Row() {Text(LocalStorage).width(50).height(60).fontSize(12)Text(countStorage ${this.playCount} incr by 1).onClick(() {storage.setnumber(countStorage, 1 storage.getnumber(countStorage));}).width(250).height(60).fontSize(12)}.width(300).height(60)Child({ label: ChildA })Child({ label: ChildB })Text(playCount in LocalStorage for debug ${storage.getnumber(countStorage)}).width(300).height(60).fontSize(12)}} }将LocalStorage实例从UIAbility共享到一个或多个视图 上面的实例中LocalStorage的实例仅仅在一个Entry装饰的组件和其所属的子组件一个页面中共享如果希望其在多个视图中共享可以在所属UIAbility中创建LocalStorage实例并调用windowStage.loadContent。 // EntryAbility.ts import UIAbility from ohos.app.ability.UIAbility; import window from ohos.window;export default class EntryAbility extends UIAbility {storage: LocalStorage new LocalStorage({PropA: 47});onWindowStageCreate(windowStage: window.WindowStage) {windowStage.loadContent(pages/Index, this.storage);} }在UI页面通过GetShared接口获取在通过loadContent共享的LocalStorage实例。 // 通过GetShared接口获取stage共享的LocalStorage实例 let storage LocalStorage.GetShared()Entry(storage) Component struct CompA {// can access LocalStorage instance using // LocalStorageLink/Prop decorated variablesLocalStorageLink(PropA) varA: number 1;build() {Column() {Text(${this.varA}).fontSize(50)}} }说明 对于开发者更建议使用这个方式来构建LocalStorage的实例并且在创建LocalStorage实例的时候就写入默认值因为默认值可以作为运行异常的备份也可以用作页面的单元测试。 AppStorage应用全局的UI状态存储 AppStorage是应用全局的UI状态存储是和应用的进程绑定的由UI框架在应用程序启动时创建为应用程序UI状态属性提供中央存储。 和AppStorage不同的是LocalStorage是页面级的通常应用于页面内的数据共享。而AppStorage是应用级的全局状态共享还相当于整个应用的“中枢”持久化数据PersistentStorage和环境变量Environment都是通过和AppStorage中转才可以和UI交互。 本文仅介绍AppStorage使用场景和相关的装饰器StorageProp和StorageLink。 概述 AppStorage是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储这些状态数据在应用级别都是可访问的。AppStorage将在应用运行过程保留其属性。属性通过唯一的键字符串值访问。 AppStorage可以和UI组件同步且可以在应用业务逻辑中被访问。 AppStorage中的属性可以被双向同步数据可以是存在于本地或远程设备上并具有不同的功能比如数据持久化详见PersistentStorage。这些数据是通过业务逻辑中实现与UI解耦如果希望这些数据在UI中使用需要用到StorageProp和StorageLink。 StorageProp 在上文中已经提到如果要建立AppStorage和自定义组件的联系需要使用StorageProp和StorageLink装饰器。使用StorageProp(key)/StorageLink(key)装饰组件内的变量key标识了AppStorage的属性。 当自定义组件初始化的时候StorageProp(key)/StorageLink(key)装饰的变量会通过给定的key绑定在AppStorage对应的属性完成初始化。本地初始化是必要的因为无法保证AppStorage一定存在给定的key这取决于应用逻辑是否在组件初始化之前在AppStorage实例中存入对应的属性。 StorageProp(key)是和AppStorage中key对应的属性建立单向数据同步我们允许本地改变的发生但是对于StorageProp本地的修改永远不会同步回AppStorage中相反如果AppStorage给定key的属性发生改变改变会被同步给StorageProp并覆盖掉本地的修改。 装饰器使用规则说明 StorageProp变量装饰器说明装饰器参数key常量字符串必填字符串需要有引号。允许装饰的变量类型Object class、string、number、boolean、enum类型以及这些类型的数组。嵌套类型的场景请参考观察变化和行为表现。类型必须被指定且必须和LocalStorage中对应属性相同。不支持any不允许使用undefined和null。同步类型单向同步从AppStorage的对应属性到组件的状态变量。组件本地的修改是允许的但是AppStorage中给定的属性一旦发生变化将覆盖本地的修改。被装饰变量的初始值必须指定如果AppStorage实例中不存在属性则作为初始化默认值并存入AppStorage中。 变量的传递/访问规则说明 传递/访问说明从父节点初始化和更新禁止StorageProp不支持从父节点初始化只能AppStorage中key对应的属性初始化如果没有对应key的话将使用本地默认值初始化初始化子节点支持可用于初始化State、Link、Prop、Provide。是否支持组件外访问否。 **图1 **StorageProp初始化规则图示 [外链图片转存中…(img-fVJhzHOM-1704894464578)] 观察变化和行为表现 观察变化 当装饰的数据类型为boolean、string、number类型时可以观察到数值的变化。当装饰的数据类型为class或者Object时可以观察到赋值和属性赋值的变化即Object.keys(observedObject)返回的所有属性。当装饰的对象是array时可以观察到数组添加、删除、更新数组单元的变化。 框架行为 当StorageProp(key)装饰的数值改变被观察到时修改不会被同步回AppStorage对应属性键值key的属性中。当前StorageProp(key)单向绑定的数据会被修改即仅限于当前组件的私有成员变量改变其他的绑定该key的数据不会同步改变。当StorageProp(key)装饰的数据本身是状态变量它的改变虽然不会同步回AppStorage中但是会引起所属的自定义组件的重新渲染。当AppStorage中key对应的属性发生改变时会同步给所有StorageProp(key)装饰的数据StorageProp(key)本地的修改将被覆盖。 StorageLink StorageLink(key)是和AppStorage中key对应的属性建立双向数据同步 本地修改发生该修改会被写回AppStorage中AppStorage中的修改发生后该修改会被同步到所有绑定AppStorage对应key的属性上包括单向StorageProp和通过Prop创建的单向绑定变量、双向StorageLink和通过Link创建的双向绑定变量变量和其他实例比如PersistentStorage。 装饰器使用规则说明 StorageLink变量装饰器说明装饰器参数key常量字符串必填字符串需要有引号。允许装饰的变量类型Object、class、string、number、boolean、enum类型以及这些类型的数组。嵌套类型的场景请参考观察变化和行为表现。类型必须被指定且必须和AppStorage中对应属性相同。不支持any不允许使用undefined和null。同步类型双向同步从AppStorage的对应属性到自定义组件从自定义组件到AppStorage对应属性。被装饰变量的初始值必须指定如果AppStorage实例中不存在属性则作为初始化默认值并存入AppStorage中。 变量的传递/访问规则说明 传递/访问说明从父节点初始化和更新禁止。初始化子节点支持可用于初始化常规变量、State、Link、Prop、Provide。是否支持组件外访问否。 **图2 **StorageLink初始化规则图示 [外链图片转存中…(img-L23az0kF-1704894464579)] 观察变化和行为表现 观察变化 当装饰的数据类型为boolean、string、number类型时可以观察到数值的变化。当装饰的数据类型为class或者Object时可以观察到赋值和属性赋值的变化即Object.keys(observedObject)返回的所有属性。当装饰的对象是array时可以观察到数组添加、删除、更新数组单元的变化。 框架行为 当StorageLink(key)装饰的数值改变被观察到时修改将被同步回AppStorage对应属性键值key的属性中。AppStorage中属性键值key对应的数据一旦改变属性键值key绑定的所有的数据包括双向StorageLink和单向StorageProp都将同步修改当StorageLink(key)装饰的数据本身是状态变量它的改变不仅仅会同步回AppStorage中还会引起所属的自定义组件的重新渲染。 使用场景 从应用逻辑使用AppStorage和LocalStorage AppStorage是单例它的所有API都是静态的使用方法类似于LocalStorage对应的非静态方法。 AppStorage.SetOrCreate(PropA, 47);let storage: LocalStorage new LocalStorage({ PropA: 17 }); let propA: number AppStorage.Get(PropA) // propA in AppStorage 47, propA in LocalStorage 17 var link1: SubscribedAbstractPropertynumber AppStorage.Link(PropA); // link1.get() 47 var link2: SubscribedAbstractPropertynumber AppStorage.Link(PropA); // link2.get() 47 var prop: SubscribedAbstractPropertynumber AppStorage.Prop(PropA); // prop.get() 47link1.set(48); // two-way sync: link1.get() link2.get() prop.get() 48 prop.set(1); // one-way sync: prop.get() 1; but link1.get() link2.get() 48 link1.set(49); // two-way sync: link1.get() link2.get() prop.get() 49storage.get(PropA) // 17 storage.set(PropA, 101); storage.get(PropA) // 101AppStorage.Get(PropA) // 49 link1.get() // 49 link2.get() // 49 prop.get() // 49从UI内部使用AppStorage和LocalStorage StorageLink变量装饰器与AppStorage配合使用正如LocalStorageLink与LocalStorage配合使用一样。此装饰器使用AppStorage中的属性创建双向数据同步。 AppStorage.SetOrCreate(PropA, 47); let storage new LocalStorage({ PropA: 48 });Entry(storage) Component struct CompA {StorageLink(PropA) storLink: number 1;LocalStorageLink(PropA) localStorLink: number 1;build() {Column({ space: 20 }) {Text(From AppStorage ${this.storLink}).onClick(() this.storLink 1)Text(From LocalStorage ${this.localStorLink}).onClick(() this.localStorLink 1)}} }以持久化方式订阅某个事件并接收事件回调 推荐使用持久化方式订阅某个事件并接收事件回调可以减少开销增强代码的可读性。 // xxx.ets import emitter from ohos.events.emitter;let NextID: number 0;class ViewData {title: string;uri: Resource;color: Color Color.Black;id: number;constructor(title: string, uri: Resource) {this.title title;this.uri urithis.id NextID;} }Entry Component struct Gallery2 {dataList: ArrayViewData [new ViewData(flower, $r(app.media.icon)), new ViewData(OMG, $r(app.media.icon)), new ViewData(OMG, $r(app.media.icon))]scroller: Scroller new Scroller()private preIndex: number -1build() {Column() {Grid(this.scroller) {ForEach(this.dataList, (item: ViewData) {GridItem() {TapImage({uri: item.uri,index: item.id})}.aspectRatio(1).onClick(() {if (this.preIndex item.id) {return}var innerEvent { eventId: item.id }// 选中态黑变红var eventData {data: {colorTag: 1}}emitter.emit(innerEvent, eventData)if (this.preIndex ! -1) {console.info(preIndex: ${this.preIndex}, index: ${item.id}, black)var innerEvent { eventId: this.preIndex }// 取消选中态红变黑var eventData {data: {colorTag: 0}}emitter.emit(innerEvent, eventData)}this.preIndex item.id})}, (item: ViewData) JSON.stringify(item))}.columnsTemplate(1fr 1fr)}} }Component export struct TapImage {State tapColor: Color Color.Black;private index: number;private uri: Resource;onTapIndexChange(colorTag: emitter.EventData) {this.tapColor colorTag.data.colorTag ? Color.Red : Color.Black}aboutToAppear() {//定义事件IDvar innerEvent { eventId: this.index }emitter.on(innerEvent, this.onTapIndexChange.bind(this))}build() {Column() {Image(this.uri).objectFit(ImageFit.Cover).border({ width: 5, style: BorderStyle.Dotted, color: this.tapColor })}} }以下示例为消息机制方式订阅事件会导致回调监听的节点数较多非常耗时不推荐以此来实现应用代码。 // xxx.ets class ViewData {title: string;uri: Resource;color: Color Color.Black;constructor(title: string, uri: Resource) {this.title title;this.uri uri} }Entry Component struct Gallery2 {dataList: ArrayViewData [new ViewData(flower, $r(app.media.icon)), new ViewData(OMG, $r(app.media.icon)), new ViewData(OMG, $r(app.media.icon))]scroller: Scroller new Scroller()build() {Column() {Grid(this.scroller) {ForEach(this.dataList, (item: ViewData, index?: number) {GridItem() {TapImage({uri: item.uri,index: index})}.aspectRatio(1)}, (item: ViewData, index?: number) {return
http://www.w-s-a.com/news/287248/

相关文章:

  • logo模板下载网站推荐哪家网站开发培训好
  • 做外贸网站效果图页面关键词优化
  • 广平网站建设成都活动轨迹
  • 小型网站网站建设需要网络公司是什么行业
  • 滑动 手机网站 代码网页制作与设计讨论
  • 自己做网站处理图片用什么软件wordpress html5支持
  • 校园网站怎么建软文文案范文
  • 中国建设官方网站如何创建自己的软件
  • 来宾住房与城乡建设网站天津西青区怎么样
  • 西安网站建设培训班鄂州网页定制
  • 西部数码网站备份自己怎么做网站啊
  • h5网站开发用什么软件制作公司网站建设代理怎么做
  • 网站建设资料准备网上购物app有哪些
  • 沧州做网站优化哪家公司便宜国内百度云网站建设
  • 网站的最近浏览 怎么做龙岩市人才网最新招聘信息
  • 网站建设需要找工信部吗网站开发账务处理
  • 做那种的视频网站个体工商网站备案
  • 推广网官方推广网站中国建设招聘信息网站
  • 医院网站建设需要多少钱网络营销渠道可分为哪几种
  • 怎么取网页视频网站元素计算机专业论文网站开发
  • 上海网站建设备案号怎么恢复wordpress打开页面空白
  • 30个做设计的网站企业设计网站
  • 招生网站开发的背景创意 wordpress
  • 网站备案资料查询小型企业管理系统软件
  • 温州网站建设维护怎么做好网站开发、设计
  • 佛山 做网站公司有哪些网站排名忽然不见了
  • 广告网站建设最专业东莞大朗网站设计
  • 网站做流量的论坛贴吧分销商城系统源码
  • 新手建立网站的步骤网站建设费怎么入分录
  • 哪里建网站性价比高做网站赚取广告费