前言:为什么 Electron + 鸿蒙是开发者的新选择

随着鸿蒙系统在手机、PC、平板、智能设备等多终端的全面普及,其分布式架构和全场景生态已成为开发者关注的焦点。但对于习惯 Web 技术栈(HTML/CSS/JavaScript + Node.js)的开发者而言,直接转向 ArkTS + ArkUI 存在一定学习成本。而Electron for HarmonyOS的出现,为这类开发者提供了完美的过渡方案 —— 既能复用现有 Electron 项目的代码资产,又能快速接入鸿蒙生态,实现多终端应用开发。

本文基于最新的 Electron 34 稳定版 + 鸿蒙 Compatible SDK 5.0.5(API 17+),结合 KeeWeb 密码管理器、Swifty 文件工具等真实项目适配案例,从环境搭建、核心原理、项目迁移到性能优化,全程提供可直接运行的代码片段和官方资源链接,帮助不同层级的开发者快速上手鸿蒙 Electron 开发。

本文配套代码仓库:GitHub - electron-for-harmonyos/samples(含基础 Hello World、IPC 通信、Native 模块适配等完整案例)官方文档参考:HarmonyOS Electron 开发指南鸿蒙开发者社区:Electron 专项讨论区(问题答疑、资源分享)

一、鸿蒙 Electron 开发环境搭建(3 步快速上手)

1.1 环境要求清单

在开始开发前,需确保本地环境满足以下条件,避免后续出现兼容性问题:

工具 / 环境 版本要求 下载链接
操作系统 Windows 10/11、macOS 10.15+、Ubuntu 22.04+ -
DevEco Studio 5.0.0.100+ 华为开发者官网
Node.js v18.x LTS 或 v20.x LTS Node.js 官网
鸿蒙 SDK Compatible SDK 5.0.5(API 17+) DevEco Studio 内自动下载(需登录华为开发者账号)
Electron 预编译包 v34.6.0+(鸿蒙适配版) Electron 鸿蒙仓库 Release 页
目标设备 鸿蒙 NEXT(API 20+)PC / 平板 可使用鸿蒙模拟器

1.2 详细搭建步骤

步骤 1:安装 DevEco Studio 并配置 SDK
  1. 下载 DevEco Studio 5.0+,安装时勾选「HarmonyOS SDK」组件(默认已勾选);
  2. 打开 IDE 后,首次启动会提示「配置 SDK」,点击「Next」,选择「Compatible SDK 5.0.5」,等待下载完成(约 10-20 分钟,取决于网络速度);
  3. 配置项目签名(必需,否则无法运行到设备):
    • 进入「File → Project Structure → Signing Configs」;
    • 点击「Auto-generate signature」,IDE 会自动生成调试证书(无需手动创建);
    • 确认「Status」显示「Valid」,表示签名配置成功。
步骤 2:导入 Electron 预编译包

鸿蒙 Electron 提供了预编译的 HAP 包模板,开发者无需手动编译 Electron 核心库,直接导入即可:

  1. Electron 鸿蒙仓库下载预编译包(如v34.6.0-20251105.1-release.zip);
  2. 解压压缩包,得到ohos_hap目录,该目录是鸿蒙应用的核心结构;
  3. 在 DevEco Studio 中点击「File → Open」,选择ohos_hap目录,等待项目加载完成(首次加载会自动安装依赖,需耐心等待)。

项目核心目录结构说明(重点关注app目录):

plaintext

ohos_hap/
├── web_engine/                # 鸿蒙Web引擎模块(Electron依赖核心)
│   └── src/main/resources/resfile/resources/app/  # 开发者代码目录(关键!)
│       ├── main.js            # Electron主进程入口
│       ├── preload.js         # 预加载脚本(安全桥梁)
│       ├── index.html         # 渲染进程页面
│       └── package.json       # 项目依赖配置
├── libs/                      # 核心依赖库(含libelectron.so等)
└── module.json5               # 鸿蒙应用配置文件(权限、包名等)
步骤 3:配置 Node.js 环境

Electron 项目依赖 Node.js,需确保本地 Node.js 版本符合要求:

  1. 安装 Node.js v20.18.1(推荐使用 nvm 管理版本,避免版本冲突):
    • Windows(PowerShell):

      bash

      # 安装nvm(若未安装)
      winget install CoreyButler.NVMforWindows
      # 重启PowerShell后执行
      nvm install 20.18.1
      nvm use 20.18.1
      
    • macOS/Linux:

      bash

      # 安装nvm
      curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
      # 重启终端后执行
      nvm install 20.18.1
      nvm use 20.18.1
      
  2. 验证环境是否正常:

    bash

    node -v  # 输出 v20.18.1
    npm -v   # 输出 10.8.2(或更高版本)
    

1.3 第一个鸿蒙 Electron 应用:Hello World

接下来编写一个简单的 Electron 应用,验证环境是否正常运行:

步骤 1:编写核心代码

web_engine/src/main/resources/resfile/resources/app/目录下创建以下 5 个文件:

1. package.json(项目配置)

定义项目名称、版本、入口文件及依赖:

json

{
  "name": "ohos-electron-hello",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "build:ohos": "ohos-builder build"  # 鸿蒙打包脚本(后续用到)
  },
  "dependencies": {
    "electron": "^34.6.0"  # 需与预编译包版本一致
  },
  "devDependencies": {
    "@ohos/hap-builder": "^5.0.0"  # 鸿蒙HAP打包工具
  }
}
2. main.js(主进程入口)

Electron 主进程负责窗口创建、原生能力调用,需适配鸿蒙的硬件加速限制:

javascript

const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

// ⚠️ 鸿蒙PC关键配置:禁用硬件加速(避免渲染黑屏或卡顿)
app.disableHardwareAcceleration();
// 追加Chromium GPU禁用开关(双重保障,部分设备需此配置)
app.commandLine.appendSwitch('disable-gpu');

// 存储窗口实例(避免被GC回收)
let mainWindow = null;

// 创建应用窗口
function createWindow() {
  mainWindow = new BrowserWindow({
    width: 900,          // 窗口宽度
    height: 600,         // 窗口高度
    title: "鸿蒙Electron示例",  // 窗口标题
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),  // 预加载脚本路径
      contextIsolation: true,  // 开启上下文隔离(安全必需)
      nodeIntegration: false,  // 禁用Node集成(安全必需)
      webviewTag: true,        // 允许使用<webview>标签(按需开启)
      devTools: true           // 开发阶段开启调试工具
    }
  });

  // 加载渲染页面(本地HTML文件)
  mainWindow.loadFile(path.join(__dirname, 'index.html'));
  
  // 打开调试工具(开发阶段使用,发布时需关闭)
  mainWindow.webContents.openDevTools({ mode: 'right' });
  
  // 窗口关闭事件:释放实例
  mainWindow.on('closed', () => {
    mainWindow = null;
  });
}

// 鸿蒙生命周期适配:ready事件触发后创建窗口
app.whenReady().then(() => {
  createWindow();
  
  // 适配鸿蒙多窗口激活逻辑(macOS类似)
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

// 窗口关闭逻辑(适配鸿蒙多设备)
app.on('window-all-closed', () => {
  // 鸿蒙PC端无需保留应用进程,直接退出
  app.quit();
});

// IPC通信:主进程监听渲染进程消息(后续测试用)
ipcMain.handle('get-app-version', () => {
  return app.getVersion();  // 返回应用版本
});

ipcMain.on('show-message', (event, msg) => {
  console.log('渲染进程发送的消息:', msg);
  // 向渲染进程发送回复
  event.reply('message-reply', `主进程已收到:${msg}`);
});
3. preload.js(预加载脚本)

预加载脚本是主进程与渲染进程的安全桥梁,需通过contextBridge暴露 API(禁止直接暴露ipcRenderer):

javascript

const { contextBridge, ipcRenderer } = require('electron');

// 安全暴露API到渲染进程(挂载到window对象)
contextBridge.exposeInMainWorld('ohosElectronAPI', {
  // 调用主进程方法(异步)
  getAppVersion: () => ipcRenderer.invoke('get-app-version'),
  // 向主进程发送消息
  sendMessage: (msg) => ipcRenderer.send('show-message', msg),
  // 监听主进程回复
  onMessageReply: (callback) => {
    // 包装回调,避免直接暴露ipcRenderer
    const listener = (event, data) => callback(data);
    ipcRenderer.on('message-reply', listener);
    // 返回取消监听的方法(避免内存泄漏)
    return () => ipcRenderer.removeListener('message-reply', listener);
  }
});
4. index.html(渲染进程页面)

渲染进程负责 UI 展示,需配置鸿蒙兼容的 CSP 策略(避免脚本被拦截):

html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- ⚠️ 鸿蒙必需:放宽CSP策略以支持本地资源加载 -->
  <meta http-equiv="Content-Security-Policy" content="default-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;">
  <title>鸿蒙Electron Hello World</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body { font-family: "HarmonyOS Sans SC", -apple-system, sans-serif; text-align: center; padding: 50px 20px; background: #f5f7fa; }
    .container { max-width: 600px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
    h1 { color: #1a73e8; margin-bottom: 20px; }
    .version { margin: 20px 0; font-size: 18px; color: #666; }
    button { padding: 12px 24px; background: #1a73e8; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; margin-top: 10px; }
    button:hover { background: #1557b0; }
    .reply { margin-top: 20px; padding: 15px; background: #f0f7ff; border-radius: 4px; color: #0f62fe; text-align: left; }
  </style>
</head>
<body>
  <div class="container">
    <h1>🎉 Hello Electron on HarmonyOS!</h1>
    <div class="version">应用版本:<span id="app-version">加载中...</span></div>
    <button onclick="testIPC()">点击测试IPC通信</button>
    <div id="message-reply" class="reply" style="display: none;"></div>
  </div>

  <script src="renderer.js"></script>
</body>
</html>
5. renderer.js(渲染进程逻辑)

调用预加载脚本暴露的 API,实现 UI 交互和 IPC 通信:

javascript

// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', async () => {
  // 1. 获取应用版本并显示
  const appVersion = await window.ohosElectronAPI.getAppVersion();
  document.getElementById('app-version').textContent = appVersion;
  
  // 2. 监听主进程回复
  const removeListener = window.ohosElectronAPI.onMessageReply((data) => {
    const replyEl = document.getElementById('message-reply');
    replyEl.textContent = data;
    replyEl.style.display = 'block';
  });
  
  // 页面卸载时取消监听(避免内存泄漏)
  window.addEventListener('beforeunload', removeListener);
});

// 测试IPC通信
function testIPC() {
  const msg = `测试消息(${new Date().toLocaleTimeString()})`;
  window.ohosElectronAPI.sendMessage(msg);
}
步骤 2:安装依赖并运行
  1. 打开终端,进入app目录(关键路径,避免依赖安装错误):

    bash

    cd web_engine/src/main/resources/resfile/resources/app/
    
  2. 安装项目依赖(首次安装约 5-10 分钟):

    bash

    npm install
    
  3. 连接鸿蒙设备或启动模拟器:
    • 设备:开启「开发者模式」和「USB 调试」,通过 USB 连接电脑;
    • 模拟器:打开 DevEco Studio → 点击「Tools → Device Manager」→ 选择「HarmonyOS NEXT PC Emulator」启动;
  4. 运行应用:
    • 在 DevEco Studio 中点击顶部工具栏的「Run」按钮(或按Shift+F10);
    • 首次运行会自动编译 HAP 包并安装到设备,成功后设备会显示应用窗口,调试工具自动打开。

1.4 常见问题排查

报错信息 解决方案
「SysCap 不匹配:缺少 SystemCapability」 编辑module.json5,移除reqSysCapabilities中的测试类能力(如SystemCapability.Test.PerfTest
「找不到 libelectron.so」 重新下载 Electron 预编译包,确认libs/arm64-v8a目录包含libelectron.so文件
窗口黑屏 / 空白无内容 确保main.js中已添加app.disableHardwareAcceleration()disable-gpu开关
「CSP 阻止脚本执行」 index.html<meta>标签中配置正确的 CSP 策略(参考上述示例)
「签名验证失败」 重新生成签名:进入「Project Structure → Signing Configs」→ 点击「Reset」

二、鸿蒙 Electron 核心原理与架构差异

2.1 传统 Electron 与鸿蒙 Electron 的架构对比

要做好鸿蒙 Electron 开发,需先理解其与传统 Electron 的核心差异,避免按传统开发习惯踩坑:

对比维度 传统 Electron(Windows/macOS/Linux) 鸿蒙 Electron
渲染引擎 Chromium(完整 Blink + V8) 鸿蒙 Web 组件(基于 Chromium 深度定制,移除冗余模块)
JS 运行时 Node.js(主进程)+ V8(渲染进程) ArkCompiler + QuickJS(兼容 V8 语法,无 Node.js)
进程模型 主进程 + 多渲染进程(沙箱隔离) 单进程(Stage 模型)+ 鸿蒙 Ability(轻量级隔离)
原生能力访问 Node.js API + Native Modules 鸿蒙系统 API(@ohos.*)+ IPC 代理
文件系统 传统文件路径(如 C:/、/Users/) 鸿蒙沙箱路径(应用仅访问自身沙箱目录)
打包格式 .exe/.dmg/.AppImage .hap(鸿蒙应用包,支持多终端部署)
安全机制 preload + 上下文隔离 鸿蒙权限系统 + 进程隔离 + 强制安全配置

关键结论:鸿蒙 Electron不支持 Node.js 运行时,所有原生能力(如文件操作、系统信息)需通过鸿蒙系统 API 实现,且必须适配鸿蒙的沙箱和权限模型。

2.2 鸿蒙 Electron 的核心适配点

1. 生命周期适配:与鸿蒙 Stage 模型对齐

传统 Electron 的生命周期以app对象为核心(如readywindow-all-closed),而鸿蒙应用采用 Stage 模型,需将两者生命周期对齐:

传统 Electron 生命周期 鸿蒙 Stage 模型生命周期 适配逻辑
app.ready() onCreate() 鸿蒙应用启动后,先执行onCreate()初始化,再触发app.ready()创建窗口
window.close() onDestroy() 窗口关闭时,需调用鸿蒙terminateAbility()释放资源,避免内存泄漏
app.quit() onRelease() 应用退出前,需在onRelease()中保存数据(如用户配置)

代码示例:生命周期适配

javascript

// main.js(鸿蒙生命周期适配)
const { app, BrowserWindow } = require('electron');
const ohosAbility = require('@ohos.ability.uiAbility');  // 引入鸿蒙Ability模块

let mainWindow = null;

// 鸿蒙Ability onCreate生命周期(应用启动时执行)
ohosAbility.on('create', () => {
  console.log('鸿蒙Ability onCreate');
  // 初始化配置(如加载用户数据)
  initAppConfig();
});

// Electron ready事件(与鸿蒙onCreate对齐)
app.whenReady().then(() => {
  createWindow();
  // 注册鸿蒙Ability destroy事件(应用退出时执行)
  ohosAbility.on('destroy', () => {
    console.log('鸿蒙Ability destroy');
    // 保存数据并释放资源
    saveAppConfig();
    app.quit();
  });
});

function createWindow() {
  mainWindow = new BrowserWindow({/* 配置省略 */});
  mainWindow.loadFile('index.html');
}

// 初始化应用配置(从鸿蒙沙箱读取)
function initAppConfig() {
  const path = require('path');
  const fs = require('fs').promises;
  const configPath = path.join(app.getPath('userData'), 'config.json');
  
  fs.readFile(configPath, 'utf8')
    .then(data => console.log('配置加载成功:', JSON.parse(data)))
    .catch(err => console.log('配置文件不存在,创建默认配置:', err));
}

// 保存应用配置(写入鸿蒙沙箱)
function saveAppConfig() {
  const path = require('path');
  const fs = require('fs').promises;
  const configPath = path.join(app.getPath('userData'), 'config.json');
  const config = { theme: 'light', lastOpenTime: new Date().toISOString() };
  
  fs.writeFile(configPath, JSON.stringify(config, null, 2))
    .then(() => console.log('配置保存成功'))
    .catch(err => console.log('配置保存失败:', err));
}
2. 安全机制强化:强制安全配置

为符合鸿蒙系统的安全要求,鸿蒙 Electron 强制开启以下安全配置,开发者无法关闭:

  • contextIsolation: true:开启上下文隔离,渲染进程无法直接访问主进程的requiremodule等对象;
  • nodeIntegration: false:禁用 Node 集成,渲染进程无法直接调用 Node.js API;
  • 权限申请:访问敏感能力(如相机、位置、外部存储)需在module.json5中声明权限,且用户需手动授权;

权限配置示例(module.json5)

json5

{
  "module": {
    "name": "web_engine",
    "type": "feature",
    "srcEntry": "./ets/main_pages.ets",
    "description": "鸿蒙Electron应用模块",
    "mainElement": "EntryAbility",
    "deviceTypes": ["phone", "tablet", "pc"],  // 支持的设备类型
    "reqPermissions": [
      {
        "name": "ohos.permission.READ_USER_STORAGE",  // 读取用户存储权限
        "reason": "需要读取用户配置文件",
        "usedScene": {
          "ability": ["EntryAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.WRITE_USER_STORAGE",  // 写入用户存储权限
        "reason": "需要保存用户配置文件",
        "usedScene": {
          "ability": ["EntryAbility"],
          "when": "always"
        }
      }
    ]
  }
}
3. 文件路径适配:鸿蒙沙箱模型

鸿蒙采用沙箱机制,每个应用只能访问自身沙箱目录(无法直接访问系统目录或其他应用目录),需通过app.getPath()获取标准路径:

app.getPath()参数 传统 Electron 路径示例 鸿蒙 Electron 路径示例 用途
userData C:/Users/XXX/AppData/Roaming/XXX /data/storage/el2/base/haps/XXX/data/userdata/ 存储用户配置、缓存等
temp C:/Users/XXX/AppData/Local/Temp/XXX /data/storage/el2/base/haps/XXX/data/temp/ 存储临时文件
documents C:/Users/XXX/Documents /data/storage/el2/base/haps/XXX/data/documents/ 存储用户文档
downloads C:/Users/XXX/Downloads /data/storage/el2/base/haps/XXX/data/downloads/ 存储下载文件

代码示例:鸿蒙文件路径操作

javascript

// main.js(鸿蒙沙箱文件操作)
const { app } = require('electron');
const path = require('path');
const fs = require('fs').promises;

// 1. 获取用户数据目录(配置文件存储路径)
const userDataPath = app.getPath('userData');
const configPath = path.join(userDataPath, 'app_config.json');

// 2. 写入配置文件
async function writeConfig(config) {
  try {
    // 确保目录存在(鸿蒙沙箱目录默认已创建,但子目录需手动创建)
    await fs.mkdir(path.dirname(configPath), { recursive: true });
    await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf8');
    console.log('配置写入成功,路径:', configPath);
  } catch (err) {
    console.error('配置写入失败:', err);
  }
}

// 3. 读取配置文件
async function readConfig() {
  try {
    const data = await fs.readFile(configPath, 'utf8');
    return JSON.parse(data);
  } catch (err) {
    if (err.code === 'ENOENT') {
      // 文件不存在,返回默认配置
      return { theme: 'light', language: 'zh-CN' };
    }
    console.error('配置读取失败:', err);
    return null;
  }
}

// 测试文件操作
writeConfig({ theme: 'dark', language: 'en-US' })
  .then(() => readConfig())
  .then(config => console.log('读取到的配置:', config));

三、Electron 项目迁移到鸿蒙的完整指南

对于已有 Electron 项目的开发者,最关心的是如何快速迁移到鸿蒙。本节以开源项目 KeeWeb(密码管理器)为例,讲解复杂项目的迁移流程和关键适配点。

3.1 迁移方案选择(两种核心路径)

根据项目复杂度和需求,可选择以下两种迁移方案:

方案 原理 优势 适用场景
方案 A:鸿蒙 Web 组件迁移 用鸿蒙原生 Web 组件替代 Electron 的 BrowserWindow,通过 IPC 实现原生能力调用 性能优、鸿蒙生态适配好、支持多终端 新开发项目、简单 Electron 应用(无复杂 Native 模块)
方案 B:直接复用迁移 通过鸿蒙 Electron 适配器,直接运行现有 Electron 代码,仅适配关键模块 零成本复用代码、开发效率高、周期短 复杂 Electron 应用(含 Native 模块、多进程逻辑)

本文重点讲解方案 B(直接复用迁移),适用于大多数已有项目。

3.2 迁移实战:以 KeeWeb 为例

KeeWeb 是一款基于 Electron 的开源密码管理器(GitHub 地址),使用 Electron 13.x + jQuery + Handlebars,包含主进程、渲染进程、Native 模块(如keytar存储密钥),迁移难度中等,具有代表性。

步骤 1:项目结构调整

首先将 KeeWeb 的原始代码目录调整为鸿蒙 Electron 的标准结构:

原始 KeeWeb 目录

plaintext

KeeWeb/
├── app/                # 渲染进程代码(HTML/CSS/JS)
├── main/               # 主进程代码(main.js、ipc.js等)
├── preload/            # 预加载脚本(preload.js)
├── package.json        # 项目配置
└── resources/          # 静态资源(图标、语言文件等)

鸿蒙适配后目录

plaintext

ohos_hap/
├── web_engine/src/main/resources/resfile/resources/app/
│   ├── app/                # 保留原渲染进程代码(无需修改)
│   ├── main/               # 保留原主进程代码(需适配鸿蒙)
│   ├── preload/            # 保留原预加载脚本(需安全适配)
│   ├── package.json        # 调整依赖和脚本(关键)
│   └── harmony-adapter.js  # 鸿蒙适配层(Mock不支持的模块)
├── libs/                   # 鸿蒙Electron核心库(从预编译包获取)
└── module.json5            # 鸿蒙应用配置(权限、包名等)
步骤 2:package.json 适配(关键依赖调整)

KeeWeb 的原始package.json依赖了electronkeytarelectron-updater等模块,需调整为鸿蒙兼容版本:

json

{
  "name": "keeweb-ohos",
  "version": "1.18.0",
  "main": "./main/main.js",  # 主进程入口(与原项目一致)
  "scripts": {
    "start": "electron .",
    "build:ohos": "ohos-builder build --output ./dist",  # 鸿蒙打包脚本
    "lint": "eslint ."
  },
  "dependencies": {
    "electron": "^34.6.0",  # 升级到鸿蒙兼容的Electron版本
    "jquery": "^3.6.4",     # 保留原依赖(无需修改)
    "handlebars": "^4.7.8", # 保留原依赖(无需修改)
    "@ohos/security.storage": "^5.0.0"  # 鸿蒙安全存储模块(替代keytar)
  },
  "devDependencies": {
    "@ohos/hap-builder": "^5.0.0",  # 鸿蒙HAP打包工具
    "eslint": "^8.57.0"
  },
  "ohos": {
    "deviceTypes": ["pc", "tablet"],  # 支持的鸿蒙设备类型
    "hapName": "KeeWeb-ohos"          # HAP包名称
  }
}
步骤 3:主进程适配(核心修改点)

KeeWeb 的主进程代码(main/main.js)需适配鸿蒙的硬件加速、生命周期和 Native 模块:

1. 禁用硬件加速(必需)

在主进程入口添加鸿蒙关键配置:

javascript

// main/main.js(顶部添加)
const { app } = require('electron');

// 鸿蒙PC关键配置:禁用硬件加速
app.disableHardwareAcceleration();
app.commandLine.appendSwitch('disable-gpu');

// 原KeeWeb主进程代码...
2. 生命周期对齐(避免崩溃)

KeeWeb 原代码在app.ready()后直接创建窗口,需延迟创建以适配鸿蒙生命周期:

javascript

// main/main.js(修改窗口创建逻辑)
let mainWindow = null;

// 原代码:app.whenReady().then(createWindow);
// 鸿蒙适配:延迟500ms创建窗口,避免与鸿蒙Ability生命周期冲突
app.whenReady().then(() => {
  setTimeout(() => {
    createWindow();
    // 注册鸿蒙退出事件(保存数据)
    const ohosAbility = require('@ohos.ability.uiAbility');
    ohosAbility.on('destroy', () => {
      console.log('KeeWeb:鸿蒙应用退出,保存数据...');
      // 调用KeeWeb原有的数据保存方法
      saveAllDatabases();
      app.quit();
    });
  }, 500);
});

// 原窗口创建函数(无需修改,仅调整调用时机)
function createWindow() {
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    title: 'KeeWeb',
    webPreferences: {
      preload: path.join(__dirname, '../preload/preload.js'),
      contextIsolation: true,  // 鸿蒙强制开启,需确保原代码支持
      nodeIntegration: false,  // 鸿蒙强制关闭
      webviewTag: false
    }
  });

  mainWindow.loadURL(`file://${path.join(__dirname, '../app/index.html')}`);
  // 原窗口事件逻辑...
}
3. Native 模块适配(Mock 不支持的模块)

KeeWeb 使用keytar模块存储密码(Windows 的 Credential Manager、macOS 的 Keychain),鸿蒙不支持该模块,需用鸿蒙@ohos/security.storage模块替代,通过harmony-adapter.js实现 Mock:

javascript

// harmony-adapter.js(Mock keytar模块)
const ohosSecureStorage = require('@ohos/security.storage');

// 模拟keytar的API(与原模块接口一致)
const mockKeytar = {
  /**
   * 获取密码
   * @param {string} service - 服务名
   * @param {string} account - 账号名
   * @returns {Promise<string>} 密码
   */
  getPassword: async (service, account) => {
    try {
      // 鸿蒙安全存储的键:service_account(避免冲突)
      const key = `${service}_${account}`;
      const password = await ohosSecureStorage.get(key);
      return password || null;
    } catch (err) {
      console.error('keytar getPassword失败:', err);
      return null;
    }
  },

  /**
   * 保存密码
   * @param {string} service - 服务名
   * @param {string} account - 账号名
   * @param {string} password - 密码
   * @returns {Promise<void>}
   */
  setPassword: async (service, account, password) => {
    try {
      const key = `${service}_${account}`;
      await ohosSecureStorage.set(key, password);
    } catch (err) {
      console.error('keytar setPassword失败:', err);
      throw err;
    }
  },

  /**
   * 删除密码
   * @param {string} service - 服务名
   * @param {string} account - 账号名
   * @returns {Promise<boolean>} 是否删除成功
   */
  deletePassword: async (service, account) => {
    try {
      const key = `${service}_${account}`;
      await ohosSecureStorage.delete(key);
      return true;
    } catch (err) {
      console.error('keytar deletePassword失败:', err);
      return false;
    }
  }
};

// 替换Node.js的require缓存,让KeeWeb认为加载的是真实keytar
require.cache.keytar = { exports: mockKeytar };
// 全局暴露(如需在其他模块使用)
global.keytar = mockKeytar;

在主进程入口加载适配层(确保在require('keytar')之前):

javascript

// main/main.js(顶部添加)
require('../harmony-adapter.js');  // 加载鸿蒙适配层
const keytar = require('keytar');  // 此时加载的是mockKeytar

// 原KeeWeb代码...
步骤 4:预加载脚本适配(安全强化)

KeeWeb 的预加载脚本(preload/preload.js)原代码直接暴露ipcRenderer,需通过contextBridge安全暴露:

原预加载脚本(不安全,鸿蒙不支持)

javascript

// 原preload.js(不安全)
window.ipcRenderer = require('electron').ipcRenderer;
window.keytar = require('keytar');

鸿蒙适配后(安全合规)

javascript

// preload/preload.js(鸿蒙适配版)
const { contextBridge, ipcRenderer } = require('electron');

// 安全暴露API到渲染进程
contextBridge.exposeInMainWorld('keewebAPI', {
  // IPC通信:调用主进程方法
  invoke: (channel, ...args) => {
    // 白名单限制(仅允许KeeWeb使用的通道)
    const validChannels = ['db:open', 'db:save', 'config:get', 'config:set'];
    if (validChannels.includes(channel)) {
      return ipcRenderer.invoke(channel, ...args);
    }
    throw new Error(`非法IPC通道:${channel}`);
  },

  // IPC通信:发送消息
  send: (channel, ...args) => {
    const validChannels = ['window:minimize', 'window:maximize', 'window:close'];
    if (validChannels.includes(channel)) {
      ipcRenderer.send(channel, ...args);
    }
  },

  // IPC通信:监听主进程消息
  on: (channel, callback) => {
    const validChannels = ['db:changed', 'notification:show'];
    if (validChannels.includes(channel)) {
      const listener = (event, ...args) => callback(...args);
      ipcRenderer.on(channel, listener);
      // 返回取消监听方法(避免内存泄漏)
      return () => ipcRenderer.removeListener(channel, listener);
    }
    throw new Error(`非法IPC通道:${channel}`);
  },

  // 密码存储API(通过主进程代理,避免渲染进程直接访问)
  keytar: {
    getPassword: (service, account) => ipcRenderer.invoke('keytar:get', service, account),
    setPassword: (service, account, password) => ipcRenderer.invoke('keytar:set', service, account, password),
    deletePassword: (service, account) => ipcRenderer.invoke('keytar:delete', service, account)
  }
});

同时,需在主进程中实现keytar相关的 IPC 处理:

javascript

// main/ipc.js(添加keytar IPC处理)
const { ipcMain } = require('electron');
const keytar = require('keytar');

// 处理渲染进程的keytar请求
ipcMain.handle('keytar:get', (event, service, account) => {
  return keytar.getPassword(service, account);
});

ipcMain.handle('keytar:set', (event, service, account, password) => {
  return keytar.setPassword(service, account, password);
});

ipcMain.handle('keytar:delete', (event, service, account) => {
  return keytar.deletePassword(service, account);
});

// 原KeeWeb IPC处理逻辑...
步骤 5:测试与调试
  1. 安装依赖:

    bash

    cd web_engine/src/main/resources/resfile/resources/app/
    npm install
    
  2. 运行应用,测试核心功能:
    • 密码库打开 / 保存(验证文件操作适配);
    • 密码存储(验证keytar Mock 是否生效);
    • 窗口操作(最小化、最大化、关闭,验证生命周期适配);
  3. 调试技巧:
    • 主进程日志:在 DevEco Studio 的「Log」面板查看(过滤「Electron」标签);
    • 渲染进程调试:通过mainWindow.webContents.openDevTools()打开 Chrome 调试工具;
    • 鸿蒙系统日志:使用hdc logcat命令查看(需安装鸿蒙调试工具hdc)。

四、鸿蒙 Electron 性能优化技巧

迁移完成后,需针对鸿蒙设备进行性能优化,提升用户体验。以下是关键优化点:

4.1 渲染性能优化

  1. 禁用不必要的渲染功能

    • 关闭 DevTools(发布版本):在main.js中设置webPreferences.devTools: false
    • 禁用动画和过渡效果(低配置设备):在 CSS 中添加* { animation: none !important; transition: none !important; }
  2. 优化页面加载速度

    • 压缩静态资源(HTML/CSS/JS):使用terser压缩 JS,csso压缩 CSS;
    • 懒加载非关键资源:通过document.addEventListener('DOMContentLoaded', () => { /* 加载非关键资源 */ })

4.2 内存优化

  1. 避免内存泄漏

    • 及时取消 IPC 监听:渲染进程中调用onMessageReply返回的取消方法;
    • 释放窗口实例:窗口关闭时将mainWindow设为null,避免 GC 无法回收。
  2. 限制渲染进程内存

    javascript

    // main.js(设置渲染进程内存限制)
    app.commandLine.appendSwitch('js-flags', '--max-old-space-size=512');  // 限制为512MB
    

4.3 启动速度优化

  1. 减少启动时加载的模块

    • 延迟加载非关键模块(如日志、统计模块);
    • 使用require动态加载(而非import静态加载)。
  2. 预编译 HTML 模板

    • 对于 Handlebars 等模板引擎,预编译模板文件,避免运行时编译:

      javascript

      // 预编译Handlebars模板(主进程启动时执行)
      const handlebars = require('handlebars');
      const fs = require('fs').promises;
      const path = require('path');
      
      async function precompileTemplates() {
        const templatePath = path.join(__dirname, '../app/templates');
        const files = await fs.readdir(templatePath);
        
        for (const file of files) {
          if (file.endsWith('.hbs')) {
            const content = await fs.readFile(path.join(templatePath, file), 'utf8');
            const compiled = handlebars.precompile(content);
            // 保存预编译结果到缓存目录
            await fs.writeFile(
              path.join(app.getPath('temp'), `template_${file.replace('.hbs', '.js')}`),
              `module.exports = ${compiled};`,
              'utf8'
            );
          }
        }
      }
      
      // 启动时预编译
      app.whenReady().then(precompileTemplates);
      

五、总结与后续学习资源

5.1 本文核心总结

  1. 环境搭建:需使用 DevEco Studio 5.0+、鸿蒙 Compatible SDK 5.0.5 和 Electron 34 + 预编译包,关键是配置签名和禁用硬件加速;
  2. 核心差异:鸿蒙 Electron 无 Node.js 运行时,需通过 IPC 调用鸿蒙系统 API,且必须适配沙箱路径和权限模型;
  3. 项目迁移:复杂项目需适配主进程生命周期、Mock 不支持的 Native 模块、安全暴露 IPC API,重点是保持原代码接口一致;
  4. 性能优化:从渲染、内存、启动速度三方面入手,禁用不必要功能,减少资源占用。

5.2 后续学习资源

  1. 官方文档

  2. 开源项目

  3. 工具推荐

通过本文的学习,相信你已掌握鸿蒙 Electron 的核心开发能力。建议从简单项目入手,逐步尝试复杂项目迁移,遇到问题可在鸿蒙开发者社区提问,快速解决实战中的难点。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐