HAP / HAR / HSP 到底啥区别?顺带把「导入」那点疑惑讲清楚

HAP / HAR / HSP 到底啥区别?顺带把「导入」那点疑惑讲清楚

我把 demo 拆成了三个模块:entrycommonlibrary。建模块时 DevEco 让我在 HAP / HAR / HSP 里选,当时就一脸懵------这仨名字跟绕口令似的。拆完跑起来疑问更多了:导入的包怎么「用完就没了」?动态共享包说「不占大小」,那它到底加不加载?ESObject 又是个啥......

这篇用一座图书馆的比方,把三者一次讲明白,再把我那几个疑问逐个解决。

一、先一句话定位三兄弟

把「开发一个鸿蒙应用」想象成「经营一座图书馆」:

  • HAP = 能上架借阅的书。 真正能被安装、能跑起来的「成品」就是它。你的 App 本体就是一个(或几个)HAP。
  • HAR = 复印的资料。 要用就复印一份、夹进你的书里。复印完,它就成了书的一部分。
  • HSP = 一本公共参考书。 整个图书馆就一本,摆在中央,谁要查谁去翻,大家共用。

记住这三个画面,下面全是展开。

二、HAR(静态共享包)= 复印夹进书里

HAR 是编译期 就被「复印」进使用方的:编译时,它的代码和资源被打进依赖它的那个 HAP 里。

👉 你的疑问:「导入包是直接导入之后就没有」------对的。 HAR 在编译完成那一刻就「融」进了使用它的 HAP,运行时没有一个独立的 HAR 存在了,它已经是宿主的一部分。就像复印件夹进书里,书印好后你找不到那张「独立的复印件」了。

这带来两个代价:

  1. 重复占体积。 一份 100KB 的工具 HAR,如果被 5 个 HAP 都引用,就被复印 5 份 → 多占 ~500KB。
  2. 单例会各算各的。 每个副本有自己的一份静态变量,所以「全局单例」在多个副本里其实是好几个实例,不是同一个 。 这正是上一步我们踩过的坑:HSP 里的模块拿不到 entry 里那个 LoadLog 单例------因为 HAR 被不同模块各复印一份,单例就裂开了。

👉 你的疑问:「静态共享包只是把不同功能封进不同模块,可以独立开发不同技术栈吗?」 前半句对:HAR 就是用来封装、复用、解耦 的,不同团队/仓库各自维护各自的 HAR、按版本引用,能独立开发。但「不同技术栈」是个误会 ------鸿蒙这些包都是同一套 ArkTS / ArkUI ,不存在「这个模块用 React、那个用 Vue」那种技术栈隔离。模块化解决的是「谁负责哪块、怎么复用 」,不是「换语言换框架」。(HAR 里可以带 native 的 .so,但应用层逻辑还是 ArkTS。)

三、HSP(动态共享包)= 一本公共参考书

HSP 不复印。整个应用里它只有一份 ,运行时加载,多个 HAP 共享同一个实例

👉 你的疑问:「动态共享包不占大小,是指不加载吗?」不是! 「不占大小」说的是:它不会被重复复印 进每个 HAP(去掉重复 = 省体积),不是「零体积」,更不是「不加载」

公共参考书的画面正好:全馆就一本 (所以不重复占地方),但你要用它,还是得走过去翻开 (要加载)。HSP 跟着应用一起安装,运行时按需加载一次,之后大家共用这一份。

附带好处:因为是唯一实例,单例能在各 HAP 间共享 ;而且 HSP 能放 UI / 页面(HAR 也能放 UI,但「又要共享 UI、又想省体积」时 HSP 更合适)。

四、HAP = 那本能真正上架的书

HAP 是唯一能被安装、能运行 的包。一个应用至少有一个 entry HAP,复杂应用可以再拆出若干 feature HAP。HAR 和 HSP 都不能单独安装/运行,它们是给 HAP 用的「料」。

五、并排对比

HAP HAR(静态共享) HSP(动态共享)
比喻 能借阅的书 复印夹进书里 公共参考书
能否独立安装/运行 ✅ 能 ❌ 不能 ❌ 不能
何时「进」使用方 ------ 编译期打进去 运行时加载
被多处用会重复占体积吗 ------ (各复印一份) 不会(全应用一份)
单例 ------ 各副本各一个 全应用共享一个
能放 UI/页面
你的 demo entry common(网络底层) library(动态加载的功能)

六、顺带把「导入」和 ESObject 说清楚

👉 你的疑问:「ESObject 是模块的数据类型???」不是。

ESObject 是 ArkTS 给「编译期说不清类型的值 」准备的一个类型逃生舱 。动态 import('library') 拿回来的模块,编译器静态不知道它长什么样 (它是运行时才加载的),所以只能先用 ESObject 接住,再动态地访问它的方法。

不是「模块专属类型」 ------凡是动态来的、互操作来的、类型不确定的值,都可能是 ESObject。一句话:ESObject ≈「我暂时不知道你具体是啥,但先让我能用」。 代价是这块没有静态类型检查,所以能不用就不用。

👉 你的疑问:「为什么导入动态共享包,加载一次就不用再加载了?」

因为模块缓存 :一个模块被求值一次 后就进缓存,之后无论再 import 多少次,拿到的都是同一个缓存实例 ------这就是你看到「加载时刻 HH:mm:ss 一直不变」的原因。HSP 更进一步,全应用只有一份实例。所以「加载一次就够」是 ES 模块(和 HSP 单实例)的本性,不是哪里特别配置出来的。

demo 里我们还顺手加了一层「显式缓存」:首次 import('library') 把结果存进字段,之后直接复用、连 import() 都不再调------这是更地道的写法。但底层就算你每次都调 import(),模块也不会重新加载,道理就在这。

七、那到底怎么选?

  • 只有一个模块会用HAR(简单,编译进去就完事)。
  • 多个 HAP/模块共用,或想去重省体积,或要共享单例/UIHSP
  • 一个经验:能不拆就别乱拆。拆模块要付依赖管理、版本对齐、编译变慢的成本,别为了「看起来专业」而过度模块化。

对上你的 demo:网络底层只被 entry 用、且要静态直连 → 放 common(HAR) 正合适;library 想演示「按需动态加载的独立分包」→ 用 HSP 正好。

一句话总结

HAP 是能跑的成品书;HAR 是复印件------编译期夹进谁就成了谁的一部分,被多处用会重复占体积、单例还各算各的;HSP 是全馆唯一的公共参考书------运行时加载一次、大家共享,「不占大小」是说不重复复印、不是不加载。 至于 ESObject,它不是模块类型,而是「编译期说不清类型」的逃生舱;「加载一次就够」则是模块缓存的本性。


参考(建议对着官方再过一遍)

相关推荐
Full Stack Developme16 分钟前
跨站请求伪造 (CSRF) 是什么 设计及工作原理
前端·okhttp·csrf
MoSTChillax24 分钟前
UI规范设计:从视觉到交互的全面指南
前端·ui·产品经理·设计规范·vibecoding
雪碧聊技术1 小时前
Vue + Element Plus 实现文本溢出显示省略号及悬浮提示
前端·javascript·css·vue.js·文本溢出省略
eric-sjq1 小时前
0.6B 前端生成模型本地部署实战:消费级显卡跑通 WanlyFrontend 全流程
前端·本地部署
小妖6662 小时前
设置了 box-sizing: border-box; 不管用,下边框还是被挤压没了
前端·css·html
雪隐2 小时前
个人电脑玩AI-16让5060 Ti给你打工——5060Ti 16G 跑 MiniMax-Music-3:从下载到 60s 出歌的全流程
前端·人工智能·后端
CAD老兵3 小时前
让 AI Coding Agent 直接访问 CAD 文档:GitMCP 实战指南
前端·人工智能·github
程序员鱼皮4 小时前
DeepSeek Harness 最新邪修曝光!被吹成核弹的极简模式,真的夯吗?
前端·后端·ai编程
无己心4 小时前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
袅沫4 小时前
Nginx中部署多个前端项目——区分路径
服务器·前端