摘要

单 entry 模块存放全部页面、工具、组件会导致工程臃肿、编译缓慢、多人协作代码冲突、功能无法复用。HAR 静态共享库用于拆分独立业务模块、通用基础能力库,实现代码解耦、多工程复用、分层隔离。API Version23 重构 HAR 依赖导入、资源互通、跨模块类型导出、编译缓存机制,修复低版本 HAR 资源找不到、类型导入爆红、跨模块工具循环依赖、图片 rawfile 无法访问、多 HAR 编译冲突等兼容缺陷。旧项目升级 API23 后常出现:导入 HAR 组件提示不存在、模型 interface 跨模块无法识别、rawfile 图片加载空白、同时引入多个 HAR 编译报错。本文基于 DevEco Studio API23+,完整讲解 HAR 库创建、依赖配置、跨模块资源 / 工具 / 组件调用、循环依赖规避、多模块分层架构实战,封装基础通用能力 HAR、业务笔记模块 HAR 两套完整示例,输出分包分层规范、导出约束、编译优化方案,为大型鸿蒙项目模块化拆分提供标准化模板。

关键词

OpenHarmony;ArkTS;API Version23;HAR;静态共享库;模块化拆分;多模块工程;代码复用;分层架构

一、引言

1.1 HAR 分包开发背景

小型课程设计单 entry 模块可正常开发,但中大型项目、多人协作、多终端复用场景存在明显痛点:

  1. 所有代码堆在 entry,文件上千,打开工程卡顿、编译耗时久;
  2. 通用工具、自定义组件、数据模型无法给其他项目复用;
  3. 业务耦合严重,删除某一功能会牵连大量页面代码;
  4. 多人开发容易出现文件命名冲突、git 合并大量冲突;
  5. 功能无法独立版本管控,基础能力更新全项目重新编译。

HAR(Harmony Archive)静态共享库用于解决以上问题,特点:

  1. 静态编译打包,编译时合并至主模块,运行无性能损耗;
  2. 可拆分通用基础库、独立业务模块库;
  3. 支持导出 ets 工具、自定义组件、类型、常量、资源;
  4. 支持多个 HAR 互相依赖,主 entry 统一引入所有 HAR。

OpenHarmony API Version23 HAR 核心升级变更:

  1. 严格控制跨模块导出权限,未 export 的类 / 函数外部模块不可访问;
  2. 统一 rawfile、media 资源跨 HAR 访问路径规则,修复图片资源加载失败;
  3. 优化循环依赖检测,编译阶段直接抛出依赖环报错,便于定位;
  4. 完善类型导出传递,HAR 导出 interface 后 entry 可正常识别类型;
  5. 分模块编译缓存,修改单个 HAR 仅重新编译该模块,大幅提升编译速度;
  6. 区分 HAR 静态库与 HSP 动态共享库,简化小型项目分包成本。

大量旧项目升级后跨模块导入爆红、资源加载空白,根源是未规范导出、资源路径写法错误、存在循环依赖,因此掌握 HAR 分包是企业级大型项目必备架构能力。

1.2 开发环境与分层场景

开发工具:DevEco Studio 5.0+ 适配系统:OpenHarmony API23、HarmonyOS NEXT 模块类型:entry(主应用模块)、多个 har 静态库模块 分包示例:

  • har_base:全局工具、权限、网络、文件、通知、动画公共底层能力
  • har_note:笔记独立业务(页面、CRUD、列表组件、数据库模型) 测试场景:主 entry 导入 har_base 工具、entry 引入 har_note 页面、HAR 之间互相依赖、跨模块资源访问、类型共享

二、API23+ HAR 核心约束与基础配置

2.1 HAR 与 entry 核心区别

表格

模块 用途 能否独立运行 页面注册 Ability
entry 主应用入口 可独立安装运行 module.json5 注册 pages 可定义 EntryAbility
HAR 静态库 代码 / 资源共享分包 不可单独运行 禁止注册 pages 不允许定义 Ability

2.2 module.json5 HAR 基础模板(har_base 示例)

json

{
  "module": {
    "name": "har_base",
    "type": "har",
    "description": "全局基础工具库",
    "deviceTypes": ["phone"],
    "deliveryWithInstall": false,
    "installationFree": false
  }
}

2.3 主 entry 引入 HAR 依赖(entry module.json5)

json

"dependencies": [
  {
    "name": "har_base",
    "version": "1.0.0",
    "scope": "shared"
  },
  {
    "name": "har_note",
    "version": "1.0.0",
    "scope": "shared"
  }
]

2.4 跨模块导入路径规则

  1. 导入 HAR 内 ets 代码:import xxx from '@ohos:模块名/相对路径'
  2. HAR 内部互相导入:同标准相对路径../
  3. HAR 资源访问:模块名、r ('har 模块名:rawfile/xxx')

2.5 API23 强制约束

  1. HAR 中所有需要外部访问的类、函数、interface、常量必须加 export,私有代码禁止导出;
  2. HAR 不能定义 UIAbility、不能在 module.json5 注册 pages 页面;页面只能存放于 entry;
  3. 禁止双向循环依赖(har_base 依赖 har_note 同时 har_note 依赖 har_base);
  4. 动态权限、数据库初始化上下文仅 entry 可提供,HAR 工具仅接收外部传入 context;
  5. HAR 不支持 AppStorage 全局初始化,全局状态统一在 entry EntryAbility 管理;
  6. 多个 HAR 同名资源会产生覆盖冲突,资源文件名增加模块前缀区分。

三、基础最简 HAR 导入示例

3.1 har_base 内通用工具 har_base/src/main/ets/utils/toast_util.ets

ets

import promptAction from '@ohos.promptAction'
// 必须export导出,entry才能导入使用
export function showToast(msg: string) {
  promptAction.showToast({message: msg})
}

3.2 entry 页面跨模块导入 HAR 工具

ets

// 固定格式 @ohos:har模块名/文件相对路径
import { showToast } from '@ohos:har_base/utils/toast_util'

@Entry
@Component
struct Index {
  build() {
    Button("调用HAR封装Toast")
      .onClick(()=> showToast("来自基础HAR库工具"))
  }
}

3.3 HAR 共享数据模型 interface

har_base/src/main/ets/model/NoteModel.ets

ets

export interface NoteItem {
  id: number
  title: string
  content: string
}

entry 页面使用:

ets

import { NoteItem } from '@ohos:har_base/model/NoteModel'

@State note: NoteItem = {id:1, title:"测试", content:"HAR共享实体"}

四、完整分层多模块实战架构(两套 HAR 分包完整代码)

整体工程目录结构

plaintext

项目根目录
├─ entry(主应用,页面、EntryAbility、路由跳转)
├─ har_base(基础底层HAR:工具、常量、类型、通用组件)
│  └─ src/main/ets
│     ├─ utils 网络/文件/权限/通知/动画全套工具
│     ├─ model 全局interface、常量Constant
│     └─ components 通用弹窗、加载组件
└─ har_note(笔记业务HAR:数据库操作、笔记专用组件、业务逻辑)
   └─ src/main/ets
      ├─ model 笔记业务实体
      ├─ db 笔记RDB封装
      └─ components 笔记条目ListItem组件

4.1 实战一:基础底层 HAR har_base 通用导出汇总

har_base/src/main/ets/model/Constant.ets(全局常量导出)

ets

export const STORAGE_KEY = {
  IS_LOGIN: "isLogin",
  DARK_MODE: "darkMode"
}

export const PAGE_ROUTE = {
  INDEX: "pages/index/index",
  NOTE_LIST: "pages/note/NoteList"
}
har_base/src/main/ets/components/LoadingDialog.ets(通用组件导出)

ets

@Component
export struct LoadingDialog {
  build() {
    Column() {
      Text("加载中...")
    }
    .width(120)
    .height(100)
    .backgroundColor("#00000099")
    .borderRadius(12)
  }
}
har_base/src/main/ets/utils/permission_util.ets(权限工具单例导出)

ets

import accessToken from '@ohos.accessToken'
import common from '@ohos.app.ability.common'
import { showToast } from './toast_util'

class PermissionUtil {
  private static ins: PermissionUtil
  private ctx: common.UIAbilityContext | null = null

  static getInstance() {
    if(!PermissionUtil.ins) PermissionUtil.ins = new PermissionUtil()
    return PermissionUtil.ins
  }

  setContext(ctx: common.UIAbilityContext) {
    this.ctx = ctx
  }

  async requestCamera(): Promise<boolean> {
    if(!this.ctx) return false
    const status = accessToken.checkAccessTokenSync(this.ctx, "ohos.permission.CAMERA")
    if(status === accessToken.GrantStatus.PERMISSION_GRANTED) return true
    const res = await accessToken.requestPermissions(this.ctx, ["ohos.permission.CAMERA"])
    if(res[0].grantStatus === accessToken.GrantStatus.PERMISSION_GRANTED) return true
    showToast("未授予相机权限")
    return false
  }
}

export default PermissionUtil.getInstance()

4.2 实战二:业务 HAR har_note 笔记数据库与条目组件

har_note/src/main/ets/db/note_db.ets(依赖 har_base 工具)

ets

import relationalStore from '@ohos.data.relationalStore'
import common from '@ohos.app.ability.common'
import { showToast } from '@ohos:har_base/utils/toast_util'
import { NoteItem } from '@ohos:har_base/model/NoteModel'

class NoteDbUtil {
  private static ins: NoteDbUtil
  private store: relationalStore.RdbStore | null = null
  private ctx: common.UIAbilityContext | null = null

  static getInstance() {
    if(!NoteDbUtil.ins) NoteDbUtil.ins = new NoteDbUtil()
    return NoteDbUtil.ins
  }

  setContext(ctx: common.UIAbilityContext) {
    this.ctx = ctx
  }

  async init() {
    if(!this.ctx) return
    const config: relationalStore.StoreConfig = {
      name: "note_db",
      securityLevel: relationalStore.SecurityLevel.S1
    }
    this.store = await relationalStore.getRdbStore(this.ctx, config)
    await this.store.executeSql(`CREATE TABLE IF NOT EXISTS note(id INTEGER PRIMARY KEY AUTOINCREMENT,title TEXT,content TEXT)`)
  }

  async addNote(title:string, content:string): Promise<number> {
    if(!this.store) return -1
    const bucket = {title, content}
    return await this.store.insert("note", bucket)
  }

  async queryList(page:number, size:number): NoteItem[] {
    if(!this.store) return []
    const pred = new relationalStore.RdbPredicates("note")
    pred.offset(page*size).limit(size)
    const rs = await this.store.query("note", ["id","title","content"], pred)
    let list: NoteItem[] = []
    while(rs.goToNextRow()){
      list.push({
        id: rs.getLong(0),
        title: rs.getString(1),
        content: rs.getString(2)
      })
    }
    rs.close()
    return list
  }
}

export default NoteDbUtil.getInstance()
har_note/src/main/ets/components/NoteItemCard.ets(导出笔记条目组件)

ets

import { NoteItem } from '@ohos:har_base/model/NoteModel'

@Component
export struct NoteItemCard {
  @Param data: NoteItem
  @Param onDelete: () => void

  build() {
    Row() {
      Column({space:4}).layoutWeight(1) {
        Text(this.data.title).fontSize(17)
        Text(this.data.content).fontColor("#666")
      }
      Button("删除").backgroundColor("#f56c6c")
        .onClick(()=> this.onDelete())
    }
    .width("100%")
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(10)
  }
}

4.3 实战三:entry 主页面调用两套 HAR 完整示例 pages/note/NoteList.ets

ets

// 导入基础HAR工具、常量、通用组件
import PermissionUtil from '@ohos:har_base/utils/permission_util'
import { LoadingDialog } from '@ohos:har_base/components/LoadingDialog'
import { PAGE_ROUTE } from '@ohos:har_base/model/Constant'
// 导入笔记业务HAR数据库、条目组件
import NoteDbUtil from '@ohos:har_note/db/note_db'
import { NoteItemCard } from '@ohos:har_note/components/NoteItemCard'
import { NoteItem } from '@ohos:har_base/model/NoteModel'
import RouterUtil from '../utils/router_util'

@Entry
@Component
struct NoteListPage {
  @State list: NoteItem[] = []
  ctrl: CustomDialogController | null = null

  async aboutToAppear() {
    // 给HAR工具传入页面上下文
    const ctx = getContext(this)
    PermissionUtil.setContext(ctx)
    NoteDbUtil.setContext(ctx)
    await NoteDbUtil.init()
    await this.loadData()
  }

  async loadData() {
    this.list = await NoteDbUtil.queryList(0,10)
  }

  async delItem(id: number) {
    await NoteDbUtil.deleteNote(id)
    await this.loadData()
  }

  build() {
    Column({space:12}) {
      Text("笔记管理(多HAR分包工程演示)").fontSize(22).padding(10)
      List({space:10}).layoutWeight(1) {
        ForEach(this.list, (item:NoteItem)=>{
          ListItem() {
            NoteItemCard({
              data: item,
              onDelete: ()=> this.delItem(item.id)
            })
          }
        })
      }
      Button("返回首页").onClick(()=> RouterUtil.replace(PAGE_ROUTE.INDEX))
    }
    .width("100%")
    .height("100%")
    .backgroundColor("#F5F5F5")
  }

  async aboutToDisappear() {
    await NoteDbUtil.closeDB()
  }
}

五、API23+ HAR 模块化分层与编译优化规范

5.1 分包分层规范(单向依赖,杜绝循环)

  1. 底层 har_base:无任何其他 HAR 依赖,提供通用基础能力;
  2. 业务 HAR(har_note、har_user):仅允许依赖 har_base,业务 HAR 之间禁止互相依赖;
  3. entry 主模块:可依赖所有 HAR,页面、Ability、全局状态全部放在 entry;
  4. 分层依赖流向:entry → 业务 HAR → 底层 har_base,单向不回流。

5.2 导出编码规范

  1. 所有对外可访问函数、类、组件、interface 必须添加 export;
  2. 仅暴露外部需要调用的 API,内部辅助工具私有不导出;
  3. 单例工具使用export default,常量 / 类型使用命名export const/interface
  4. HAR 内组件统一 @Component + export,entry 才能直接引入渲染。

5.3 资源访问规范

  1. HAR 图片资源:$r('har模块名:media/图片名')
  2. HAR rawfile 静态 HTML:$r('har模块名:rawfile/index.html')
  3. 不同 HAR 资源文件名增加模块前缀,避免同名覆盖;
  4. 通用全局图片统一存放 har_base media 目录,业务图片存放对应业务 HAR。

5.4 编译性能优化规范

  1. 按功能拆分多个小型 HAR,不创建单个超大 HAR;
  2. 修改仅涉及单一 HAR 时,仅该模块增量编译,缩短等待时间;
  3. 稳定不变的底层基础能力抽离为独立 HAR,极少重新编译;
  4. 调试阶段可临时移除未开发完成的 HAR 依赖,加快编译速度。

5.5 协作开发规范

  1. 不同开发人员负责独立 HAR 模块,代码隔离减少冲突;
  2. HAR 对外 API 统一维护导出文件,接口变更统一同步;
  3. 公共基础修改统一提交 har_base,各业务模块同步更新依赖。

六、API23 升级高频 HAR 兼容问题与解决方案

问题 1:entry 导入 HAR 代码提示爆红,找不到模块 解决:entry module.json5 添加对应 dependencies 依赖,导入路径使用@ohos:har名/标准前缀。

问题 2:HAR 内定义 interface,entry 接收时报类型不匹配 解决:实体 interface 统一放在底层 har_base 并 export 导出,业务 HAR、entry 统一从 base 导入。

问题 3:HAR 中读取 media 图片空白无法显示 解决:使用跨模块资源格式$r('har模块名:media/xxx'),不使用本地相对资源写法。

问题 4:编译提示循环依赖报错 cyclic dependency 解决:调整分层,业务 HAR 只依赖底层 base,业务 HAR 之间不互相导入。

问题 5:HAR 中调用 RDB/Preferences 提示上下文为空 解决:HAR 工具仅提供方法,由 entry 页面获取 context 传入 HAR 工具 setContext。

问题 6:多个 HAR 存在同名组件,渲染出现错乱 解决:组件、资源文件名增加 HAR 模块专属前缀,避免命名冲突。

七、总结

HAR 静态共享库是 OpenHarmony 大型项目标准化分包方案,API Version23 完善跨模块导出、资源访问、循环依赖检测、增量编译机制,解决单 entry 工程臃肿、代码无法复用、编译缓慢、多人协作冲突等核心痛点。 标准分层架构:底层基础 HAR 存放全局通用工具与基础组件,各业务独立拆分为业务 HAR,主 entry 仅负责页面与应用生命周期管理,严格单向依赖杜绝循环耦合。全部代码兼容 API23 与 HarmonyOS NEXT,适合课程设计大型综合项目、企业商用鸿蒙工程模块化改造。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐