鸿蒙PC使用 Electron 迁移:Beekeeper Studio 适配全记录
一、写在前面
欢迎加入鸿蒙PC开发者社区,共同打造开发者工具生态:鸿蒙PC开发者社区:https://harmonypc.csdn.net/
项目开源地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_beekeeper-studio
欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper
环境搭建文章:https://blog.csdn.net/lbcyllqj/article/details/161286249?sharetype=blogdetail&sharerId=161286249&sharerefer=PC&sharesource=lbcyllqj&spm=1011.2480.3001.8118
这篇文章记录的是一次 Beekeeper Studio 迁移到 OpenHarmony / HarmonyOS PC 的适配实践。
Beekeeper Studio 是一个跨平台 SQL 编辑器和数据库管理工具,桌面端基于 Electron、Vue 2、TypeScript 构建。它不是一个简单的静态页面应用,而是包含数据库连接、连接配置、SQL 编辑器、数据表浏览、插件、主题、通知、内部 SQLite 数据库、主进程和渲染进程通信等一整套桌面软件能力。
这类项目迁移到鸿蒙 PC 时,最容易出现一种误判:应用能启动、页面也不是白屏,于是第一反应会认为“前端资源应该没问题”。但这次遇到的核心问题恰好不是 JS 没加载,也不是 Vue 没挂载,而是页面已经渲染出来了,UI 却像“样式丢失”一样退回到了浏览器默认样式。
最终排查下来,根因在于 OpenHarmony 运行路径没有触发桌面 Electron 原有的设置初始化逻辑,导致 body 上没有写入 theme-light / theme-dark 等主题 class。而 Beekeeper Studio 的大量 SCSS 都依赖 body.theme-light、body.theme-dark 这类选择器生效,所以看起来就像整个 UI 丢失了一样。
这篇文章会从项目结构、鸿蒙 HAP 构建、真机安装、问题现象、定位过程、代码修复和最终验证几个阶段展开。重点不是简单记录命令,而是复盘一次 Electron 项目在鸿蒙 PC 上“能运行但样式异常”的真实排障过程。

二、项目背景
Beekeeper Studio 是一个比较典型的现代 Electron 桌面应用。项目整体是 monorepo 结构,主要目录如下:
beekeeper-studio-master/
├── apps/
│ ├── studio/
│ │ ├── src/
│ │ ├── src-commercial/
│ │ ├── vite.config.mjs
│ │ ├── esbuild.mjs
│ │ └── electron-builder-config.js
│ ├── ui-kit/
│ └── sqltools/
├── ohos_hap/
├── package.json
└── yarn.lock
其中 apps/studio 是主应用,包含 Electron 主进程、preload、Vue 渲染进程以及业务逻辑。和这次鸿蒙适配关系最密切的入口主要有:
apps/studio/src-commercial/entrypoints/main.ts
apps/studio/src-commercial/entrypoints/renderer.ts
apps/studio/src-commercial/entrypoints/preload.ts
apps/studio/src/App.vue
apps/studio/src/assets/styles/app.scss
ohos_hap/
桌面端构建时,主进程由 ESBuild 处理,渲染进程由 Vite 处理。迁移到鸿蒙 PC 后,还需要把构建好的 Electron 应用资源同步到 ohos_hap 工程里,再通过 hvigor 生成可安装的 HAP。
本次适配目标不是只让窗口出现,而是要让 Beekeeper Studio 在鸿蒙 PC 上至少具备下面这些基础能力:
- HAP 可以正常构建、签名和安装
- 应用可以在真实设备上启动
- 渲染进程可以加载 Vue 页面
- 连接首页可以显示 Demo Database 和 New Connection 面板
- Beekeeper Studio 的主题样式能够正常生效
- 修复后可以通过真机截图确认效果

三、构建和同步流程
这个项目使用 Yarn 管理依赖。在当前环境里直接执行 yarn 不一定在 PATH 中,因此使用 corepack yarn 更稳妥。
渲染进程构建命令如下:
corepack yarn workspace beekeeper-studio build:vite
主进程构建命令如下:
corepack yarn workspace beekeeper-studio build:esbuild
构建完成以后,需要把 Electron 产物同步到鸿蒙 HAP 工程:
OHOS_BEEKEEPER_SKIP_BUILD=1 corepack yarn ohos:sync
然后构建并签名 HAP:
OHOS_BEEKEEPER_SKIP_BUILD=1 OHOS_SIGN_HAP=1 corepack yarn ohos:build
最终生成的 HAP 位于:
ohos_hap/electron/build/default/outputs/default/electron-default-signed.hap
安装到真机:
hdc install -r ohos_hap/electron/build/default/outputs/default/electron-default-signed.hap
启动应用:
hdc shell aa force-stop io.beekeeperstudio.ohos
hdc shell aa start -a EntryAbility -b io.beekeeperstudio.ohos -m electron
真机截图可以使用:
hdc shell snapshot_display -f /data/local/tmp/beekeeper_screen.jpeg
hdc file recv /data/local/tmp/beekeeper_screen.jpeg ./beekeeper_screen.jpeg

四、第一次真机运行:不是白屏,而是 UI 像丢了
应用安装到鸿蒙 PC 真机以后,第一眼看到的画面比较迷惑。
它不是典型的白屏,也不是黑屏。页面上能看到:
- Beekeeper Studio 标题
- Demo Database
- New Connection
- Connection Type 下拉框
- Welcome to Beekeeper Studio 提示
这说明几个关键点已经成立:
- HAP 本身可以安装
- Electron 容器可以启动
- 渲染进程 JS 已经执行
- Vue 应用已经挂载
- 基础数据也能显示出来
但问题是,整个页面看起来像没有经过 Beekeeper Studio 的 UI 系统处理:链接是默认的紫色,下拉框和按钮像浏览器原生样式,布局也缺少正常主题下的视觉层级。
这时候如果只从“资源加载失败”方向看,很容易走偏。因为如果 CSS 文件真的完全没加载,页面表现会更乱;而这里的现象更像是 CSS 载入了,但核心选择器没有命中。
五、先确认不是崩溃,也不是 Vue 没启动
排查这类问题时,第一步要先排除“程序已经崩了”的情况。
通过进程和应用状态可以确认应用仍然在运行:
hdc shell ps -ef | rg 'io.beekeeperstudio|beekeeper|electron'
hdc shell aa dump -a io.beekeeperstudio.ohos
同时,从真机截图看,页面上不仅有静态标题,还有 Demo Database、连接表单、欢迎提示等内容。也就是说,Vue 组件树已经渲染出来了。
这个判断很重要。因为它把问题范围从“启动链路断了”缩小到了“运行时状态或样式作用条件不对”。
接下来重点看 Beekeeper Studio 的样式体系。
在 apps/studio/src-commercial/entrypoints/renderer.ts 中,渲染入口确实引入了全局样式:
import '@/assets/styles/app.scss'
所以问题不太像是 app.scss 完全没有进入打包产物。继续查看 SCSS 后可以发现,很多样式都挂在主题选择器下面,例如:
body.theme-light {
...
}
body.theme-dark {
...
}
body.theme-system {
...
}
这就给了一个关键方向:如果 body 上没有 theme-light 或 theme-dark,那么大量主题样式就不会命中。

六、问题根因:OpenHarmony 路径没有初始化 settings
继续看 App.vue,可以看到主题 class 是根据 Vuex 里的 settings/themeValue 写到 document.body.className 上的。
原来的逻辑大致是:
if (this.themeValue) {
document.body.className = `theme-${this.themeValue}`
}
也就是说,只有 themeValue 有值时,body 才会得到 theme-light 或 theme-dark。
那么 themeValue 从哪里来?继续看渲染进程入口 renderer.ts。
桌面 Electron 路径下,settings 初始化发生在渲染进程收到主进程传来的 MessagePort 之后:
window.onmessage = (event) => {
if (event.source === window && event.data.type === 'port') {
const [port] = event.ports;
const { sId } = event.data;
Vue.prototype.$util.setPort(port, sId);
app.$store.dispatch('settings/initializeSettings');
}
}
这套逻辑在桌面 Electron 中是成立的。问题在于,OpenHarmony 适配路径使用的是 fallback utility 连接方式,并不走桌面端这个 MessagePort 分支。
结果就是:
- 渲染进程能启动
- Vue 能挂载
- 页面能显示
- 但
settings/initializeSettings没有在 OpenHarmony 路径下执行 settings/themeValue没有及时写入body上没有theme-light- SCSS 中依赖主题 class 的样式全部不命中
这就是“UI 看起来丢失了”的真正原因。
它不是资源包没打进去,也不是 Vue 页面没有渲染,而是一个运行时初始化分支在鸿蒙路径下缺了一步。

七、第一处修复:OpenHarmony 下主动初始化 settings
修复思路很直接:在 OpenHarmony 路径下,不等待桌面端 MessagePort,而是在创建 Vue 实例并设置 utility 后主动初始化 settings。
修改文件:
apps/studio/src-commercial/entrypoints/renderer.ts
修复后的关键代码如下:
const app = new Vue({
render: h => h(App),
store,
})
Vue.prototype.$util = utility;
if (window.platformInfo.isOpenHarmony) {
await app.$store.dispatch('settings/initializeSettings');
}
window.main.attachPortListener();
window.onmessage = (event) => {
if (event.source === window && event.data.type === 'port') {
const [port] = event.ports;
const { sId } = event.data;
log.log('Received port in renderer with sId: ', sId);
Vue.prototype.$util.setPort(port, sId);
app.$store.dispatch('settings/initializeSettings');
}
}
这段修改的含义是:
- 桌面 Electron 仍然保留原来的
MessagePort初始化路径 - OpenHarmony 下先使用 fallback utility 初始化 settings
- 尽早让
settings/themeValue有值 - 让后续
App.vue可以正确写入body.theme-light
这类修复要尽量小心,不要为了鸿蒙路径破坏桌面路径。这里通过 window.platformInfo.isOpenHarmony 做平台判断,避免影响原本的桌面 Electron 行为。

八、第二处修复:给 App.vue 增加默认 light 主题兜底
只修 renderer.ts 还不够稳。因为 settings 初始化本质上依赖运行时能力,如果后续某次读取设置失败,页面仍然可能回到没有主题 class 的状态。
所以第二处修复是在 App.vue 中给主题写入增加默认兜底:当 themeValue 为空时,默认使用 light。
修改文件:
apps/studio/src/App.vue
主题监听器修改为:
themeValue() {
document.body.className = `theme-${this.themeValue || 'light'}`
this.trigger(AppEvent.changedTheme, this.themeValue)
}
挂载阶段也改成无条件写入默认主题:
document.body.className = `theme-${this.themeValue || 'light'}`
这一步的意义是给渲染层加一道保护。即使 settings 还没回来,body 上至少也会有 theme-light,不会让整套主题 SCSS 全部失效。
从迁移经验看,这种兜底非常必要。桌面端一些“初始化一定会成功”的隐含前提,到了鸿蒙环境里可能会因为通信方式、沙箱路径、数据库初始化顺序发生变化。UI 层如果没有默认态,就很容易出现这次这种“页面活着,但样式像没了”的问题。

九、重新构建、同步、签名和安装
修完以后重新走完整构建流程。
先构建渲染进程:
corepack yarn workspace beekeeper-studio build:vite
再构建主进程:
corepack yarn workspace beekeeper-studio build:esbuild
同步到鸿蒙 HAP 工程:
OHOS_BEEKEEPER_SKIP_BUILD=1 corepack yarn ohos:sync
构建签名 HAP:
OHOS_BEEKEEPER_SKIP_BUILD=1 OHOS_SIGN_HAP=1 corepack yarn ohos:build
安装:
hdc install -r ohos_hap/electron/build/default/outputs/default/electron-default-signed.hap
本次构建过程中,Vite 阶段会出现 Sass deprecation warning 和 chunk size warning,ESBuild 阶段也会有个别 external 相关 warning;鸿蒙构建阶段也会有 ArkTS warning。只要最终 HAP 成功产出并安装成功,这些 warning 不影响本次 UI 修复验证。
安装成功后可以看到:
install bundle successfully.
AppMod finish
十、真机验证:主题样式恢复
安装新的 HAP 后,强停并重新启动应用:
hdc shell aa force-stop io.beekeeperstudio.ohos
hdc shell aa start -a EntryAbility -b io.beekeeperstudio.ohos -m electron
等待几秒后截图:
hdc shell snapshot_display -f /data/local/tmp/beekeeper_screen_after.jpeg
hdc file recv /data/local/tmp/beekeeper_screen_after.jpeg ./beekeeper_screen_after.jpeg
再次查看真机画面,可以看到 Beekeeper Studio 的主题 UI 已经恢复:
- 左侧 sidebar 样式正常
New Connection按钮样式正常- Filter 输入框样式正常
- Connection Type 下拉框样式正常
- 右下角 Welcome toast 样式正常
- 页面不再显示默认紫色链接和原生控件外观
这说明 body.theme-light 已经写入,依赖主题 class 的 SCSS 重新命中。

十一、这次问题容易误判的地方
这次问题比较有代表性,因为它看起来像“CSS 丢了”,但实际不是简单的静态资源加载问题。
如果 CSS 完全没有打进包里,通常会看到更彻底的裸页面,甚至布局也会明显散掉。但这次页面内容、组件层级、数据和通知都在,说明前端主逻辑并没有断。
真正的线索在于样式并不是全部缺失,而是主题相关样式没有命中。Beekeeper Studio 的全局样式体系依赖 body.theme-light、body.theme-dark 这类 class。如果这个 class 缺失,很多组件就会退回到基础样式甚至浏览器默认样式。
所以定位时不能只问“CSS 有没有加载”,还要继续问:
- CSS 是否加载了
- 选择器是否命中了
- 主题 class 是否存在
- 主题值是否从 settings 初始化出来
- settings 初始化是否在当前平台路径执行
最终根因正是在第 5 步:桌面 Electron 的 MessagePort 初始化路径没有覆盖 OpenHarmony fallback utility 路径。
十二、迁移经验总结
这次 Beekeeper Studio 的鸿蒙 PC 适配,可以总结出几个经验。
第一,Electron 项目迁移到鸿蒙 PC 时,不能只看页面是不是白屏。页面能显示不代表运行时状态完整,尤其是主题、配置、插件、用户设置这类依赖主进程通信或本地数据库的状态。
第二,桌面端成立的初始化顺序,到了 OpenHarmony 路径下需要重新确认。Beekeeper Studio 桌面端通过 MessagePort 收到 utility port 后初始化 settings,但鸿蒙 fallback utility 不走这个分支,因此需要补一条平台专属初始化路径。
第三,UI 主题必须有默认兜底。像 document.body.className = theme-${themeValue} 这种逻辑,如果 themeValue 为空就不写 class,在桌面端可能长期没问题,但在迁移环境里会放大成整个 UI 异常。
第四,真机截图非常重要。很多 UI 问题只看日志很难判断,但前后截图一对比,基本就能确定是“页面没渲染”“CSS 没加载”还是“主题 class 没命中”。
第五,修复要尽量保持平台隔离。这里没有改掉桌面 Electron 原来的 MessagePort 逻辑,而是在 window.platformInfo.isOpenHarmony 下补充 settings 初始化,这样对原平台影响最小。
十三、最终结果
最终修复后,Beekeeper Studio 已经可以在鸿蒙 PC 真机上正常启动,并显示完整的浅色主题 UI。
本次关键修改有两处:
apps/studio/src-commercial/entrypoints/renderer.ts
apps/studio/src/App.vue
核心修复点如下:
- OpenHarmony 渲染路径下主动执行
settings/initializeSettings App.vue中主题 class 写入增加light默认兜底- 重新构建 Vite 和 ESBuild 产物
- 同步到
ohos_hap - 构建签名 HAP 并安装到真机验证
这次适配的问题表面上是“UI 丢失”,本质上是 Electron 桌面运行时和 OpenHarmony 运行时初始化链路不同。把这条链路补齐以后,Beekeeper Studio 的主题系统就能恢复正常,应用也从“能启动但看起来不对”推进到了“真机上具备正常桌面 UI”的状态。
十四、常见问题(FAQ)
下面整理几个在这次适配过程中反复确认、也最容易被问到的问题。
Q1:页面能显示,但样式像丢了,怎么快速判断是不是这次同款问题?
看两点。第一,页面内容、组件层级、数据、通知都在,只是控件退回到浏览器原生外观(紫色链接、原生下拉框),说明前端主逻辑没断,更像是样式选择器没命中而不是资源没加载。第二,去看 body 上有没有 theme-light / theme-dark 这类 class。如果没有,而 SCSS 又大量挂在 body.theme-light 下,基本就能确认是主题 class 缺失导致的。
Q2:为什么不能直接判断成"CSS 没加载"?
如果 CSS 完全没打进包,页面会更彻底地裸掉,连布局都会明显散架。这次布局、数据、组件都在,只是主题相关样式不生效,所以方向应该是"选择器是否命中"“主题 class 是否存在”,而不是"文件有没有加载"。排查时按这条链路逐级往下问:CSS 是否加载 → 选择器是否命中 → 主题 class 是否存在 → 主题值是否初始化 → settings 是否在当前平台执行了初始化。
Q3:根因到底是什么?
桌面 Electron 路径下,settings/initializeSettings 是在渲染进程收到主进程传来的 MessagePort 之后才触发的。但 OpenHarmony 适配走的是 fallback utility 连接方式,不经过这个 MessagePort 分支,于是 settings 没初始化、themeValue 没值、body 没写入主题 class,依赖主题 class 的 SCSS 全部不命中。
Q4:为什么修了 renderer.ts 还要再改 App.vue?只改一处不行吗?
只改 renderer.ts 不够稳。settings 初始化本质依赖运行时能力,如果后续某次读取设置失败,页面仍可能回到没有主题 class 的状态。所以在 App.vue 里加一道默认兜底:themeValue 为空时默认用 light,保证 body 至少有 theme-light,整套主题 SCSS 不会全部失效。一处是修根因,一处是加保护,两者配合。
Q5:这套修复会不会影响桌面 Electron 的原有行为?
不会。renderer.ts 里保留了原来的 MessagePort 初始化路径,只通过 window.platformInfo.isOpenHarmony 判断,额外为鸿蒙路径补一条初始化。App.vue 的兜底逻辑对桌面端也是无害的(桌面端 themeValue 正常有值,|| 'light' 不会被触发)。修复尽量做到平台隔离。
Q6:为什么用 corepack yarn 而不是直接 yarn?
这个项目用 Yarn 管理依赖,但当前环境里 yarn 不一定在 PATH 中。corepack yarn 由 Node 自带的 corepack 调起项目指定版本的 Yarn,更稳妥,能避免本机没装或装了错误版本 Yarn 的问题。
Q7:构建过程中那些 warning 要不要处理?
本次可以先不管。Vite 阶段会有 Sass deprecation warning 和 chunk size warning,ESBuild 阶段有个别 external 相关 warning,鸿蒙构建阶段有 ArkTS warning。只要最终 HAP 成功产出并安装成功,这些 warning 不影响本次 UI 修复的验证。当然长期维护时仍建议逐步清理。
Q8:完整的构建到安装顺序是什么?
四步加安装:先 build:vite 构建渲染进程,再 build:esbuild 构建主进程,然后 OHOS_BEEKEEPER_SKIP_BUILD=1 ohos:sync 同步产物到 ohos_hap,再 OHOS_BEEKEEPER_SKIP_BUILD=1 OHOS_SIGN_HAP=1 ohos:build 构建并签名 HAP,最后用 hdc install -r 安装到真机。签名 HAP 产物在 ohos_hap/electron/build/default/outputs/default/electron-default-signed.hap。
Q9:为什么这次特别强调真机截图?
主题 class 这类问题只看日志很难判断到底是"页面没渲染"“CSS 没加载"还是"主题 class 没命中”。修复前后各截一张图一对比,现象立刻清楚。截图用 hdc shell snapshot_display 生成,再用 hdc file recv 拉到本地即可。
Q10:从这次适配能总结出哪条最通用的经验?
桌面端那些"初始化一定会成功""通信一定会到位"的隐含前提,到了鸿蒙环境可能因为通信方式、沙箱路径、数据库初始化顺序而变化。所以迁移时不能只看是不是白屏——页面能显示不代表运行时状态完整,尤其是主题、配置、插件、用户设置这类依赖主进程通信或本地数据库的状态,UI 层最好都有默认态兜底。
更多推荐




所有评论(0)