Electron 跑在鸿蒙 PC 上没问题,但上架签名我栽了三天
先说结论,省得你往后翻:Electron 跑在鸿蒙 PC 上一点问题都没有,但直接拿 electron-builder 出的鸿蒙产物去传华为应用市场,签名一定过不了审核。
我栽在这个问题上整整三天,从周四下午到周日晚上,把鸿蒙开发者文档翻了两遍,把 electron-builder 的鸿蒙插件源码扒了,又去华为开发者联盟的工单系统里蹲了两天才问出真实原因。今天就把我踩过的坑和最终能跑通的做法给你写出来。
一、事情是这样开始的
雷达鸭(收录一人公司真实赚钱案例的 App,鸿蒙版在华为应用市场能搜到)的 Windows 客户端我用的是 Electron,这套代码本来要直接迁到鸿蒙 PC 端去。我心想:鸿蒙支持 WebView 容器,又兼容 Electron 的运行时,无非就是换个打包工具的事。
事实上也确实只是换个打包工具的事——直到上架这一步。
我先是跑了一遍 electron-builder --win --x64,让它输出鸿蒙 PC 架构的产物。然后打开华为开发者联盟的"应用上架"页面,按照流程上传了 .exe 安装包和 .dmg 镜像(Electron 跨平台产物的常规组合)。点了"提交审核"。
半天后收到审核意见:
“您的应用安装包签名不符合《华为应用市场应用签名规范》,请使用华为提供的签名工具重新签名后上传。”
我看到这句话第一反应是:"我这就是普通的代码签名证书啊,Windows 上别的应用市场都认的。"然后我天真地打开了 Windows 的证书管理器,看了看我的代码签名证书——Valid from 2024-01-01 to 2027-01-01,颁发者是 DigiCert,标准 EV 证书。
我说:“这证书哪里不规范了?”
我又把 .exe 拉进 signtool verify /v /pa 跑了一遍,签名链完整、签名时间戳正常、证书没过期。
但华为应用市场就是拒收。
二、我以为是证书问题,其实是工具链问题
你猜怎么着,我重蹈覆辙折腾了整整一天才意识到一件事:
华为应用市场对于"鸿蒙 PC 应用"这个分类,根本不认 Windows 传统的代码签名。
它要的是鸿蒙自己的 .p7b 签名——也就是 hvigorw 或者 deveco 那一套用鸿蒙证书签出来的格式。这个签名不是给 Windows 操作系统看的,是给鸿蒙的 Ability Runtime 加载校验用的。
等一下,这里我漏说一个前提。
如果你直接把 Electron 当成 Windows 应用来上架(也就是选择"PC 应用 → Windows"分类),那普通的代码签名证书是认的,我前面 .exe 那个其实可以过。
但如果你想进"鸿蒙 PC 应用"分类(也就是走鸿蒙原生应用商店推荐位的那个分类),它就只认鸿蒙自己的签名。 哪怕你的代码是 Electron 写的,跑在鸿蒙 WebView 容器里,提交时也要是鸿蒙签名的格式。
——这是两套完全平行的世界观。
我当天晚上画了个表帮自己理清:
| | |
|---|
| DigiCert / GlobalSign 等 CA | |
| | |
| | |
| | |
| | |
| | |
问题来了:Electron-builder 出来的产物是 PE 文件,不是 .hap。它没有"鸿蒙 .hap 包"这个概念。
你拿一个 .exe 去签 .p7b,工具会直接报错:
ERROR: Failed to sign 'radar-duck-setup.exe': This file is not a HAP/HAR/HSP package. Only OpenHarmony packages can be signed with the HarmonyOS profile.
我盯着这个错信息愣了五分钟,然后才反应过来:这条路从根本上就走不通。
三、我最终用的"缝合怪"做法
OK,故事讲到这里开始讲方案。
如果你和我一样,主体代码用 Electron 写、但要上华为应用市场的"鸿蒙 PC 应用"分类,目前唯一靠谱的做法是——
用 DevEco Studio 起一个鸿蒙原生壳工程,把 Electron 的产物塞进这个壳的 WebView 组件里,然后让这个壳工程走 hvigorw 的标准签名流程。
听起来很恶心对不对?我也觉得。但没办法,鸿蒙的"原生应用商店"分类是按"是不是 .hap 包"来卡人的。
具体步骤如下。
第一步:先在 DevEco Studio 里新建一个 Empty Ability 工程
这一步没什么坑。打开 DevEco Studio → New Project → Empty Ability。Language 选 ArkTS(不要选 C++,那个是给 NDK 用的)。Compile SDK 选 5.0.0(12),Min SDK 选 5.0.0(12)——这里有个细节我等下说。
工程名叫 RadarDuckShell 之类的。建好后不要急着写 ArkUI 界面。
第二步:在 EntryAbility 里加载 Electron 的 WebView 容器
打开 entry/src/main/ets/entryability/EntryAbility.ets,把 onWindowStageCreate 改一下:
import UIAbility from '@ohos.app.ability.UIAbility';import window from '@ohos.window';import webview from '@ohos.web.webview';import { BusinessError } from '@ohos.base';export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { windowStage.loadContent('pages/Index', (err: BusinessError) => { if (err.code) { console.error('Failed to load the content. Cause: ' + JSON.stringify(err)); return; } // 等 Index 页面加载完成后再注入 WebView webview.WebviewController.setWebDebuggingAccess(true); }); }}
第三步:写一个全屏 WebView,把 Electron 产物的本地路径塞进去
entry/src/main/ets/pages/Index.ets:
@Entry@Componentstruct Index { webController: webview.WebviewController = new webview.WebviewController(); build() { Column() { Web({ src: rawfile 的资源会有路径解析 Bug,具体表现是页面 404 但控制台什么错都不报。混合模式必须开 MixedMode.All。否则你的 Electron 代码里如果用了 XHR 跨域请求本地资源,会被鸿蒙的 WebView 拦截。.hap 包体积限制 200MB。Electron 渲染层如果塞了太多图片资源、字体、或者没做懒加载,分分钟超。我手头这个项目的 web 端原始包是 80MB,进了 .hap 之后算上鸿蒙壳和资源压缩变成了 110MB,离 200MB 还有空间,但已经够紧。别忘了在 module.json5 里声明 WebView 权限:{ "module": { "requestPermissions": [ { "name": "ohos.permission.INTERNET" } ] }}不开 INTERNET 权限,XHR 一概失败,控制台也是什么错都不报——我在这又浪费了半小时。六、收尾所以你看,Electron 跑在鸿蒙 PC 上一点问题都没有——本地能跑、模拟器能装、UI 正常——只有上架这一关是真正的鸿蒙特色。如果你也在用 Electron 做鸿蒙 PC 应用,我个人最讨厌的就是这种"主体代码不用鸿蒙技术栈,但上架必须套个壳"的设计——它不是技术问题,是生态问题。但短期内我们改变不了华为应用市场的分类规则,只能适应它。如果让我重来一次,我不会花那三天去研究怎么让 electron-builder 直接产 .hap。我会一开始就走壳工程方案——ArkTS 壳只承担"被鸿蒙识别"的责任,WebView 里跑的是 Electron 的渲染产物(或者你用 Vite 直接 build 出来的 web 包也行,不用 Electron 那套),职责清晰,签名通畅。你遇到过 Electron 上架鸿蒙的坑吗?或者你用的是 Tauri / Wails 而不是 Electron?留言说说你的方案,咱们互相参考一下。关于作者:10+ 年搬砖经验的 Web 前端 + 鸿蒙 ArkTS 北向开发老兵,软件设计师 + 注册人工智能工程师 + agent 工程师,平时写写技术文章,也搞 AI 自动化和小产品(雷达鸭就是其中一个)。CSDN 不定期更新,欢迎偶遇。