news 2026/7/14 5:22:02

OpenHarmony HAR 静态共享组件库模块化拆分(API Version23 + 企业级分包方案)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenHarmony HAR 静态共享组件库模块化拆分(API Version23 + 企业级分包方案)

摘要

单 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,适合课程设计大型综合项目、企业商用鸿蒙工程模块化改造。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 5:20:00

HarmonyOS 放大缩小手势实现:从基础到进阶

双指捏合缩放已是移动应用交互的标配&#xff0c;HarmonyOS 通过 PinchGesture 提供了优雅的实现方案。本文将 2 万字详解其核心机制、实战代码与边界处理。一、手势交互概述在移动应用开发中&#xff0c;手势交互是提升用户体验的关键技术。用户通过直观的手势&#xff08;如滑…

作者头像 李华
网站建设 2026/7/14 5:19:04

遗传算法工程落地:种群设计、约束处理与收敛诊断实战

1. 这不是教科书里的遗传算法&#xff0c;而是我调试了73次后写下的实操笔记“遗传算法”这四个字在机器学习入门课里常被一笔带过——讲完选择、交叉、变异三个算子&#xff0c;配张种群迭代示意图&#xff0c;再提一句“模拟自然进化”&#xff0c;就算交差。但真正把它用在工…

作者头像 李华
网站建设 2026/7/14 5:13:28

基于Urho3D的体素引擎开发:从稀疏存储到贪婪网格化

1. 项目概述&#xff1a;为什么要在Urho3D上折腾体素引擎&#xff1f;如果你是一个对游戏开发、图形学或者沙盒建造类游戏&#xff08;比如《我的世界》&#xff09;底层技术感兴趣的C开发者&#xff0c;那么“在Urho3D上实现一个体素引擎”这个想法&#xff0c;大概率已经在你…

作者头像 李华
网站建设 2026/7/14 5:12:31

线性回归实战:用价格弹性与销售建模驱动业务决策

1. 项目概述&#xff1a;线性回归不是数学课&#xff0c;是你的第一台“数据印钞机”“From Data to Dollars: Using Linear Regression”——这个标题里藏着一个被严重低估的真相&#xff1a;线性回归从来就不是统计学课本里的一个公式&#xff0c;而是商业世界里最基础、最可…

作者头像 李华
网站建设 2026/7/14 5:11:07

深度学习模型性能优化:突破准确率瓶颈的完整实战指南

在深度学习模型开发过程中&#xff0c;我们经常会遇到模型性能提升缓慢甚至停滞的情况。最近在优化一个图像分类项目时&#xff0c;就遇到了准确率卡在某个瓶颈难以突破的问题。经过一系列调优实验&#xff0c;终于实现了性能的持续提升&#xff0c;这里将完整的优化思路和实操…

作者头像 李华
网站建设 2026/7/14 5:10:19

VC++使用WinINet API实现FTP文件上传:从原理到实战

1. 项目概述&#xff1a;为什么选择VC实现FTP文件上传&#xff1f;在当前的开发环境中&#xff0c;HTTP API和云存储服务大行其道&#xff0c;但FTP&#xff08;文件传输协议&#xff09;作为一种经典、稳定且被广泛支持的协议&#xff0c;依然在大量企业内部系统、工业控制、设…

作者头像 李华