一、写在前面
开源地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_hoppscotch
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
这篇文章记录的是 Hoppscotch 在 OpenHarmony / HarmonyOS Electron 运行环境中的一次适配实践。
Hoppscotch 本身是一个开源 API 开发生态工具,核心能力包括 REST、GraphQL、WebSocket、集合、环境变量、历史记录等。它的核心体验主要运行在 Web 层,项目本身也提供了 selfhost web 产物。因此这次适配选择了一条更稳妥的路线:
将 Hoppscotch 的 selfhost web 产物放进鸿蒙 Electron 壳,形成完整的本地 HAP 应用体验。
这种方式的优势是明显的:Hoppscotch 的核心 API 调试体验本来就在 Web 层完成,鸿蒙侧需要把静态资源加载、运行时环境变量注入、外部链接处理和 HAP 构建流程完整打通。
本文会按迁移过程来拆解:
- 原始项目是什么结构
- 为什么选择 Web 静态产物承载路线
- 鸿蒙 HAP 工程如何接入
- 构建脚本做了哪些关键处理
- 适配过程中最大的困难是什么,如何解决
- 已经完成哪些能力
这里直接给出结论:这次迁移已经给 Hoppscotch 的核心 API 调试体验提供了稳定的鸿蒙运行容器。真正复杂的地方集中在 Web kernel、运行时配置、请求链路和数据模型上,鸿蒙侧适配完成,这些核心能力已经可以在 HAP 中完整承载。
所以整个过程里我基本遵循了一个原则:保持 Hoppscotch 原有业务逻辑稳定,把工程入口、资源路径和运行时配置处理干净。这样 REST、GraphQL、WebSocket、集合、环境变量、历史记录等能力都可以沿着原有 Web kernel 在鸿蒙环境中运行。
这个过程看起来有点"绕",但实际适配时非常省时间。鸿蒙 Electron 应用的运行链路横跨 HAP、Electron 主进程、静态资源、Vite 产物和业务初始化,把入口收窄之后,整个工程更容易稳定下来。

二、项目背景
Hoppscotch 是一个比较典型的现代前端 monorepo 项目,仓库里包含多个 package:
packages/hoppscotch-selfhost-webpackages/hoppscotch-commonpackages/hoppscotch-kernelpackages/hoppscotch-desktoppackages/hoppscotch-backendpackages/hoppscotch-cli
项目根目录使用 pnpm 管理工作区,构建链路基于 Vite 和现代前端工具链。
Hoppscotch 的核心功能本身就围绕 Web 应用组织。也就是说,它可以先通过 selfhost-web 产物跑起来,再由鸿蒙 Electron runtime 提供一个本地窗口容器。
这次适配的关键判断就是:
以 Hoppscotch 的 Web kernel 为核心,把 API 调试体验完整承载到鸿蒙窗口中。
这样可以覆盖最核心的 API 调试链路,同时让 HAP 工程、资源同步、运行时配置和窗口行为都形成完整闭环。
三、总体变化概览
本次适配主要新增了三类内容。
3.1 鸿蒙 HAP 工程
新增目录:
text
ohos_hap/
├── AppScope/
├── electron/
├── web_engine/
└── build-profile.json5
其中 web_engine/src/main/resources/resfile/resources/app 是鸿蒙侧 Electron runtime 最终读取应用资源的位置。
适配后的资源结构大致是:
text
resources/app/
├── main.js
├── package.json
└── dist/
├── index.html
└── assets/
3.2 OpenHarmony 构建脚本
项目根目录新增了两个关键脚本:
text
scripts/build-ohos-package.cjs
scripts/build-ohos-hap.cjs
同时在 package.json 中增加:
json
{
"build:ohos": "node scripts/build-ohos-package.cjs",
"ohos:sync": "OHOS_HOPPSCOTCH_OUT=ohos_hap/web_engine/src/main/resources/resfile/resources/app pnpm run build:ohos",
"ohos:build": "node scripts/build-ohos-hap.cjs"
}
这三条命令分别负责:
- 构建或复用 Hoppscotch selfhost web 产物
- 同步资源到鸿蒙 HAP 工程
- 调用
ohpm install和hvigor assembleHap生成 HAP
3.3 Electron 启动引导
适配脚本会在资源目录下写入一个轻量 main.js,职责很明确:
- 注入
HOPPSCOTCH_OPENHARMONY=1 - 默认启用 GPU 兼容配置
- 创建
BrowserWindow - 使用
win.loadFile(path.join(__dirname, 'dist/index.html'))加载本地页面 - 对外部链接使用
shell.openExternal
这个入口负责承载 Hoppscotch Web 产物,让窗口创建、本地页面加载和外部链接处理都集中在鸿蒙 Electron runtime 中。

四、原始项目直接跑鸿蒙会遇到什么问题
Hoppscotch 原始仓库并不是为鸿蒙 Electron HAP 准备的,它默认面对的是 Web 部署或官方桌面端环境。
如果直接把 Web 构建产物塞进 file:// 环境,常见问题会有三类。
4.1 根路径资源不兼容
Web 项目在普通服务器部署时,资源路径经常是这样的:
html
<script src="/assets/index.xxx.js"></script>
<link href="/assets/index.xxx.css">
但在鸿蒙 Electron 的本地 file:// 加载环境中,根路径 /assets/... 并不等价于 dist/assets/...。结果就是:
index.html能打开- JS / CSS 加载失败
- 页面空白或样式丢失
4.2 Web Manifest 中的根路径
Hoppscotch 的 Web 产物里可能包含 manifest.webmanifest。如果里面的 start_url、id、src 等字段仍然使用 / 开头,也会对本地加载造成干扰。
4.3 运行时环境变量
Hoppscotch selfhost web 依赖一组 VITE_ 运行时配置,例如:
VITE_BASE_URLVITE_BACKEND_GQL_URLVITE_BACKEND_WS_URLVITE_BACKEND_API_URLVITE_APP_TOS_LINKVITE_APP_PRIVACY_POLICY_LINK
在普通 Web 部署中,这些变量可以由部署环境或构建过程注入。放到鸿蒙本地静态文件之后,就必须在构建脚本里显式处理。
五、鸿蒙适配核心路径
5.1 第一步:选择 selfhost web 作为承载目标
本次选择:
text
packages/hoppscotch-selfhost-web
原因是:
- Web 端功能覆盖 API 调试主路径
- 静态产物更容易进入鸿蒙 Electron runtime
- 资源路径和 HAP 承载边界清晰
- UI、路由、请求、集合、环境等核心体验可以完整进入鸿蒙窗口
这一步的本质是降低迁移入口复杂度。
5.2 第二步:构建脚本检查资源路径
scripts/build-ohos-package.cjs 里有一个重要判断:
js
const hasRootRelativeAssets = file => {
const html = fs.readFileSync(file, 'utf8');
return /(?:src|href|content)="\/(?:assets|favicon\.ico|icon\.png|manifest\.webmanifest)/.test(html);
};
如果发现 dist/index.html 里还有根路径资源,脚本会强制重新构建。
这一步解决的是:
避免把一个只能在 Web server 下工作的产物,误同步进鸿蒙 HAP。
5.3 第三步:处理 Manifest 兼容
脚本还会检查 manifest.webmanifest 中是否存在不适合鸿蒙本地加载的根路径:
js
const hasOhosIncompatibleManifest = () => {
return /"(?:(?:start_url|id|url|src)":"\/)/.test(content);
};
如果存在这类内容,也会触发重新构建。
5.4 第四步:注入运行时环境变量
脚本内置了一组默认运行时配置:
js
const defaultRuntimeEnv = {
VITE_BASE_URL: 'https://hoppscotch.io',
VITE_SHORTCODE_BASE_URL: 'https://hopp.sh',
VITE_BACKEND_GQL_URL: 'https://api.hoppscotch.io/graphql',
VITE_BACKEND_WS_URL: 'wss://api.hoppscotch.io/graphql',
VITE_BACKEND_API_URL: 'https://api.hoppscotch.io/v1',
VITE_PROXYSCOTCH_ACCESS_TOKEN: ''
};
同步阶段会把 import_meta_env_placeholder 替换成真实的 globalThis.import_meta_env。
这一步非常关键,因为 Hoppscotch 不是一个纯静态展示页,它启动后需要知道后端 GraphQL、WebSocket、API URL 等运行参数。
5.5 第五步:写入鸿蒙 Electron 启动文件
脚本生成的 main.js 只做必要工作:
- 创建窗口
- 设置尺寸
- 使用兼容的沙箱配置
- 加载本地
dist/index.html - 拦截新窗口打开行为
窗口配置大致如下:
js
const win = new BrowserWindow({
width: 1280,
height: 800,
minWidth: 960,
minHeight: 640,
title: 'Hoppscotch',
backgroundColor: '#0f172a',
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
sandbox: false
}
});
这里保持 nodeIntegration: false 是一个合适的选择:Hoppscotch Web 端不需要直接访问 Node,可以让 renderer 层保持清晰的 Web 运行模型。
5.6 第六步:把"可复用产物"和"鸿蒙运行目录"分开
适配脚本没有直接把 Hoppscotch 的源码目录当成 HAP 运行目录,而是把它拆成两层:
text
packages/hoppscotch-selfhost-web/dist
ohos_hap/web_engine/src/main/resources/resfile/resources/app
前者是 Hoppscotch 自己的 Web 构建结果,后者是鸿蒙 Electron runtime 真正读取的应用资源。这个拆分看起来只是多了一次复制,但好处很明显:
- 可以单独判断 Web 产物是否健康
- 可以在同步阶段做路径、manifest、环境变量的二次处理
- HAP 工程里只放最终运行需要的内容
- 更换构建参数时不会污染原始源码结构
我更倾向于把 resources/app 看成最终运行包的资源入口。凡是进入这个目录的东西,都应该是鸿蒙 Electron 能直接消费的资源。
5.7 第七步:环境变量不要散落在各处
Hoppscotch 的配置项不少,如果每遇到一个缺失变量就在代码里临时补一个默认值,文章和项目都会很快变乱。现在的做法是把默认配置集中在 defaultRuntimeEnv,再允许外部真实的 VITE_ 环境变量覆盖。
这有两个实际价值。
第一,普通开发者直接执行 pnpm run ohos:sync 时,至少能拿到一个可启动的默认包,不需要先理解 Hoppscotch 后端所有部署细节。
第二,如果接入私有化 Hoppscotch 后端,只需要在构建环境里传入对应的 VITE_BACKEND_GQL_URL、VITE_BACKEND_WS_URL、VITE_BACKEND_API_URL,不用再改脚本源码。
这也是我觉得比较舒服的一点:脚本里有默认值,但没有把部署场景写死。

六、问题困难解决
这次 Hoppscotch 适配中最大的困难集中在 Web 产物的本地运行环境上:
如何让一个原本面向 Web server 的现代 Vite 产物,在鸿蒙 Electron 的
file://本地环境里稳定启动。
这个问题表面上看只是"路径不对",但实际会连带出三层问题。
6.1 困难一:根路径资源兼容
如果产物里仍然保留 /assets/...,鸿蒙本地加载时就会找不到资源。
典型表现是:
- 应用窗口能打开
- 标题栏正常
- 页面区域无法加载完整内容
- 日志里可能只有资源加载失败,或者根本不明显
解决方式是让构建脚本主动扫描 index.html,发现根路径资源就重新构建,并在同步后再次检查。如果同步后的 dist/index.html 仍然不符合要求,脚本直接抛错,不允许继续打包。
这比打完包再到设备侧反复定位资源问题可靠得多。
6.2 困难二:运行时环境变量不能丢
Hoppscotch 启动后需要后端地址、WebSocket 地址、隐私协议链接等配置。普通 Web 部署可以靠环境变量注入,但本地 HAP 资源不能假设部署平台会帮我们处理。
解决方式是在 build-ohos-package.cjs 中集中维护 defaultRuntimeEnv,并允许外部 VITE_ 环境变量覆盖。最终把配置写入:
js
globalThis.import_meta_env = {...}
这样 HAP 内的静态页面启动时就能拿到完整运行时配置。
6.3 困难三:桌面端原生能力需要统一收口
Hoppscotch 的桌面端能力涉及更多原生运行假设。鸿蒙适配中把这些能力统一收口到 Electron runtime 的窗口承载、外部链接处理和 HAP 资源管理里。
处理方式是:
- Web kernel 负责 API 调试主体验
- HAP 工程负责应用安装和资源承载
- Electron runtime 负责窗口、链接和本地页面加载
- 运行时变量负责后端、WebSocket、API URL 等配置注入
这次适配中比较关键的经验是:
Hoppscotch 的鸿蒙 Electron 适配,核心是让 Web 内核、运行时变量、HAP 资源和 Electron 窗口共同形成完整闭环。
6.4 困难四:构建脚本必须能反复执行
迁移类项目最怕脚本只能在"某一次刚好成功"的机器上跑通。Hoppscotch 这种 monorepo 项目本来就依赖 pnpm workspace,构建 selfhost web 时又会牵涉多个 package。如果同步脚本每次都靠手动清目录、手动复制文件,很快就会出现"我这边能跑,你那边不行"的情况。
所以脚本里没有做增量式的小修小补,而是采用比较直接的策略:
- 判断是否需要重新构建 Web 产物
- 清理输出目录
- 重新复制
dist - 注入运行时变量
- 写入
main.js和 runtimepackage.json
这套流程牺牲了一点执行时间,但换来的是结果更可预期。对鸿蒙适配来说,这个取舍通常值得,因为设备侧定位成本比本地多等几秒构建高得多。
6.5 运行链路确认顺序
适配完成后,Hoppscotch 在鸿蒙中的运行链路可以按下面顺序确认:
- 先看
resources/app/dist/index.html是否存在 - 再看里面是否还有
/assets/...这样的根路径资源 - 检查
manifest.webmanifest是否还引用根路径 - 确认
import_meta_env_placeholder是否已经被替换 - 看 Electron 是否真正加载了
dist/index.html - 最后再进入 Hoppscotch 自己的业务初始化和网络请求
这个顺序可以确认资源、manifest、运行时变量、Electron 入口和业务初始化都已经进入正确位置。
七、构建与运行方式
同步资源:
bash
cd /hoppscotch-ohos
pnpm run ohos:sync
构建 HAP:
bash
cd /hoppscotch-ohos
pnpm run ohos:build
构建脚本会依次完成:
- 检查或构建
packages/hoppscotch-selfhost-web/dist - 同步到
ohos_hap/web_engine/src/main/resources/resfile/resources/app - 执行
ohpm install - 调用
hvigor assembleHap
HAP 输出位置:
text
ohos_hap/electron/build/default/outputs/default/

八、适配成果
hoppscotch-ohos 已经完成:
- OpenHarmony Electron HAP 工程
- selfhost web 静态产物同步能力
- 本地
file://加载入口 - 运行时环境变量注入
- 外部链接拦截
- HAP 命令行构建能力
- REST 请求能力承载
- GraphQL / WebSocket 运行配置
- 集合、环境、历史记录等核心 API 调试体验承载
适配完成的是:
Hoppscotch Web 核心体验完整进入鸿蒙 Electron runtime。
从应用体验看,REST、GraphQL、WebSocket、collection、environment、history 等核心链路已经完成鸿蒙化承载。
九、功能验证结果
Hoppscotch 鸿蒙版按实际 API 调试路径完成了功能验证。核心体验不只停留在页面加载,而是覆盖请求发送、响应展示、状态保存和实时连接。
第一组是基础请求:
- REST
GET、POST、带 header、带 body - 请求失败时的错误提示
- 大响应体和 JSON 格式化
- 请求历史记录正常保存
第二组是实时链路:
- GraphQL 查询与变量
- GraphQL subscription / WebSocket 连接
- WebSocket 普通连接、发送、接收、断开重连
第三组是工程化能力:
- collection 新建、编辑、删除
- environment 变量读取和切换
- 导入导出集合
- 登录态与后端账号能力
第四组是鸿蒙环境相关能力:
- 外部链接拦截到系统浏览器
- 网络权限和证书异常提示
- 横竖屏或窗口尺寸变化时的布局
- HAP 重新安装后的本地数据表现
这些能力共同组成了 Hoppscotch 在鸿蒙环境中的完整 API 调试体验。
十、结语
hoppscotch-ohos 的适配重点,是把 Hoppscotch 的 Web 核心体验稳定放进鸿蒙 Electron runtime:
把一个 Web-first 的复杂开发工具,放进鸿蒙 Electron runtime 中稳定承载。
这条路线的好处是边界清楚:Web 内核负责核心业务,HAP 工程负责资源承载,Electron runtime 负责本地窗口。工程链路、资源路径和运行时变量处理稳定之后,API 请求、集合、环境、WebSocket、导入导出等能力就能在鸿蒙环境中形成完整闭环。
