鸿蒙 Electron 整合实战:从跨端通信到鸿蒙原生应用打包的全流程
2026/5/4 15:39:01 网站建设 项目流程

在前两篇文章中,我们分别探讨了鸿蒙与 Electron 的基础消息交互、分布式数据同步与跨端能力调用。但在实际项目落地中,开发者还会面临Electron 应用接入鸿蒙服务生态鸿蒙原生能力与 Electron 组件的深度融合将 Electron 应用打包为鸿蒙桌面版安装包等关键问题。本文将聚焦这些落地层面的需求,通过完整的代码案例,实现从 Electron 与鸿蒙服务的深度绑定,到最终打包为鸿蒙原生应用的全流程,为开发者提供可直接落地的解决方案。

一、落地场景与技术选型

1. 核心落地场景

本次实战聚焦三个核心落地场景:

  • 鸿蒙账号集成:Electron 应用接入鸿蒙账号服务,实现统一的用户身份认证。
  • 鸿蒙文件服务调用:Electron 应用调用鸿蒙设备的分布式文件服务,实现文件跨设备读写。
  • Electron 应用鸿蒙化打包:将 Electron 应用打包为鸿蒙桌面版(OpenHarmony)的 HAP 安装包,实现原生部署。

2. 技术选型

在之前的技术栈基础上,新增以下关键技术:

  • 鸿蒙侧:使用鸿蒙账号服务 SDK分布式文件服务(DFS)OpenHarmony 应用打包工具
  • Electron 侧:使用鸿蒙服务端 SDK for Node.jsElectron-builder定制打包流程、Node-API实现 Electron 与鸿蒙原生模块的交互。
  • 通信层:使用gRPC替代 MQTT,实现更高效的跨端远程过程调用。

二、环境准备:新增依赖与配置

1. Electron 侧新增依赖

bash

运行

# 鸿蒙账号服务Node.js SDK(模拟,实际替换为官方SDK) npm install @ohos/account-node-sdk --save # 鸿蒙分布式文件服务SDK npm install @ohos/dfs-node-sdk --save # gRPC相关依赖 npm install @grpc/grpc-js @grpc/proto-loader --save # Electron打包工具(支持鸿蒙桌面版配置) npm install electron-builder --save-dev # Node-API绑定工具(用于调用鸿蒙原生模块) npm install node-addon-api --save-dev

2. 鸿蒙侧新增配置

entry/src/main/module.json5中添加鸿蒙账号和文件服务权限:

json5

{ "module": { // 原有配置... "requestPermissions": [ { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.DISTRIBUTED_DATASYNC" }, { "name": "ohos.permission.GET_NETWORK_INFO" }, { "name": "ohos.permission.ACCOUNT_MANAGER" // 鸿蒙账号权限 }, { "name": "ohos.permission.READ_USER_STORAGE" // 文件读取权限 }, { "name": "ohos.permission.WRITE_USER_STORAGE" // 文件写入权限 } ], "distributedConfiguration": { "deviceCommunication": true, "dataSync": true, "fileShare": true // 开启分布式文件共享 } } }

3. gRPC 协议文件定义

在项目根目录创建proto/harmony_service.proto,定义跨端服务接口:

protobuf

syntax = "proto3"; package harmony; // 鸿蒙账号服务接口 service AccountService { rpc Login (AccountRequest) returns (AccountResponse); rpc GetUserInfo (UserInfoRequest) returns (UserInfoResponse); } // 分布式文件服务接口 service FileService { rpc UploadFile (FileUploadRequest) returns (FileUploadResponse); rpc DownloadFile (FileDownloadRequest) returns (FileDownloadResponse); } // 账号请求参数 message AccountRequest { string account = 1; string password = 2; } // 账号响应结果 message AccountResponse { bool success = 1; string token = 2; string message = 3; } // 用户信息请求 message UserInfoRequest { string token = 1; } // 用户信息响应 message UserInfoResponse { string userId = 1; string username = 2; string avatar = 3; } // 文件上传请求 message FileUploadRequest { string token = 1; string fileName = 2; bytes fileContent = 3; } // 文件上传响应 message FileUploadResponse { bool success = 1; string fileUrl = 2; } // 文件下载请求 message FileDownloadRequest { string token = 1; string fileUrl = 2; } // 文件下载响应 message FileDownloadResponse { bool success = 1; bytes fileContent = 2; }

三、代码案例:鸿蒙账号集成与分布式文件操作

场景 1:Electron 集成鸿蒙账号服务(gRPC 实现)

1.1 鸿蒙侧:gRPC 服务端实现账号认证

typescript

运行

// entry/src/main/ets/utils/HarmonyGrpcServer.ts import grpc from '@ohos/grpc'; // 鸿蒙gRPC SDK import protoLoader from '@ohos/proto-loader'; import account from '@ohos.account.osAccount'; // 鸿蒙账号API import path from '@ohos.path'; // 加载proto文件 const protoPath = path.join(getContext().filesDir, 'harmony_service.proto'); const packageDefinition = protoLoader.loadSync(protoPath, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }); const harmonyProto = grpc.loadPackageDefinition(packageDefinition).harmony; // 实现账号服务接口 class AccountServiceImpl implements harmonyProto.AccountService { async Login(call: any, callback: any) { const { account, password } = call.request; try { // 调用鸿蒙账号服务进行登录验证(模拟,实际调用官方API) const osAccountManager = account.getOsAccountManager(getContext()); const accountInfo = await osAccountManager.getOsAccountById(account); if (accountInfo && password === '123456') { // 实际项目需加密验证 callback(null, { success: true, token: `token_${Date.now()}`, message: '登录成功' }); } else { callback(null, { success: false, token: '', message: '账号或密码错误' }); } } catch (error) { callback(null, { success: false, token: '', message: `登录失败:${error}` }); } } async GetUserInfo(call: any, callback: any) { const { token } = call.request; try { // 验证token并获取用户信息 if (token.startsWith('token_')) { callback(null, { userId: '10001', username: '鸿蒙用户', avatar: '/sdcard/avatar.jpg' }); } else { callback(null, { userId: '', username: '', avatar: '' }); } } catch (error) { callback(null, { userId: '', username: '', avatar: '' }); } } } // 启动gRPC服务 export function startGrpcServer() { const server = new grpc.Server(); server.addService(harmonyProto.AccountService.service, new AccountServiceImpl()); server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), (err: any, port: number) => { if (err) { console.error('gRPC服务启动失败:', err); return; } server.start(); console.log(`gRPC服务已启动,端口:${port}`); }); }
1.2 Electron 侧:gRPC 客户端调用鸿蒙账号服务

javascript

运行

// main/grpc/client.js const grpc = require('@grpc/grpc-js'); const protoLoader = require('@grpc/proto-loader'); const path = require('path'); // 加载proto文件 const protoPath = path.join(__dirname, '../../proto/harmony_service.proto'); const packageDefinition = protoLoader.loadSync(protoPath, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true }); const harmonyProto = grpc.loadPackageDefinition(packageDefinition).harmony; // 创建gRPC客户端 const accountClient = new harmonyProto.AccountService( '192.168.1.100:50051', // 鸿蒙设备的IP和gRPC端口 grpc.credentials.createInsecure() ); // 鸿蒙账号登录方法 exports.harmonyLogin = function (account, password, callback) { accountClient.Login({ account, password }, (err, response) => { if (err) { callback(err, null); } else { callback(null, response); } }); }; // 获取鸿蒙用户信息方法 exports.getHarmonyUserInfo = function (token, callback) { accountClient.GetUserInfo({ token }, (err, response) => { if (err) { callback(err, null); } else { callback(null, response); } }); };
1.3 Electron 渲染进程:账号登录 UI 与交互

html

预览

<!-- src/renderer/account.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>鸿蒙账号登录</title> <style> .login-container { max-width: 400px; margin: 50px auto; padding: 20px; border: 1px solid #ccc; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } .form-item { margin-bottom: 20px; } .form-item label { display: block; margin-bottom: 5px; font-weight: bold; } .form-item input { width: 100%; padding: 10px; font-size: 16px; border: 1px solid #ccc; border-radius: 4px; } #loginBtn { width: 100%; padding: 10px; font-size: 16px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } #loginBtn:hover { background-color: #0056b3; } .user-info { margin-top: 20px; padding: 10px; border: 1px solid #eee; border-radius: 4px; } </style> </head> <body> <div class="login-container"> <h2>鸿蒙账号登录</h2> <div class="form-item"> <label for="account">账号</label> <input type="text" id="account" placeholder="请输入鸿蒙账号"> </div> <div class="form-item"> <label for="password">密码</label> <input type="password" id="password" placeholder="请输入密码"> </div> <button id="loginBtn">登录</button> <div class="user-info" id="userInfo" style="display: none;"> <h3>用户信息</h3> <p>用户ID:<span id="userId"></span></p> <p>用户名:<span id="username"></span></p> <p>头像:<span id="avatar"></span></p> </div> </div> <script> const accountInput = document.getElementById('account'); const passwordInput = document.getElementById('password'); const loginBtn = document.getElementById('loginBtn'); const userInfo = document.getElementById('userInfo'); const userId = document.getElementById('userId'); const username = document.getElementById('username'); const avatar = document.getElementById('avatar'); loginBtn.addEventListener('click', () => { const account = accountInput.value.trim(); const password = passwordInput.value.trim(); if (!account || !password) { alert('请输入账号和密码'); return; } // 调用主进程的鸿蒙登录方法 window.electronAPI.harmonyLogin(account, password, (err, response) => { if (err) { alert(`登录失败:${err.message}`); return; } if (response.success) { alert(response.message); // 获取用户信息 window.electronAPI.getHarmonyUserInfo(response.token, (err, user) => { if (err) { alert(`获取用户信息失败:${err.message}`); return; } userInfo.style.display = 'block'; userId.textContent = user.userId; username.textContent = user.username; avatar.textContent = user.avatar; }); } else { alert(response.message); } }); }); </script> </body> </html>

场景 2:Electron 调用鸿蒙分布式文件服务

2.1 鸿蒙侧:扩展 gRPC 服务实现文件操作

typescript

运行

// entry/src/main/ets/utils/HarmonyGrpcServer.ts(新增文件服务实现) import fileIo from '@ohos.fileio'; import distributedFile from '@ohos.distributedFile'; // 实现文件服务接口 class FileServiceImpl implements harmonyProto.FileService { async UploadFile(call: any, callback: any) { const { token, fileName, fileContent } = call.request; try { // 验证token if (!token.startsWith('token_')) { callback(null, { success: false, fileUrl: '' }); return; } // 写入分布式文件 const dfs = distributedFile.getDistributedFileSystem(getContext()); const filePath = `/sdcard/harmony_files/${fileName}`; await dfs.writeFile(filePath, fileContent); callback(null, { success: true, fileUrl: filePath }); } catch (error) { callback(null, { success: false, fileUrl: '', message: `${error}` }); } } async DownloadFile(call: any, callback: any) { const { token, fileUrl } = call.request; try { // 验证token if (!token.startsWith('token_')) { callback(null, { success: false, fileContent: new Uint8Array(0) }); return; } // 读取分布式文件 const dfs = distributedFile.getDistributedFileSystem(getContext()); const fileContent = await dfs.readFile(fileUrl); callback(null, { success: true, fileContent: fileContent }); } catch (error) { callback(null, { success: false, fileContent: new Uint8Array(0) }); } } } // 在startGrpcServer中添加文件服务 server.addService(harmonyProto.FileService.service, new FileServiceImpl());
2.2 Electron 侧:实现文件上传与下载

javascript

运行

// main/grpc/client.js(新增文件服务方法) const fileClient = new harmonyProto.FileService( '192.168.1.100:50051', grpc.credentials.createInsecure() ); // 上传文件到鸿蒙设备 exports.uploadFileToHarmony = function (token, fileName, fileContent, callback) { fileClient.UploadFile({ token, fileName, fileContent }, (err, response) => { if (err) { callback(err, null); } else { callback(null, response); } }); }; // 从鸿蒙设备下载文件 exports.downloadFileFromHarmony = function (token, fileUrl, callback) { fileClient.DownloadFile({ token, fileUrl }, (err, response) => { if (err) { callback(err, null); } else { callback(null, response); } }); };
2.3 Electron 渲染进程:文件操作 UI

html

预览

<!-- src/renderer/file.html --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>鸿蒙分布式文件操作</title> <style> .file-container { max-width: 600px; margin: 50px auto; padding: 20px; border: 1px solid #ccc; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } .file-item { margin-bottom: 20px; } #uploadFile { margin: 10px 0; } button { padding: 10px 20px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; } button:hover { background-color: #218838; } #fileUrlInput { width: 70%; padding: 10px; margin-right: 10px; } </style> </head> <body> <div class="file-container"> <h2>鸿蒙分布式文件操作</h2> <div class="file-item"> <h3>文件上传</h3> <input type="file" id="uploadFile"> <button id="uploadBtn">上传到鸿蒙设备</button> <p id="uploadResult"></p> </div> <div class="file-item"> <h3>文件下载</h3> <input type="text" id="fileUrlInput" placeholder="请输入鸿蒙文件路径(如/sdcard/harmony_files/test.txt)"> <button id="downloadBtn">从鸿蒙设备下载</button> <p id="downloadResult"></p> </div> </div> <script> const uploadFile = document.getElementById('uploadFile'); const uploadBtn = document.getElementById('uploadBtn'); const uploadResult = document.getElementById('uploadResult'); const fileUrlInput = document.getElementById('fileUrlInput'); const downloadBtn = document.getElementById('downloadBtn'); const downloadResult = document.getElementById('downloadResult'); // 上传文件 uploadBtn.addEventListener('click', () => { const file = uploadFile.files[0]; if (!file) { alert('请选择要上传的文件'); return; } const reader = new FileReader(); reader.onload = (e) => { const fileContent = new Uint8Array(e.target.result); // 从本地存储获取登录token(实际项目需持久化存储) const token = localStorage.getItem('harmonyToken') || ''; if (!token) { alert('请先登录鸿蒙账号'); return; } window.electronAPI.uploadFileToHarmony(token, file.name, fileContent, (err, response) => { if (err) { uploadResult.textContent = `上传失败:${err.message}`; return; } if (response.success) { uploadResult.textContent = `上传成功,文件路径:${response.fileUrl}`; fileUrlInput.value = response.fileUrl; } else { uploadResult.textContent = `上传失败:${response.message}`; } }); }; reader.readAsArrayBuffer(file); }); // 下载文件 downloadBtn.addEventListener('click', () => { const fileUrl = fileUrlInput.value.trim(); if (!fileUrl) { alert('请输入文件路径'); return; } const token = localStorage.getItem('harmonyToken') || ''; if (!token) { alert('请先登录鸿蒙账号'); return; } window.electronAPI.downloadFileFromHarmony(token, fileUrl, (err, response) => { if (err) { downloadResult.textContent = `下载失败:${err.message}`; return; } if (response.success) { // 将文件内容保存到本地 const blob = new Blob([response.fileContent], { type: 'application/octet-stream' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = fileUrl.split('/').pop(); a.click(); downloadResult.textContent = '下载成功'; } else { downloadResult.textContent = '下载失败'; } }); }); </script> </body> </html>

场景 3:Electron 应用打包为鸿蒙桌面版 HAP 包

3.1 配置 Electron 打包脚本

在项目根目录创建electron-builder.json5

json5

{ "appId": "com.example.harmonyelectron", "productName": "HarmonyElectronApp", "directories": { "output": "dist" }, "files": [ "src/**/*", "main/**/*", "proto/**/*", "node_modules/**/*", "package.json" ], // 鸿蒙桌面版配置(基于OpenHarmony) "linux": { "target": [ { "target": "deb", "arch": ["x64"] } ], "category": "Utility" }, // 自定义鸿蒙HAP打包钩子 "afterPack": "./scripts/build-harmony-hap.js" }
3.2 编写鸿蒙 HAP 打包脚本

创建scripts/build-harmony-hap.js

javascript

运行

const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); // 鸿蒙HAP打包脚本 exports.default = async function (context) { const appOutDir = context.appOutDir; const harmonyHapDir = path.join(__dirname, '../harmony-hap'); // 1. 创建鸿蒙HAP项目目录(提前初始化好的OpenHarmony项目) if (!fs.existsSync(harmonyHapDir)) { console.error('鸿蒙HAP项目目录不存在'); return; } // 2. 复制Electron打包产物到鸿蒙HAP项目的assets目录 const assetsDir = path.join(harmonyHapDir, 'entry/src/main/assets'); if (!fs.existsSync(assetsDir)) { fs.mkdirSync(assetsDir, { recursive: true }); } // 复制Electron可执行文件和资源 fs.readdirSync(appOutDir).forEach(file => { const src = path.join(appOutDir, file); const dest = path.join(assetsDir, file); if (fs.lstatSync(src).isDirectory()) { fs.cpSync(src, dest, { recursive: true }); } else { fs.copyFileSync(src, dest); } }); // 3. 调用DevEco Studio的打包命令生成HAP包 try { // 注意:需要配置DevEco Studio的环境变量 execSync('hvigor build --mode module -p product=default', { cwd: harmonyHapDir, stdio: 'inherit' }); console.log('鸿蒙HAP包打包成功'); // 4. 复制HAP包到dist目录 const hapSrc = path.join(harmonyHapDir, 'entry/build/outputs/hap/debug/entry-debug.hap'); const hapDest = path.join(context.outDir, 'HarmonyElectronApp.hap'); fs.copyFileSync(hapSrc, hapDest); console.log(`HAP包已复制到:${hapDest}`); } catch (error) { console.error('鸿蒙HAP包打包失败:', error); } };
3.3 鸿蒙侧适配 Electron 运行环境

在鸿蒙 HAP 项目的entry/src/main/ets/Application/MyApplication.ets中添加 Electron 启动逻辑:

typescript

运行

import AbilityStage from '@ohos.app.ability.AbilityStage'; import process from '@ohos.process'; import path from '@ohos.path'; export default class MyAbilityStage extends AbilityStage { onCreate() { super.onCreate(); // 启动Electron应用(鸿蒙桌面版需支持Node.js运行时) const electronPath = path.join(this.context.filesDir, 'HarmonyElectronApp'); const childProcess = process.spawn(electronPath, [], { cwd: this.context.filesDir }); childProcess.stdout.on('data', (data) => { console.log(`Electron输出:${data}`); }); childProcess.stderr.on('data', (data) => { console.error(`Electron错误:${data}`); }); } }

四、运行与测试流程

1. 鸿蒙账号登录测试

  1. 在鸿蒙设备上启动 gRPC 服务,确保账号服务正常运行。
  2. 启动 Electron 应用,进入账号登录页面,输入鸿蒙账号和密码,验证登录功能及用户信息获取。

2. 分布式文件操作测试

  1. 登录后,选择本地文件上传到鸿蒙设备,验证文件是否成功写入分布式文件系统。
  2. 输入鸿蒙设备的文件路径,下载文件到 Electron 本地,验证文件读取功能。

3. 鸿蒙 HAP 包打包与部署

  1. 执行npm run build命令,Electron-builder 会先打包 Electron 应用,再调用脚本生成鸿蒙 HAP 包。
  2. 将生成的 HAP 包安装到鸿蒙桌面版设备,验证应用是否能正常启动并运行。

五、落地优化与注意事项

1. 性能优化

  • gRPC 连接池:在 Electron 中实现 gRPC 连接池,避免频繁创建和销毁连接,提升通信效率。
  • 文件分块传输:大文件上传 / 下载时采用分块传输,避免内存溢出。
  • 鸿蒙运行时优化:在鸿蒙桌面版中优化 Node.js 运行时性能,提升 Electron 应用的启动速度。

2. 安全注意事项

  • 账号密码加密:鸿蒙账号登录时,使用 HTTPS/gRPC TLS 加密传输账号和密码,避免明文传输。
  • Token 有效期:为鸿蒙登录 Token 设置有效期,实现自动刷新机制,提升安全性。
  • 文件权限控制:鸿蒙分布式文件操作时,添加文件权限校验,限制非法文件访问。

3. 兼容性处理

  • 鸿蒙版本兼容:针对不同版本的鸿蒙系统(API 9/10/11),做兼容性适配,确保功能正常运行。
  • Electron 版本适配:选择与鸿蒙桌面版 Node.js 运行时兼容的 Electron 版本,避免运行时冲突。

六、总结

本文通过鸿蒙账号集成、分布式文件操作、应用鸿蒙化打包三个核心案例,完成了鸿蒙 Electron 整合从开发到落地的全流程。相较于前两篇的技术探索,本文更注重实际项目的落地细节,解决了开发者在项目部署时遇到的关键问题。

随着鸿蒙桌面版的不断普及,Electron 与鸿蒙的融合将成为跨端开发的重要方向。开发者可以基于本文的方案,结合业务需求,进一步拓展功能场景,如集成鸿蒙支付服务、推送服务等,最终打造出覆盖桌面、移动端、智能设备的全场景鸿蒙应用。同时,也需要关注鸿蒙生态的更新,及时适配新的 API 和工具链,让应用始终保持最佳的运行状态。

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询