第四篇: 整理工程 - 模块化
第三篇:接入第三方库,使用Axios网络请求,PullToRefresh上拉刷新下拉加载
背景:鸿蒙0基础探索。
愿景:本系列旨在用简单直接的方式从让鸿蒙app先从0-1。
理由:有产出更有继续下去的动力。
记录:每篇文章保留下探索的过程。
PS: 不教写代码,不封装,业务代码能力因人而异。(关键我不会啊😁)
为什么第四篇就开始探索模块化:
机缘巧合,在我们iOS项目中集成的某个三方库有鸿蒙sdk版本,我好奇就下载来看看。(ps:这发生在我在开始探索鸿蒙之前,那肯定啥也看不懂。)太多的配置文件晕头转向的, 本来是在备忘录自己简单记一记,不知道抽了哪门子疯想试试MarkDown,于是乎有了我的第一篇文章捋一捋鸿蒙配置文件JSON5文件(ps: 建议先扫一眼这一篇), 顺势才有了这个系列的文章。
在记录配置文件的时候,理解到一个概念天生模块化, 在iOS中想做个模块化,那得费了老劲,探索的文章,框架也是五花八门。但鸿蒙把模块化变得异常方便。
既然这么好用,那势必会成为日常开发必备知识点
探索
在经历了前三篇,创建应用Tabs, Navigation, Grid, 引入三方库,axios网络请求之后,相当于已经有了一个可开发业务的雏形,所以模块化成为我们的第四篇也并不意外。预期探索以下几点:
- 如何新建模块,顺势建一个公用模块。
- 把我们前三篇做的内容,拆成各自独立的模块
- 如何模块间相互跳转。
创建一个新的模块
书接上回, 老样子,遇事不决先去文档搜搜看。于是找到了这个添加/删除Module
我按文档新建了一个名common的模块,新建 - run,很顺利。 但,跑出来怎么是Hello world。 我其他的页面去哪里了,怎么回事。 于是乎我又新建了一个名为Mine的模块,果然启动之后,直接初始化的Mine的Index.ets。
在我探索了半天无果之后,我很费解,文档写的挺简洁,不应该有很复杂的坑才对啊,于是乎我返回看文档,一句话引起了我的注意
每一个Module都可以独立进行编译和运行。一个应用/服务通常会包含一个或多个Module
独立运行?。没错,就是它, 这是可选的,新建模块,默认直接选中了新模块,可别run错自己想运行模块了
该写点什么在里面。
在第三篇的时候,我们展示过空态页(实际上就是一个自定义组件)。那空态页就挺适合放进common里,能让其他模块也用。
我们Common模块新建一个EmptyComponent.ets文件
// 通用 - 空布局
@Component
export struct EmptyComponent {
build() {
Column({ space: 30}) {
Image($r('app.media.startIcon'))
.width(50)
.height(50)
Text("暂无数据")
Button("点我重试")
}
}
}
我们在Common模块使用它看看效果。不错,符合预期。
怎么给其他模块用呢。
**我卡住了, 探索了很久就是没能正确的导入,我找到[工程管理]那一篇,他成功了。他的方式新建方式又指向是[开发静态共享包]这一篇。**
于是乎,我转向先去了解一下,动态,静态,har, hsp这些是什么概念。
HAP, HAR, HSP 是什么。(看文档搅不清楚的时候回头来看看)
工程目录管理,需要了解,看了能知道很多demo为什么这样建文件夹。
下面几个概念还是还是要去官方文档看看,都有什么使用限制之类。
- HAP: 是应用安装和运行的基本单元。HAP包是由代码、资源、第三方库、配置文件等打包生成的模块包,其主要分为两种类型:entry和feature。文档链接
说明
- entry(轻量工程推荐): 在一个工程中同一个设备类型只支持一个Entry类型的模块。相当于一个产品,有点像iOS中的target
- feature(复杂工程使用):
这就有点像我之前认为的模块化/组件化的,把业务拆成多个模块,每个模块可以单独运行
- HAR:是静态共享包,可以包含代码、C++库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。这可能有点像iOS的.a库。 文档链接
使用场景
- HSP: 动态库,运行时按需加载。 这可能有点像iOS的.framwork 文档链接
说明
- 应用内HSP:在编译过程中与应用包名(bundleName)强耦合,只能给某个特定的应用使用。
- 集成态HSP:构建、发布过程中,不与特定的应用包名耦合;使用时,工具链支持自动将集成态HSP的包名替换成宿主应用包名。
使用场景
- 多个HAP/HSP共用的代码和资源放在同一个HSP中,可以提高代码、资源的可重用性和可维护性,同时编译打包时也只保留一份HSP代码和资源,能够有效控制应用包大小。
- HSP在运行时按需加载,有助于提升应用性能。
- 同一个组织内部的多个应用之间,可以使用集成态HSP实现代码和资源的共享。
工程管理
HAR(静态库,Static Library)的方式创建模块
按照前面我们创建common时所选择的Empty Ability,去common的配置->module.json5中看到. type他是feature。而feature就对应我们上面所说的HAP的两种类型之一,在HAP文档中写了这样一句"不支持导出接口和ArkUI组件,给其他模块使用。"
· 卒 · 推倒重来。
{
"module": {
"name": "common",
"type": "feature"
}
}
那我们删除上面新建的common模块和Mine模块,跟着这篇工程管理去调整目录,模块使用HAR静态包的方式。
PS: 几种模式的切换,可以通过直接修改module.json5配置文件的一些设置,很方便。如果是已有的模块想切换模式可以探索一下。
我们也使用小节中介绍的common、features、product三层工程结构把我们第三篇的代码整理一下,为了练习,我决定把HAP,HAR,HSP的方式都体验一下。。
/application
├── common # 公共特性目录 **(我使用HAR(又称静态库,Static Library)方式)**
│
├── features # 功能模块目录
│ ├── home # 首页模块 (我使用HAR)
│ ├── cat # 使用网络请求猫图片模块 (我使用HAR)
| ├── pokemon # 宠物小精灵模块 ** (我使用HSP, (又称动态库,Shared Library)) **
│ └── mine # 我的 ** (我使用HSP) **
│
└── product # 产品层目录
├── entry # 默认类目录(使用HAP,entry特性),这个模块我就不重命名了,系统新建工程自带的就是这
├── testCat # 单独运行cat这个模块,(使用HAP,feature特性)
└── ...
- 新建Module: 名为common的HAR,并在里面写一个一个空布局组件EmptyComponent.ets。
- 在common/index.ets里(注意这个index文件的路径),导出要提供给其他模块使用的内容,类似于头文件的功能。比如我导出
export { EmptyComponent } from './src/main/ets/components/EmptyComponent'
- 新建一个product文件夹,把entry模块拖入到product中。
- entry模块添加依赖,将common模块添加为依赖。 在oh-package.json5中添加。 同文档中。路径的话按你实际路径来。比如我是
"@ohos/common": "file:../../common"
- 在entry中使用common中提供的组件。比如在猫.ets中使用.
import { EmptyComponent } from '@ohos/common'
然后我在网络请求如果数据为空就是这样,符合预期。
继续,我们把entry里之前写的文件,拆成四个功能模块,放到feature下。
我们新建一个home
模块
- 把HomePage.ets, HomeDetail.ets拖到home模块的components文件夹下。
- 按上面的exprot导出暴露给其他模块使用, 其他模块添加依赖, import导入使用。
重点:因为存在系统路由表的跳转,需要在module.json5中指定routerMap, 并且在resources中新建资源文件profile, 新建rout_map.json文件,里面配置路由表。
不清楚可以看我的第一篇
还是再附个图吧。
同理,我们继续新建一个cat
模块
- 先同上完操作
- 因为在CatSqualPage中还使用资源文件rawfile,所以我还要再把图添加进来。
- 因为在CatSqualPage中还使用的common下的空布局,所以cat模块也需要添加
common
模块的依赖。这一步我遇到了问题,我第一次导入不到正确的页面。后来一步步排查,什么也没改,只是把common模块去掉又导入了一遍就好了。有可能是模拟器,或者存在什么缓存之类的。
HSP(动态库, shared Library) (给我感觉常规开发用HAR模式的应该更多,建议使用静态库)。
我们继续新建一个pokomon
模块, 但这次我们选择HSP的方式。
- 同上面静态的操作一样
- 有一点区别就是,可以选择他编译,run,但不能独立成可视化的。
重要:还有一个问题,导动态库的时候可以要先把动态库run一次,可能因为我在mine模块的时候没编译,导致启动entry就闪退。试了半天
那我们继续新建一个mine
模块, 使用HSP的方式,不再赘述。
至此,我们也完成了common、features、product三层工程结构。这时候,我的entry里就只剩下index.ets这个导航栏的壳子。entry依赖了1个公共模块和4个业务模块。
HAP(模式:feature, Empty Ability)
我们现在试试,新建一个testCats
模块, 使用HAP的方式,选择Empty Ability,feature模式。
这个东西能干嘛用:我想到一个场景。 模块化开发和调试
举例:我在testCats模块里,依赖pokemon模块,单独的运行和调试pokemon里面的内容。
import { RankPage } from '@ohos/pokemon'
@Entry
@Component
struct Index {
// 导航栈,用来保存记录页面的容器。
private navPathStack: NavPathStack = new NavPathStack()
aboutToAppear() {
// 全局设置一个NavPathStack
AppStorage.setOrCreate("yxb_navPathStack", this.navPathStack)
}
build() {
RelativeContainer() {
Navigation(this.navPathStack) {
RankPage()
}
}
.height('100%')
.width('100%')
}
}
运行起来之后就是这样:
感悟一下
- 这在大型项目 - 分项目组开发中也太好用了。
- 然后再把自己的独立模块发布出去,把本地依赖切换成远程依赖。
- 每个项目组只有维护自己独立的模块,在主项目里可以跑完整项目,可以跑自己的模块
- 自己项目组开发时,把远程依赖切成本地依赖,又方便开发,又方便调试。真是方便
总结
在iOS开发中,想要做到这样方便的模块化,讲道理给我一个月我也学不会,还会出各种能力解决不了的问题。
而鸿蒙的模块化,我一个小白,只用了2天,还是一边探索,一边写文章。
另一个关键,放在别的平台搭建这样的模块化需要大量的代码,还有依赖优秀的三方库,而这些随时代发展都存在,方式落后,三方放弃维护,极难升级改造。
而鸿蒙,天生自带模块化,官方维护!!!完全不用担心撂挑子。 致敬
看看效果
因为模块化的关系,主模块只有一个index的壳,各模块独立,结构很清晰。
Demo代码
最后附上demo地址,第四篇内容 ps: demo中把pokemon和mine模块调整为har静态库了,减少和我遇到一样的问题。