0基础,从0到1探索鸿蒙应用,不一样的探索过程。第四篇

233 阅读10分钟

第四篇: 整理工程 - 模块化

第一篇:创建一个应用,使用Tabs和导航栏结构

第二篇:使用网格Grid与列表list

第三篇:接入第三方库,使用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。

image.png

在我探索了半天无果之后,我很费解,文档写的挺简洁,不应该有很复杂的坑才对啊,于是乎我返回看文档,一句话引起了我的注意

每一个Module都可以独立进行编译和运行。一个应用/服务通常会包含一个或多个Module

独立运行?。没错,就是它, 这是可选的,新建模块,默认直接选中了新模块,可别run错自己想运行模块了

image.png

该写点什么在里面。

在第三篇的时候,我们展示过空态页(实际上就是一个自定义组件)。那空态页就挺适合放进common里,能让其他模块也用。

我们Common模块新建一个EmptyComponent.ets文件

// 通用 - 空布局
@Component
export struct EmptyComponent {
  build() {
    Column({ space: 30}) {
      Image($r('app.media.startIcon'))
        .width(50)
        .height(50)
      Text("暂无数据")
      Button("点我重试")
    }
  }
}

我们在Common模块使用它看看效果。不错,符合预期。

image.png

怎么给其他模块用呢。

**我卡住了, 探索了很久就是没能正确的导入,我找到[工程管理]那一篇,他成功了。他的方式新建方式又指向是[开发静态共享包]这一篇。**

于是乎,我转向先去了解一下,动态,静态,har, hsp这些是什么概念。

HAP, HAR, HSP 是什么。(看文档搅不清楚的时候回头来看看)

工程目录管理,需要了解,看了能知道很多demo为什么这样建文件夹。

下面几个概念还是还是要去官方文档看看,都有什么使用限制之类。

  • HAP: 是应用安装和运行的基本单元。HAP包是由代码、资源、第三方库、配置文件等打包生成的模块包,其主要分为两种类型:entry和feature。文档链接
说明
  • entry(轻量工程推荐): 在一个工程中同一个设备类型只支持一个Entry类型的模块。相当于一个产品,有点像iOS中的target
  • feature(复杂工程使用): 这就有点像我之前认为的模块化/组件化的,把业务拆成多个模块,每个模块可以单独运行
  • HAR:是静态共享包,可以包含代码、C++库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。这可能有点像iOS的.a库。 文档链接
使用场景
  • 作为二方库,发布到OHPM私仓,供公司内部其他应用使用。
  • 作为三方库,发布到OHPM中心仓,供其他应用使用。
  • 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特性)
  └── ...

image.png

  1. 新建Module: 名为common的HAR,并在里面写一个一个空布局组件EmptyComponent.ets。
  2. 在common/index.ets里(注意这个index文件的路径),导出要提供给其他模块使用的内容,类似于头文件的功能。比如我导出export { EmptyComponent } from './src/main/ets/components/EmptyComponent'
  3. 新建一个product文件夹,把entry模块拖入到product中。
  4. entry模块添加依赖,将common模块添加为依赖。 在oh-package.json5中添加。 同文档中。路径的话按你实际路径来。比如我是 "@ohos/common": "file:../../common"
  5. 在entry中使用common中提供的组件。比如在猫.ets中使用. import { EmptyComponent } from '@ohos/common'

然后我在网络请求如果数据为空就是这样,符合预期。

继续,我们把entry里之前写的文件,拆成四个功能模块,放到feature下。

我们新建一个home模块

  1. 把HomePage.ets, HomeDetail.ets拖到home模块的components文件夹下。
  2. 按上面的exprot导出暴露给其他模块使用, 其他模块添加依赖, import导入使用。
  3. 重点:因为存在系统路由表的跳转,需要在module.json5中指定routerMap, 并且在resources中新建资源文件profile, 新建rout_map.json文件,里面配置路由表。不清楚可以看我的第一篇

还是再附个图吧。

image.png

同理,我们继续新建一个cat模块

  1. 先同上完操作
  2. 因为在CatSqualPage中还使用资源文件rawfile,所以我还要再把图添加进来。
  3. 因为在CatSqualPage中还使用的common下的空布局,所以cat模块也需要添加common模块的依赖。这一步我遇到了问题,我第一次导入不到正确的页面。后来一步步排查,什么也没改,只是把common模块去掉又导入了一遍就好了。有可能是模拟器,或者存在什么缓存之类的。

HSP(动态库, shared Library) (给我感觉常规开发用HAR模式的应该更多,建议使用静态库)。

我们继续新建一个pokomon模块, 但这次我们选择HSP的方式。

  1. 同上面静态的操作一样
  2. 有一点区别就是,可以选择他编译,run,但不能独立成可视化的。
  3. 重要:还有一个问题,导动态库的时候可以要先把动态库run一次,可能因为我在mine模块的时候没编译,导致启动entry就闪退。试了半天

image.png

那我们继续新建一个mine模块, 使用HSP的方式,不再赘述。

至此,我们也完成了common、features、product三层工程结构。这时候,我的entry里就只剩下index.ets这个导航栏的壳子。entry依赖了1个公共模块和4个业务模块。

image.png

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的壳,各模块独立,结构很清晰。

录屏2024-08-30 17.40.08.gif

2024-08-30 17.40.02.gif

Demo代码

最后附上demo地址,第四篇内容 ps: demo中把pokemon和mine模块调整为har静态库了,减少和我遇到一样的问题。