Electron for 鸿蒙PC:用 Node-API 打通原生系统调用,告别“Web 孤岛

Electron for 鸿蒙PC:用 Node-API 打通原生系统调用,告别"Web 孤岛"


前言

把 Electron 应用移植到鸿蒙 PC 平台,最大的拦路虎不是 UI,而是原生系统能力

在 Windows 上,你可以通过 node-ffiwinapi 或 Shell 命令随意调用系统功能。但鸿蒙 PC 的系统 API 是 C/C++ 接口,Electron 的 Node.js 运行时无法直接触达。解法只有一个:Node-API(原 N-API) ------用 C++ 编写原生插件,再通过 require() 暴露给 Electron 主进程。

本文将从零开始,带你完成以下实战目标:

  1. 搭建 Electron for HarmonyOS 的 Node-API 开发环境
  2. 编写一个获取系统硬件信息的原生插件
  3. 实现文件系统监控(inotify 风格 Watch)
  4. 主进程与渲染进程通过 IPC 消费原生数据
  5. 打包并在鸿蒙 PC 上运行

一、技术架构总览

复制代码
┌─────────────────────────────────────────────────┐
│              Electron 渲染进程 (Chromium)         │
│   Vue/React SPA ──IPC(contextBridge)──▶ 主进程   │
└──────────────────────────┬──────────────────────┘
                           │ require('./addon.node')
┌──────────────────────────▼──────────────────────┐
│           Node.js 主进程 (Node-API 层)            │
│   addon.node  ──C++ FFI──▶  鸿蒙系统 C API       │
└─────────────────────────────────────────────────┘

核心链路:渲染进程 → IPC → Node.js 主进程 → Node-API 插件(.node)→ 鸿蒙原生 C API


二、环境搭建

2.1 前置要求

工具 版本 用途
DevEco Studio 5.0.5+ 鸿蒙 SDK 和模拟器
Electron for HarmonyOS 34.x 定制版 Electron
node-gyp 10.x 编译 .node 插件
cmake-js 7.x CMake 构建插件(可选)
Python 3.10+ node-gyp 依赖

2.2 获取 Electron for HarmonyOS

bash 复制代码
# 克隆官方适配仓库
git clone https://gitee.com/openharmony-tpc/electron.git
cd electron
git checkout ohos-main   # 切到鸿蒙分支

# 安装专用 npm 包(内含鸿蒙 SDK 路径)
npm install @ohos/electron-napi-headers --save-dev

2.3 配置 node-gyp 指向鸿蒙 SDK

bash 复制代码
# 设置环境变量(以 DevEco Studio 默认路径为例)
export OHOS_SDK_HOME=$HOME/DevEco-Studio/sdk/HarmonyOS-NEXT-DB6
export GYP_DEFINES="ohos_sdk=$OHOS_SDK_HOME"

# 验证
node-gyp list

三、编写第一个 Node-API 插件:获取系统硬件信息

3.1 目录结构

复制代码
electron-hmos-demo/
├── native/
│   ├── binding.gyp          # node-gyp 构建配置
│   ├── sysinfo.cpp          # 原生插件主体
│   └── include/
│       └── ohos_sysinfo.h   # 鸿蒙系统信息头文件
├── main.js                  # Electron 主进程
├── preload.js               # 预加载脚本
├── renderer/
│   └── index.html           # 渲染页面
└── package.json

3.2 编写原生插件 sysinfo.cpp

cpp 复制代码
// native/sysinfo.cpp
#include <napi.h>
#include <string>

// 鸿蒙 PC 系统信息 API(ohos.systemCapability.deviceInfo)
#ifdef __OHOS__
#include "device_info.h"   // HarmonyOS SDK 头文件
#else
// Windows/Mac 开发环境 mock
#include <sys/utsname.h>
#endif

// 获取设备型号
Napi::String GetDeviceModel(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

#ifdef __OHOS__
  // 鸿蒙原生 API:OH_DeviceInfo_GetDeviceModel
  char model[256] = {0};
  OH_DeviceInfo_GetDeviceModel(model, sizeof(model));
  return Napi::String::New(env, std::string(model));
#else
  // 非鸿蒙环境返回 mock 数据
  return Napi::String::New(env, "HarmonyOS-PC-Mock");
#endif
}

// 获取系统版本
Napi::String GetOsVersion(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

#ifdef __OHOS__
  char version[128] = {0};
  OH_DeviceInfo_GetSystemVersion(version, sizeof(version));
  return Napi::String::New(env, std::string(version));
#else
  struct utsname buf;
  uname(&buf);
  return Napi::String::New(env, std::string(buf.release));
#endif
}

// 获取可用内存(MB)
Napi::Number GetFreeMemory(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

#ifdef __OHOS__
  // 鸿蒙 OH_Memory API
  OH_MemoryInfo_t memInfo;
  OH_Memory_GetInfo(&memInfo);
  double freeMB = static_cast<double>(memInfo.freeBytes) / 1024.0 / 1024.0;
  return Napi::Number::New(env, freeMB);
#else
  // 开发环境用 Node.js 内置 API mock
  return Napi::Number::New(env, 4096.0);
#endif
}

// 异步获取 CPU 使用率(演示异步 Node-API 用法)
class CpuUsageWorker : public Napi::AsyncWorker {
public:
  CpuUsageWorker(Napi::Function& callback)
    : Napi::AsyncWorker(callback), cpuUsage_(0.0) {}

  void Execute() override {
#ifdef __OHOS__
    OH_CpuInfo_t cpuInfo;
    OH_Cpu_GetUsage(&cpuInfo);
    cpuUsage_ = cpuInfo.totalUsage * 100.0;
#else
    cpuUsage_ = 23.5;  // mock
#endif
  }

  void OnOK() override {
    Napi::HandleScope scope(Env());
    Callback().Call({Env().Null(), Napi::Number::New(Env(), cpuUsage_)});
  }

private:
  double cpuUsage_;
};

Napi::Value GetCpuUsageAsync(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  if (info.Length() < 1 || !info[0].IsFunction()) {
    Napi::TypeError::New(env, "Callback function required").ThrowAsJavaScriptException();
    return env.Null();
  }

  Napi::Function callback = info[0].As<Napi::Function>();
  auto* worker = new CpuUsageWorker(callback);
  worker->Queue();
  return env.Undefined();
}

// 模块初始化 & 导出
Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set("getDeviceModel",  Napi::Function::New(env, GetDeviceModel));
  exports.Set("getOsVersion",    Napi::Function::New(env, GetOsVersion));
  exports.Set("getFreeMemory",   Napi::Function::New(env, GetFreeMemory));
  exports.Set("getCpuUsageAsync", Napi::Function::New(env, GetCpuUsageAsync));
  return exports;
}

NODE_API_MODULE(sysinfo, Init)

3.3 编写 binding.gyp

json 复制代码
{
  "targets": [{
    "target_name": "sysinfo",
    "sources": ["sysinfo.cpp"],
    "include_dirs": [
      "<!@(node -p \"require('node-addon-api').include\")",
      "<(OHOS_SDK_HOME)/native/sysapi/include"
    ],
    "libraries": [
      "-L<(OHOS_SDK_HOME)/native/sysapi/lib",
      "-ldevice_info",
      "-lmemory_info",
      "-lcpu_info"
    ],
    "defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
    "cflags_cc": ["-std=c++17"],
    "conditions": [
      ["OS=='ohos'", {
        "defines": ["__OHOS__=1"]
      }]
    ]
  }]
}

3.4 编译插件

bash 复制代码
cd native
# 首次编译
node-gyp configure --arch=x64 --target_platform=ohos
node-gyp build

# 输出产物:native/build/Release/sysinfo.node

四、在 Electron 主进程中调用插件

4.1 主进程 main.js

javascript 复制代码
// main.js
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');

// 加载原生插件(自动适配 Debug/Release)
let sysinfo;
try {
  sysinfo = require('./native/build/Release/sysinfo.node');
  console.log('[Node-API] sysinfo plugin loaded successfully');
} catch (err) {
  console.error('[Node-API] Failed to load sysinfo plugin:', err.message);
  // Fallback: 使用 JS 层 mock(开发阶段)
  sysinfo = {
    getDeviceModel: () => 'HarmonyPC-DEV',
    getOsVersion: () => '5.0.0-OHOS',
    getFreeMemory: () => 8192,
    getCpuUsageAsync: (cb) => setTimeout(() => cb(null, 15.8), 100)
  };
}

function createWindow() {
  const win = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,    // 必须开启!
      nodeIntegration: false,    // 必须关闭!
    }
  });

  win.loadFile('renderer/index.html');
}

app.whenReady().then(createWindow);

// ===== IPC 处理:同步系统信息查询 =====
ipcMain.handle('get-sys-info', async () => {
  return {
    deviceModel: sysinfo.getDeviceModel(),
    osVersion:   sysinfo.getOsVersion(),
    freeMemoryMB: sysinfo.getFreeMemory(),
  };
});

// ===== IPC 处理:异步 CPU 使用率 =====
ipcMain.handle('get-cpu-usage', () => {
  return new Promise((resolve, reject) => {
    sysinfo.getCpuUsageAsync((err, usage) => {
      if (err) reject(err);
      else resolve(usage);
    });
  });
});

4.2 预加载脚本 preload.js

javascript 复制代码
// preload.js - 安全地将 IPC 能力暴露给渲染进程
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
  // 获取系统信息(一次性)
  getSysInfo: () => ipcRenderer.invoke('get-sys-info'),

  // 获取 CPU 使用率
  getCpuUsage: () => ipcRenderer.invoke('get-cpu-usage'),

  // 订阅实时 CPU 更新(推模式)
  onCpuUpdate: (callback) => {
    ipcRenderer.on('cpu-update', (_event, value) => callback(value));
    // 返回取消订阅函数
    return () => ipcRenderer.removeAllListeners('cpu-update');
  }
});

五、渲染进程展示数据

html 复制代码
<!-- renderer/index.html -->
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>鸿蒙 PC 系统监控</title>
  <style>
    body { font-family: 'HarmonyOS Sans', sans-serif; background: #0d1117; color: #e6edf3; padding: 2rem; }
    .card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 1.5rem; margin-bottom: 1rem; }
    .metric { font-size: 2rem; font-weight: bold; color: #58a6ff; }
    .label  { font-size: 0.85rem; color: #8b949e; margin-top: 0.25rem; }
    #cpu-bar { height: 8px; background: #21262d; border-radius: 4px; margin-top: 0.5rem; }
    #cpu-fill { height: 100%; background: linear-gradient(90deg, #238636, #3fb950); border-radius: 4px; transition: width 0.5s; }
  </style>
</head>
<body>
  <h1>🖥️ HarmonyOS PC 系统监控</h1>

  <div class="card">
    <div class="label">设备型号</div>
    <div class="metric" id="device-model">加载中...</div>
  </div>

  <div class="card">
    <div class="label">系统版本</div>
    <div class="metric" id="os-version">加载中...</div>
  </div>

  <div class="card">
    <div class="label">可用内存</div>
    <div class="metric" id="free-mem">加载中...</div>
  </div>

  <div class="card">
    <div class="label">CPU 使用率</div>
    <div class="metric" id="cpu-usage">0%</div>
    <div id="cpu-bar"><div id="cpu-fill" style="width:0%"></div></div>
  </div>

  <script>
    (async () => {
      // 一次性获取系统信息
      const info = await window.electronAPI.getSysInfo();
      document.getElementById('device-model').textContent = info.deviceModel;
      document.getElementById('os-version').textContent = info.osVersion;
      document.getElementById('free-mem').textContent =
        `${info.freeMemoryMB.toFixed(0)} MB`;

      // 定时刷新 CPU 使用率
      const refreshCpu = async () => {
        const usage = await window.electronAPI.getCpuUsage();
        document.getElementById('cpu-usage').textContent = `${usage.toFixed(1)}%`;
        document.getElementById('cpu-fill').style.width = `${usage}%`;
      };

      await refreshCpu();
      setInterval(refreshCpu, 2000);   // 每 2 秒刷新
    })();
  </script>
</body>
</html>

六、打包部署到鸿蒙 PC

6.1 package.json 关键配置

json 复制代码
{
  "name": "electron-hmos-sysinfo",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "build:native": "cd native && node-gyp rebuild --target_platform=ohos",
    "pack:hmos": "electron-builder --platform=ohos --arch=x64",
    "dev": "electron ."
  },
  "build": {
    "appId": "com.example.electron.sysinfo",
    "productName": "SysMonitor",
    "ohos": {
      "target": "default",
      "hap": {
        "type": "entry",
        "bundleName": "com.example.electron.sysinfo"
      }
    },
    "files": [
      "main.js",
      "preload.js",
      "renderer/**",
      "native/build/Release/*.node"
    ]
  },
  "dependencies": {
    "node-addon-api": "^8.1.0"
  },
  "devDependencies": {
    "electron": "34.0.0-ohos",
    "electron-builder": "^25.0.0"
  }
}

6.2 打包命令

bash 复制代码
# 1. 编译原生插件(针对鸿蒙架构)
npm run build:native

# 2. 打包为鸿蒙 HAP 包
npm run pack:hmos

# 3. 通过 DevEco Studio 安装到鸿蒙 PC
hdc shell install dist/com.example.electron.sysinfo-1.0.0.hap

七、踩坑指南

坑1:.node 文件 RPATH 问题

鸿蒙 PC 的动态库搜索路径与 Linux 不同,需在 binding.gyp 中显式设置:

json 复制代码
"ldflags": ["-Wl,-rpath,@loader_path/../lib"]

坑2:异步 Worker 线程崩溃

Node-API 的 AsyncWorker::Execute() 运行在 libuv 线程池中,不能在此直接调用任何 Napi 对象 。如需传递数据,必须用成员变量暂存,在 OnOK() 中再转换为 JS 值。

坑3:contextIsolation 导致 require 不可用

渲染进程绝对不能require('electron'),必须通过 preload.js + contextBridge 传递能力。违反此规则会导致安全漏洞,且在新版 Electron 中直接报错。

坑4:鸿蒙 SDK 版本不匹配

OH_DeviceInfo_GetDeviceModel 在 HarmonyOS NEXT DB6 之前的 API Level 不存在,确保 sdk-version 对齐:

json 复制代码
// module.json5
"metadata": [
  { "name": "ohos.SDK.version", "value": "12" }
]

八、总结与扩展方向

通过 Node-API,Electron 应用获得了直接调用鸿蒙 PC 原生 C API 的能力,从根本上解决了"Web 孤岛"问题。

原生能力 Node-API 可达性 典型场景
硬件信息 ✅ 完全支持 设备授权、运维监控
文件系统 ✅ 完全支持 文件管理器、IDE
系统通知 ✅ 完全支持 消息提醒应用
蓝牙/NFC ⚠️ 部分支持 需额外权限申请
相机/麦克风 ⚠️ 受沙箱限制 HAP 需声明权限
分布式能力 🔄 开发中 多设备协同

下一步推荐阅读:

  • 官方仓库:https://gitcode.com/openharmony-sig/electron
  • Node-API 文档:nodejs.org/api/n-api.html
  • 应用如何接入Electron框架:https://developer.huawei.com/consumer/cn/blog/topic/03208728403662067

💡 如果本文对你有帮助,欢迎点赞收藏!如有疑问欢迎评论区留言。

相关推荐
AlbertZein1 小时前
跨项目设计模式(二):策略模式——从 ImageKnife 的加载器到 HMRouter 的生命周期
harmonyos
AlbertZein1 小时前
跨项目设计模式(一):单例模式在鸿蒙框架中的 6 种实现
harmonyos
前端不太难1 小时前
鸿蒙 App 的登录 / 订单 / 支付系统拆解
华为·状态模式·harmonyos
eric*16881 小时前
鸿蒙全局安全水印组件实践:支持动态更新、全局生效、自定义样式
华为·harmonyos
AlbertZein1 小时前
跨项目设计模式(三):责任链 / 拦截器——OkHttp → HMRouter → ImageKnifePro
harmonyos
且听风吟_xincell15 小时前
ArkTS 声明式 UI 的本质:状态映射
ui·harmonyos
闲坐含香咀翠17 小时前
Electron 加载原生模块总崩溃?搞懂这两行配置就够了
前端·electron·客户端
guo_zhen_qian19 小时前
鸿蒙模拟器WebView使用Chrome inspect调试
chrome·华为·harmonyos
生活观察站21 小时前
2026鸿蒙生态适配工具测评|跨平台app开发平台选型指南
华为·harmonyos