1.HarmonyOS 模块化设计
1.1. 概述
组件化一直是移动端比较流行的开发方式,有着编译运行快,业务逻辑分明,任务划分清晰等优点,HarmonyOs组件化的使用,有利于模块之间的解耦及大型项目的共同开发;与Android端的组件化相比,HarmonyOS的组件化可以说实现起来就颇费一番周折,因为HarmonyOS经过更新迭代之后,最新的开发方式已经和之前兼容AOSP阶段有着很大的区别,以及采用全新的ArkTs语言的开发方式,想实现组件化,就需要另谋途径。
在以往的Android组件化实现中,我们直接可以在gradle里进行自定义配置相关参数,执行application和library,以及其他的参数信息,来实现一个可运行模块和依赖模块的动态转换,这是非常方便的,但是,在HarmonyOS中,构建文件中,除了使用系统配置的之外,是不支持自定义的,起码当前是不支持的,也就是说,我们无法通过在构建里来实现组件化运行的切换。
这就在一定程度上阻碍了组件化的配置,虽然我们可以按照动态包的模式,拆分出独立的业务模块,形成模块化开发,但是在业务逻辑复杂的项目,很多的模块,同时编译运行,无疑来说是耗时的,远远比不上组件化方式的开发效率,基于此,能够探索出组件化的可行性,确实是很有必要的。
1.2. 组件化好处
(1)提高代码复用性:组件化允许将应用程序的不同功能模块化,使得这些模块可以在不同的项目中重复使用,从而提高开发效率并减少重复工作。
(2) 降低组件间的耦合:通过组件化的规则将代码拆分成不同的模块,实现高内聚、低耦合,使得代码更易于维护,降低了模块间的依赖,减少了潜在的错误和问题。
(3)提升开发效率:组件化使得开发团队可以并行工作,每个团队可以专注于自己的组件,独立开发和维护,这样可以加快开发进度,提高整体的开发效率。
(4) 改善代码质量:组件化鼓励开发者编写清晰、模块化的代码,有助于提高代码的可读性和可维护性,从而提升代码质量。
(5) 便于扩展和迭代:组件化架构使得添加新功能或改进现有功能变得更加容易,有助于快速响应市场变化和用户需求。
(6) 隔离技术栈:不同的组件可以使用不同的技术栈,而不会相互影响,使得技术选型更加灵活。
(7) 独立开发/维护/发布:组件化允许每个组件独立开发、维护和发布,使得更新和迭代更加灵活。
(8)提高编译/构建速度:组件化使得编译和构建过程更加高效,因为只需要编译和构建相关的组件,而不是整个项目。
(9) 管控代码权限:组件化允许更好地控制代码权限,通过将代码分散到不同的仓库中,可以限制对特定组件的访问和修改。
(10)管理版本变更:组件化使得管理版本变更变得更加容易,因为每个组件都有明确的版本,可以更容易地跟踪和控制版本更新。
组件化是解决单一工程架构开发中问题的有效方法,它通过将大型项目拆分成更小、更易于管理的模块,提高了开发效率和代码质量。然而,组件化也带来了一些挑战,如组件粒度的划分、组件间依赖关系的管理以及跨技术栈通信等。为了实现高质量的组件化项目,需要遵循一些实践规范和原则,如组件拆分原则、组件间依赖管理以及质量保障措施。
1.3. 项目的目录结构
其中features目录下是组件/模块,包含不同的功能分区,entity是项目的主入口也就是hap包,commons目录下有3个har组件,分别是utils:所有的帮助类、uicomponents:项目中需要用到的自定义UI组件等、RouterModule:项目的路由(承载了整个项目跨组件通信的能力)
1.4. 项目创建
(1)首先创建一个项目工程,点击开发工具DevEco-Stdio的File选项,选择New然后点击Create Project。
(2)选择创建一个EmptyAbility,然后直接下一步,创建工程。
(3)工程主要包括一个entry模块,可以理解为Android的app主模块。
1.5. 公共库创建及使用(har)
创建公共库Common组件。
(1)在工程目录中,单机鼠标右键,选择New选项,然后在弹出的页面中选择Module。
(2)在弹出的页面中,选择创建一个静态库Static Library。
(3)然后点击Next,修改静态库的名称为myHar。
(4)点击完成后,编译器会自动在项目的根目录的build-profile.json5文件中添加myHar模块。
(5)添加项目依赖,myHar作为公共库,需要提供给其他模块使用,这里以entry为例,添加模块依赖,如下图所示。
javascript
{
"name": "entry",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"license": "",
"dependencies": {
// 此处也可以是以当前oh-package.json5所在目录为起点的相对路径。
//"myhar": "file:./libs/myHar.har",
//此处也可以是以当前oh-package.json5所在目录为起点的相对路径
"myhar": "file:../myHar"
}
}
(5)至此,一个公共库myHar就创建完成了。
1.6. 功能模块创建使用(hsp)
一个功能复杂庞大的app不仅有公共模块,还有许多子模块构建而成。创建流程如下:
(1)在工程目录中,单机鼠标右键,选择New选项,然后在弹出的页面中选择Module。
(2)在弹出的页面中,选择创建一个共享库Shared Library。
(3)然后点击Next,修改静态库的名称为loginLib。
(4)点击完成后,编译器会自动在项目的根目录的build-profile.json5文件中添加loginLib模块。
(4)添加项目依赖,login作为子模块,需要被entry依赖调用,添加模块依赖,如下图所示。
(5)到目前为止子模块也创建完成。
1.7. 模块路由跳转
前面创建分别创建了公共库和子模块,如何从主模块entry跳转到子模块loginLib,或者子模块之间互相跳转呢?
(1)HarmonyOs在页面跳转时提供了router实现路由跳转。需要跳转的目标页面必须满足两个条件,@Entry修饰struct 和路由注册。被@Entry修饰的struct才是一个独立页面。同时需要在main_pages.json中注册路由。
(2)在myHar中定义完整路径。不同子模块都会依赖公共静态库myHar。然后在myHar中定义目标页面的全路径。文档中心
全路径主要由三部分组成。分别是@bundle: 代表模版。 然后是 包名 com.szy.mymodule。最后是模块名+目标页面的全路径。
javascript
export class PageConstant {
static readonly LOGIN_PAGE: string =
"@bundle:com.szy.mymodule/loginLib//ets/pages/Index.ets";
}
(3)使用export关键字导出 ConstantRouter类,提供给其他模块调用。
(4)entry引入ConstantRouter类,调用类中定义的路由。
javascript
import { router } from '@kit.ArkUI';
import { RouterConstant } from 'myhar';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { RouterParams } from 'zzslib'
@Entry
@Component
struct Index {
@State message: string = '跳转';
build() {
RelativeContainer() {
Text(this.message)
.id('HelloWorld')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
.onClick(() => {
let bundleName = (getContext(this) as common.UIAbilityContext).applicationInfo.name;
router.pushUrl({
// url:"@bundle:com.szy.mymodule/library/src/main/ets/pages/Index"
url: '@bundle:com.szy.mymodule/library/ets/pages/Index'
//url: `@bundle:${bundleName}/library/src/main/ets/pages/Index`
}).then(() => {
console.log('push page success');
}).catch((err: BusinessError) => {
console.error('pushUrl failed, code is' + err.code + ', message is' + err.message);
})
})
}
.height('100%')
.width('100%')
}
}