鸿蒙 + Electron 跨端开发进阶实战:从桌面应用到鸿蒙多设备协同(附完整代码)

引言:跨端开发的破局之路 —— 鸿蒙与 Electron 的强强联合

在全场景智慧时代,用户对应用的需求早已不再局限于单一设备。鸿蒙 OS 以分布式软总线一次开发多端部署的核心能力,构建了覆盖手机、平板、车机、智慧屏的全场景生态;而 Electron 凭借Web 技术栈快速开发桌面应用的特性,成为开发者打造跨平台桌面工具的首选框架。

但单独使用两者时,都会面临明显的短板:鸿蒙桌面应用开发门槛高,Web 生态兼容性弱;Electron 应用则缺乏多设备协同能力,无法接入鸿蒙的分布式硬件资源。因此,鸿蒙与 Electron 的融合开发成为破局关键 —— 既能用 HTML/CSS/JS 快速构建桌面交互界面,又能借助鸿蒙的分布式能力实现多设备联动,真正做到 "一套代码,多端赋能"。

本文将围绕鸿蒙与 Electron 的深度集成展开,从底层通信原理到上层应用开发,结合 3 个实战项目,提供可直接落地的技术方案。全文包含超 10 段核心代码、8 个官方资源链接,适合前端开发者、鸿蒙生态工程师学习参考,助力大家快速掌握跨端开发的核心技能。

一、技术底层:鸿蒙与 Electron 融合的核心原理

1.1 融合架构设计

鸿蒙与 Electron 的融合并非简单的 "移植",而是基于进程间通信(IPC) 和分布式服务的架构整合,核心分为三层:

  1. 应用层:Electron 负责桌面端的 UI 渲染与交互逻辑,鸿蒙应用负责多设备的硬件调用与数据同步;
  2. 通信层:通过 WebSocket、鸿蒙分布式数据服务(DDS)实现跨设备、跨进程的数据传输;
  3. 硬件层:鸿蒙设备提供传感器、摄像头、蓝牙等硬件能力,Electron 桌面端作为控制中枢下发指令。

整体架构如下图所示:

plaintext

Electron桌面端(主控) 
    ↓ ↑ WebSocket/DDS通信
鸿蒙设备集群(执行)
    - 手机:传感器数据采集
    - 平板:大屏展示
    - 智慧屏:音视频输出

1.2 关键技术对比与选型

技术方案 适用场景 优势 局限性
WebSocket 通信 局域网内设备协同 实时性高、开发成本低 依赖网络、不支持离线通信
鸿蒙分布式数据服务 跨设备数据共享 鸿蒙原生支持、离线同步 仅适用于鸿蒙设备、配置复杂
Native 桥接(C++) 高性能硬件调用 低延迟、高兼容性 开发门槛高、跨平台性差

选型建议:开发初期优先使用 WebSocket 实现快速验证;产品化阶段结合鸿蒙 DDS 提升稳定性与离线能力;高性能场景(如音视频处理)可采用 C++ Native 桥接方案。

1.3 开发环境前置要求

  • 硬件:Windows/macOS 开发机(内存≥16GB)、鸿蒙设备(API 9+,支持开发者模式)
  • 软件
  • 账号:华为开发者账号(完成实名认证,用于应用签名):华为开发者联盟

二、实战项目 1:基础通信 ——Electron 与鸿蒙设备的 WebSocket 双向交互

本项目实现最基础的跨设备通信能力:Electron 桌面端作为 WebSocket 服务端,鸿蒙设备作为客户端,完成指令下发数据上传的双向交互。

2.1 项目结构搭建plaintext

harmony-electron-demo/
├── electron-main/          # Electron桌面端代码
│   ├── main.js             # 主进程:WebSocket服务+窗口管理
│   ├── preload.js          # 预加载脚本:安全通信桥
│   ├── renderer/           # 渲染进程:UI界面
│   │   ├── index.html
│   │   ├── style.css
│   │   └── renderer.js
│   └── package.json
└── harmony-device/         # 鸿蒙设备端代码
    └── entry/
        └── src/main/ets/
            ├── MainAbility.ets  # 鸿蒙应用入口:WebSocket客户端
            └── pages/
                └── Index.ets    # 鸿蒙UI页面

2.2 Electron 桌面端实现(服务端)

2.2.1 主进程代码(main.js)

主进程负责启动 WebSocket 服务、创建桌面窗口、处理设备通信逻辑。

运行

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

let mainWindow;
let wss; // WebSocket服务实例

// 创建Electron窗口
function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true, // 开启上下文隔离,保障安全
      nodeIntegration: false   // 禁用Node.js集成
    }
  });

  // 加载渲染进程页面
  mainWindow.loadFile(path.join(__dirname, 'renderer/index.html'));
  // 开发阶段打开调试工具
  mainWindow.webContents.openDevTools();
}

// 启动WebSocket服务
function startWSServer() {
  wss = new WebSocket.Server({ port: 9000 });
  console.log('WebSocket服务已启动:ws://localhost:9000');

  // 监听设备连接
  wss.on('connection', (ws) => {
    console.log('鸿蒙设备已连接');
    // 向渲染进程发送连接状态
    mainWindow.webContents.send('device-status', 'connected');

    // 接收设备上传的数据
    ws.on('message', (message) => {
      const data = JSON.parse(message.toString());
      console.log('收到设备数据:', data);
      // 转发数据到渲染进程
      mainWindow.webContents.send('device-data', data);
    });

    // 监听连接关闭
    ws.on('close', () => {
      console.log('鸿蒙设备已断开');
      mainWindow.webContents.send('device-status', 'disconnected');
    });

    // 发送初始化指令
    ws.send(JSON.stringify({ type: 'init', content: '欢迎连接桌面端' }));
  });
}

// 监听渲染进程的指令下发请求
ipcMain.on('send-command', (event, command) => {
  if (wss && wss.clients.size > 0) {
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify({ type: 'command', data: command }));
      }
    });
  }
});

// 应用生命周期管理
app.whenReady().then(() => {
  createWindow();
  startWSServer(); // 启动WebSocket服务
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});
2.2.2 预加载脚本(preload.js)

用于在主进程与渲染进程之间建立安全通信桥,避免直接暴露 Node.js API。

运行

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

// 向渲染进程暴露安全API
contextBridge.exposeInMainWorld('electronAPI', {
  // 接收设备状态
  onDeviceStatus: (callback) => ipcRenderer.on('device-status', (event, status) => callback(status)),
  // 接收设备数据
  onDeviceData: (callback) => ipcRenderer.on('device-data', (event, data) => callback(data)),
  // 下发指令到设备
  sendCommand: (command) => ipcRenderer.send('send-command', command)
});
2.2.3 渲染进程代码(index.html + renderer.js)

预览

<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>鸿蒙-Electron通信 Demo</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <h1>鸿蒙设备通信控制台</h1>
    <div class="status">设备状态:<span id="status">未连接</span></div>
    <div class="data-view">设备数据:<span id="data">无</span></div>
    <div class="command-input">
      <input type="text" id="command" placeholder="输入指令发送到设备">
      <button id="send-btn">发送指令</button>
    </div>
  </div>
  <script src="renderer.js"></script>
</body>
</html>

运行

// renderer.js
const statusEl = document.getElementById('status');
const dataEl = document.getElementById('data');
const commandInput = document.getElementById('command');
const sendBtn = document.getElementById('send-btn');

// 监听设备状态
window.electronAPI.onDeviceStatus((status) => {
  statusEl.textContent = status;
  statusEl.style.color = status === 'connected' ? 'green' : 'red';
});

// 监听设备数据
window.electronAPI.onDeviceData((data) => {
  dataEl.textContent = JSON.stringify(data);
});

// 发送指令到设备
sendBtn.addEventListener('click', () => {
  const command = commandInput.value.trim();
  if (command) {
    window.electronAPI.sendCommand(command);
    commandInput.value = '';
  }
});

2.3 鸿蒙设备端实现(客户端)

鸿蒙设备端通过 WebSocket 连接桌面端,实现数据上传与指令接收。

2.3.1 MainAbility.ets(应用入口)

运行

import web_socket from '@ohos.net.webSocket';
import hilog from '@ohos.hilog';

let ws: web_socket.WebSocket | null = null;

export default class MainAbility extends Ability {
  onCreate(want, launchParam) {
    hilog.info(0x0000, 'TEST_TAG', '%{public}s', 'Ability onCreate');
    this.initWebSocket();
  }

  // 初始化WebSocket连接
  initWebSocket() {
    ws = web_socket.createWebSocket();
    // 替换为桌面端的局域网IP
    const url = 'ws://192.168.1.105:9000';

    ws.connect(url, (err, _) => {
      if (err) {
        hilog.error(0x0000, 'TEST_TAG', 'WebSocket连接失败:%{public}s', err.message);
        // 重试机制
        setTimeout(() => this.initWebSocket(), 3000);
        return;
      }
      hilog.info(0x0000, 'TEST_TAG', 'WebSocket连接成功');

      // 接收桌面端指令
      ws.on('message', (message: string) => {
        const data = JSON.parse(message);
        hilog.info(0x0000, 'TEST_TAG', '收到桌面端指令:%{public}s', JSON.stringify(data));
        // 转发指令到UI页面
        globalThis.commandData = data;
      });

      // 监听连接关闭
      ws.on('close', () => {
        hilog.info(0x0000, 'TEST_TAG', 'WebSocket连接关闭');
        setTimeout(() => this.initWebSocket(), 3000);
      });
    });
  }

  // 向桌面端发送数据
  sendDataToDesktop(data: object) {
    if (ws && ws.readyState === web_socket.WebSocketState.OPEN) {
      ws.send(JSON.stringify(data), (err) => {
        if (err) {
          hilog.error(0x0000, 'TEST_TAG', '数据发送失败:%{public}s', err.message);
        }
      });
    }
  }

  onDestroy() {
    hilog.info(0x0000, 'TEST_TAG', '%{public}s', 'Ability onDestroy');
    if (ws) {
      ws.close();
    }
  }
}
2.3.2 Index.ets(UI 页面)

运行

@Entry
@Component
struct Index {
  @State message: string = '设备数据上传 Demo'
  @State deviceData: string = ''
  @State command: string = '无'

  build() {
    Column() {
      Text(this.message)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .margin(20)

      Text(`接收的指令:${this.command}`)
        .fontSize(20)
        .margin(10)

      Button('上传设备信息')
        .onClick(() => {
          const data = {
            deviceName: 'HarmonyOS Phone',
            time: new Date().toLocaleString(),
            battery: '85%'
          };
          this.deviceData = JSON.stringify(data);
          // 调用Ability的方法发送数据
          const ability = getContext(this) as Ability;
          (ability as any).sendDataToDesktop(data);
        })
        .margin(20)

      Text(`上传的数据:${this.deviceData}`)
        .fontSize(16)
        .margin(10)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  // 监听全局指令数据变化
  aboutToAppear() {
    setInterval(() => {
      if (globalThis.commandData) {
        this.command = JSON.stringify(globalThis.commandData);
      }
    }, 1000);
  }
}

2.4 运行测试步骤

  1. 启动 Electron 桌面端

    bash

    运行

    cd electron-main
    npm install
    npm start
    
  2. 配置鸿蒙设备端:修改MainAbility.ets中的桌面端 IP 为实际局域网 IP;
  3. 运行鸿蒙应用:在 DevEco Studio 中连接鸿蒙设备,点击 "运行" 按钮;
  4. 功能验证
    • 设备端点击 "上传设备信息",桌面端接收并显示数据;
    • 桌面端输入指令并发送,设备端接收并显示指令内容。

三、实战项目 2:进阶应用 —— 鸿蒙传感器数据在 Electron 桌面端可视化

本项目基于基础通信能力,实现鸿蒙设备传感器数据采集 + Electron 桌面端可视化展示,核心使用鸿蒙的加速度传感器 API和 Electron 的Chart.js图表库

3.1 鸿蒙设备端:加速度传感器数据采集

在鸿蒙项目中添加传感器权限与数据采集逻辑。

3.1.1 添加权限配置(module.json5)

module.json5reqPermissions中添加传感器权限:

{
  "module": {
    "reqPermissions": [
      {
        "name": "ohos.permission.ACCELEROMETER" // 加速度传感器权限
      }
    ]
  }
}
3.1.2 传感器数据采集代码(Index.ets)

运行

import sensor from '@ohos.sensor';
import { BusinessError } from '@ohos.base';

@Entry
@Component
struct Index {
  @State message: string = '加速度传感器数据采集'
  @State x: number = 0
  @State y: number = 0
  @State z: number = 0
  private accelerometerListener: sensor.AccelerometerResponse | null = null

  build() {
    Column() {
      Text(this.message)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .margin(20)

      Text(`X轴:${this.x.toFixed(2)} m/s²`)
        .fontSize(20)
        .margin(5)
      Text(`Y轴:${this.y.toFixed(2)} m/s²`)
        .fontSize(20)
        .margin(5)
      Text(`Z轴:${this.z.toFixed(2)} m/s²`)
        .fontSize(20)
        .margin(5)

      Button('开始采集')
        .onClick(() => this.startCollect())
        .margin(10)
      Button('停止采集')
        .onClick(() => this.stopCollect())
        .margin(10)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  // 开始采集传感器数据
  startCollect() {
    this.accelerometerListener = (data) => {
      this.x = data.x;
      this.y = data.y;
      this.z = data.z;
      // 每100ms上传一次数据到桌面端
      const ability = getContext(this) as Ability;
      (ability as any).sendDataToDesktop({
        type: 'accelerometer',
        x: data.x,
        y: data.y,
        z: data.z,
        time: Date.now()
      });
    }

    try {
      sensor.on(sensor.SensorTypeId.ACCELEROMETER, this.accelerometerListener, { interval: sensor.SensorInterval.SENSOR_INTERVAL_100MS });
    } catch (err) {
      console.error(`传感器监听失败:${(err as BusinessError).message}`);
    }
  }

  // 停止采集
  stopCollect() {
    if (this.accelerometerListener) {
      sensor.off(sensor.SensorTypeId.ACCELEROMETER, this.accelerometerListener);
      this.accelerometerListener = null;
    }
  }

  aboutToDisappear() {
    this.stopCollect();
  }
}

3.2 Electron 桌面端:数据可视化展示

在 Electron 渲染进程中集成 Chart.js,实现传感器数据的实时折线图展示。

3.2.1 安装 Chart.js

cd electron-main
npm install chart.js --save
3.2.2 渲染进程代码修改(renderer.js)

运行

import Chart from 'chart.js/auto';

const statusEl = document.getElementById('status');
const dataEl = document.getElementById('data');
const commandInput = document.getElementById('command');
const sendBtn = document.getElementById('send-btn');

// 初始化图表
const ctx = document.getElementById('sensorChart').getContext('2d');
const sensorChart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: [], // 时间轴
    datasets: [
      {
        label: 'X轴加速度',
        data: [],
        borderColor: 'red',
        tension: 0.1
      },
      {
        label: 'Y轴加速度',
        data: [],
        borderColor: 'green',
        tension: 0.1
      },
      {
        label: 'Z轴加速度',
        data: [],
        borderColor: 'blue',
        tension: 0.1
      }
    ]
  },
  options: {
    responsive: true,
    scales: {
      y: {
        title: {
          display: true,
          text: '加速度 (m/s²)'
        }
      },
      x: {
        title: {
          display: true,
          text: '时间'
        }
      }
    }
  }
});

// 监听设备数据并更新图表
window.electronAPI.onDeviceData((data) => {
  if (data.type === 'accelerometer') {
    // 限制数据点数量为50个
    if (sensorChart.data.labels.length > 50) {
      sensorChart.data.labels.shift();
      sensorChart.data.datasets.forEach(dataset => dataset.data.shift());
    }
    // 添加新数据
    sensorChart.data.labels.push(new Date(data.time).toLocaleTimeString());
    sensorChart.data.datasets[0].data.push(data.x);
    sensorChart.data.datasets[1].data.push(data.y);
    sensorChart.data.datasets[2].data.push(data.z);
    sensorChart.update();
  }
  dataEl.textContent = JSON.stringify(data);
});

// 原有代码保持不变
window.electronAPI.onDeviceStatus((status) => {
  statusEl.textContent = status;
  statusEl.style.color = status === 'connected' ? 'green' : 'red';
});

sendBtn.addEventListener('click', () => {
  const command = commandInput.value.trim();
  if (command) {
    window.electronAPI.sendCommand(command);
    commandInput.value = '';
  }
});
3.2.3 修改 HTML 页面(index.html)

添加图表容器:

预览

<div class="chart-container">
  <canvas id="sensorChart"></canvas>
</div>

添加 CSS 样式:

.chart-container {
  width: 90%;
  height: 300px;
  margin: 20px auto;
}

3.3 功能验证

  1. 启动 Electron 桌面端与鸿蒙应用;
  2. 鸿蒙设备端点击 "开始采集",手持设备晃动;
  3. 桌面端实时显示加速度数据的折线图,X/Y/Z 轴数据随设备运动动态变化。

四、实战项目 3:高级特性 —— 鸿蒙原子化服务与 Electron 应用的联动

鸿蒙原子化服务是免安装、即用即走的轻量化应用形态,本项目将 Electron 应用适配为鸿蒙原子化服务的 "控制中枢",实现服务卡片触发 Electron 应用启动Electron 应用控制原子化服务运行的联动效果。

4.1 核心适配逻辑

  1. Electron 应用打包:将 Electron 桌面端打包为鸿蒙 PC 可执行文件;
  2. 原子化服务开发:开发鸿蒙服务卡片,点击卡片触发 Electron 应用启动;
  3. 跨应用通信:通过鸿蒙分布式任务调度实现原子化服务与 Electron 应用的通信。

4.2 关键代码实现

4.2.1 Electron 应用打包(package.json)

使用electron-builder打包为鸿蒙 PC 支持的格式:

{
  "scripts": {
    "build:harmony": "electron-builder --linux --arm64"
  },
  "build": {
    "appId": "com.example.harmonyelectron",
    "productName": "HarmonyElectronDemo",
    "linux": {
      "target": [
        {
          "target": "AppImage",
          "arch": ["arm64"]
        }
      ]
    }
  }
}

执行打包命令:

运行

npm run build:harmony
4.2.2 鸿蒙原子化服务卡片代码(CardAbility.ets)

运行

import card from '@ohos.service.card';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';

export default class CardAbility extends card.CardExtensionAbility {
  onCreate(want: Want) {
    hilog.info(0x0000, 'TEST_TAG', '卡片创建');
  }

  onUpdate(formId: string) {
    // 更新卡片数据
    const formData = {
      title: 'Electron控制卡片',
      content: '点击启动桌面应用'
    };
    card.updateForm(formId, formData).then(() => {
      hilog.info(0x0000, 'TEST_TAG', '卡片更新成功');
    });
  }

  // 点击卡片触发事件
  onTriggerForm(formId: string, message: string) {
    // 启动Electron应用
    const want: Want = {
      bundleName: 'com.example.harmonyelectron',
      abilityName: 'MainAbility',
      action: 'ohos.want.action.startApplication'
    };
    this.context.startAbility(want).then(() => {
      hilog.info(0x0000, 'TEST_TAG', '启动Electron应用成功');
    });
  }
}

4.3 部署与验证

  1. 将打包后的 Electron 应用安装到鸿蒙 PC 设备;
  2. 在鸿蒙设备桌面添加原子化服务卡片;
  3. 点击卡片,自动启动 Electron 应用;
  4. Electron 应用下发指令,控制鸿蒙原子化服务执行特定任务(如播放音乐、显示通知)。

五、性能优化与常见问题解决方案

5.1 性能优化策略

  1. 通信优化
    • 数据压缩:使用 gzip 压缩传输数据,减少网络开销;
    • 批量发送:传感器数据采用批量上传,降低通信频率。
  2. Electron 优化
    • 禁用硬件加速:app.disableHardwareAcceleration(),避免鸿蒙 PC 渲染异常;
    • 进程隔离:将耗时任务放在独立进程,避免阻塞主进程。
  3. 鸿蒙端优化
    • 传感器采样频率:根据需求调整,非必要不使用高频采样;
    • 后台运行:配置鸿蒙应用后台运行权限,避免被系统杀死。

5.2 常见问题解决方案

问题现象 原因分析 解决方案
WebSocket 连接失败 1. 设备不在同一局域网;2. 端口被占用 1. 检查网络配置;2. 更换端口(如 9001);3. 关闭防火墙
传感器数据采集失败 未申请权限或权限被拒绝 1. 在 module.json5 中添加权限;2. 引导用户手动授予权限
Electron 应用在鸿蒙 PC 无法启动 打包架构不匹配 使用--arm64参数打包,适配鸿蒙 PC 的 ARM 架构
原子化服务卡片不显示 卡片配置错误 检查 module.json5 中的extensionAbilities配置,确保cardSize合法

六、生态资源与未来展望

6.1 核心学习资源

  1. 鸿蒙官方文档HarmonyOS 开发者文档中心
  2. Electron 官方文档Electron API Reference
  3. OpenHarmony SIG Electron 项目Gitee 仓库(鸿蒙官方维护的 Electron 适配项目)
  4. 鸿蒙传感器开发指南传感器 API 文档

6.2 未来展望

  1. 官方适配增强:华为 OpenHarmony 社区正在推进 Electron 原生适配,未来将提供更完善的工具链与 API;
  2. 能力深度融合:Electron 应用将可直接调用鸿蒙的分布式能力,无需额外的通信层开发;
  3. 跨端生态统一:Web 技术栈将成为鸿蒙跨端开发的核心之一,Electron 作为 Web 桌面应用的代表,将在鸿蒙生态中发挥更大作用。

结语

鸿蒙与 Electron 的融合开发,为跨端应用开发提供了全新的思路 —— 既保留了 Web 技术栈的高效开发优势,又能充分利用鸿蒙的全场景能力。本文通过 3 个实战项目,从基础通信到高级联动,完整覆盖了鸿蒙 + Electron 开发的核心流程。

随着鸿蒙生态的不断发展,这种跨端开发模式将在更多场景中落地,助力开发者打造真正的全场景智慧应用。希望本文能为大家的学习与实践提供帮助,让我们一起探索跨端开发的无限可能!

欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐