随着鸿蒙生态对桌面应用场景的覆盖,鸿蒙 Electron 已从 “基础适配” 走向 “深度融合”。本文聚焦进阶开发场景,通过多窗口协同、系统通知集成、文件关联等实战案例,结合鸿蒙特有能力(如原子化服务调用),帮你打造更贴近系统体验的跨端应用。全文配套可复用源码与官方调试工具链接,解决进阶开发中的高频问题。

一、鸿蒙 Electron 进阶开发核心场景

相比基础开发,进阶场景更注重 “应用与系统的融合” 和 “复杂交互的实现”,需重点掌握三类核心能力。

1.1 进阶开发的核心方向

  • 多窗口协同:实现主窗口、弹窗、子窗口的通信与状态同步(如 “设置窗口修改配置后,主窗口实时更新”)。
  • 系统级交互:调用鸿蒙系统能力,如系统通知、文件关联、快捷键注册,让应用更贴近原生体验。
  • 鸿蒙能力深度集成:结合原子化服务、分布式任务调度,突破传统 Electron 的功能边界。

官方进阶文档参考:鸿蒙 Electron 系统能力调用指南

1.2 进阶开发的前置准备

  1. 环境升级:确保 DevEco Studio 版本≥5.0 Release,鸿蒙 SDK 升级至 API Version 11(支持更多系统能力)。
  2. 工具补充:安装鸿蒙系统能力调试工具harmony-tools,用于测试系统 API 调用:

运行

# 全局安装调试工具
npm install -g @harmonyos/electron-tools
# 验证安装
harmony-tools -v
# 输出“1.1.0”即成功
  1. 权限配置:在harmony.json中提前添加进阶场景所需权限(如系统通知、文件读写):
"permissions": [
  "ohos.permission.POST_NOTIFICATIONS", // 系统通知权限
  "ohos.permission.READ_USER_STORAGE",   // 读取存储权限
  "ohos.permission.WRITE_USER_STORAGE",  // 写入存储权限
  "ohos.permission.REGISTER_APP_SCHEME"  // 应用Scheme注册权限
]

二、实战 1:多窗口协同与状态同步

多窗口是桌面应用的常见形态,鸿蒙 Electron 中需解决 “窗口通信”“状态共享”“窗口生命周期管理” 三个核心问题。

2.1 场景需求

开发一个 “文档编辑器” 应用,包含:

  • 主窗口(文档编辑)
  • 设置窗口(修改字体、主题)
  • 预览窗口(实时预览文档效果)要求:设置窗口修改配置后,主窗口与预览窗口同步更新;预览窗口关闭时不影响主窗口运行。

2.2 核心实现代码

2.2.1 主进程:窗口创建与通信管理(main/index.js)

运行

const { app, BrowserWindow, ipcMain, dialog } = require('@harmonyos/electron');
const path = require('path');
// 存储窗口实例(便于管理)
let mainWindow, settingWindow, previewWindow;
// 共享状态(配置信息)
let appConfig = {
  fontSize: 16,
  theme: 'light',
  previewEnabled: true
};

// 创建主窗口
function createMainWindow() {
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
      preload: path.join(__dirname, 'preload.js') // 预加载脚本(共享状态)
    },
    harmonyOptions: {
      supportedDevices: ['pc', 'tablet']
    }
  });
  mainWindow.loadFile('renderer/main.html');

  // 主窗口菜单:添加“打开设置”“打开预览”选项
  mainWindow.webContents.on('did-finish-load', () => {
    mainWindow.webContents.send('config-update', appConfig); // 初始发送配置
  });

  // 监听“打开设置窗口”请求
  ipcMain.handle('open-setting-window', () => {
    if (settingWindow) {
      settingWindow.show(); // 已存在则显示,避免重复创建
      return;
    }
    settingWindow = new BrowserWindow({
      width: 600,
      height: 400,
      parent: mainWindow, // 设置父窗口(主窗口关闭时,子窗口也关闭)
      modal: false,       // 非模态(可同时操作主窗口)
      webPreferences: {
        nodeIntegration: true,
        contextIsolation: false
      }
    });
    settingWindow.loadFile('renderer/setting.html');
    // 设置窗口关闭时清空实例
    settingWindow.on('closed', () => {
      settingWindow = null;
    });
  });

  // 监听“打开预览窗口”请求
  ipcMain.handle('open-preview-window', (event, content) => {
    if (!appConfig.previewEnabled) {
      dialog.showErrorBox('预览未启用', '请在设置中开启预览功能');
      return;
    }
    if (previewWindow) {
      previewWindow.webContents.send('preview-update', content);
      previewWindow.show();
      return;
    }
    previewWindow = new BrowserWindow({
      width: 800,
      height: 600,
      parent: mainWindow,
      webPreferences: {
        nodeIntegration: true,
        contextIsolation: false
      }
    });
    previewWindow.loadFile('renderer/preview.html');
    // 初始发送文档内容
    previewWindow.webContents.on('did-finish-load', () => {
      previewWindow.webContents.send('preview-update', content);
      previewWindow.webContents.send('config-update', appConfig);
    });
    // 预览窗口关闭时清空实例
    previewWindow.on('closed', () => {
      previewWindow = null;
    });
  });

  // 监听“配置修改”请求(来自设置窗口)
  ipcMain.handle('update-config', (event, newConfig) => {
    appConfig = { ...appConfig, ...newConfig };
    // 同步更新主窗口与预览窗口的配置
    if (mainWindow) mainWindow.webContents.send('config-update', appConfig);
    if (previewWindow) previewWindow.webContents.send('config-update', appConfig);
    return { success: true };
  });
}

app.whenReady().then(createMainWindow);

// 关闭所有窗口时退出应用
app.on('window-all-closed', () => {
  mainWindow = null;
  settingWindow = null;
  previewWindow = null;
  if (process.platform !== 'darwin') app.quit();
});

2.2.2 预加载脚本:共享状态与安全通信(main/preload.js)
// 预加载脚本:向渲染进程暴露安全的API(避免直接暴露nodeIntegration)
const { contextBridge, ipcRenderer } = require('@harmonyos/electron');

contextBridge.exposeInMainWorld('appApi', {
  // 获取应用配置
  getConfig: () => ipcRenderer.invoke('get-config'),
  // 打开设置窗口
  openSettingWindow: () => ipcRenderer.invoke('open-setting-window'),
  // 打开预览窗口
  openPreviewWindow: (content) => ipcRenderer.invoke('open-preview-window', content),
  // 监听配置更新
  onConfigUpdate: (callback) => ipcRenderer.on('config-update', (event, config) => callback(config))
});
2.2.3 主窗口渲染逻辑(renderer/js/main.js)

运行

// 主窗口:文档编辑与窗口控制
const { ipcRenderer } = require('@harmonyos/electron');
const editor = document.getElementById('editor');
const openSettingBtn = document.getElementById('openSettingBtn');
const openPreviewBtn = document.getElementById('openPreviewBtn');

// 监听配置更新(来自主进程)
ipcRenderer.on('config-update', (event, config) => {
  // 应用字体大小与主题
  editor.style.fontSize = `${config.fontSize}px`;
  document.body.className = config.theme;
});

// 打开设置窗口
openSettingBtn.addEventListener('click', () => {
  ipcRenderer.invoke('open-setting-window');
});

// 打开预览窗口(传递当前文档内容)
openPreviewBtn.addEventListener('click', () => {
  ipcRenderer.invoke('open-preview-window', editor.value);
});

// 文档内容变化时,实时更新预览窗口(如果已打开)
editor.addEventListener('input', () => {
  if (previewWindow) {
    ipcRenderer.send('update-preview-content', editor.value);
  }
});
2.2.4 设置窗口渲染逻辑(renderer/js/setting.js)

运行

// 设置窗口:修改配置并同步
const { ipcRenderer } = require('@harmonyos/electron');
const fontSizeSelect = document.getElementById('fontSizeSelect');
const themeSelect = document.getElementById('themeSelect');
const previewSwitch = document.getElementById('previewSwitch');
const saveBtn = document.getElementById('saveBtn');

// 初始加载当前配置
ipcRenderer.on('config-update', (event, config) => {
  fontSizeSelect.value = config.fontSize;
  themeSelect.value = config.theme;
  previewSwitch.checked = config.previewEnabled;
});

// 保存配置并通知主进程
saveBtn.addEventListener('click', async () => {
  const newConfig = {
    fontSize: parseInt(fontSizeSelect.value),
    theme: themeSelect.value,
    previewEnabled: previewSwitch.checked
  };
  const result = await ipcRenderer.invoke('update-config', newConfig);
  if (result.success) {
    alert('配置保存成功');
    // 关闭设置窗口(可选)
    window.close();
  }
});

2.3 关键技术点总结

  1. 窗口父子关系:通过parent属性绑定子窗口(如设置窗口)到主窗口,主窗口关闭时子窗口自动关闭,避免内存泄漏。
  2. 状态共享:主进程维护全局appConfig,配置修改后通过webContents.send同步到所有窗口。
  3. 窗口复用:判断窗口实例是否存在,避免重复创建(如if (settingWindow) settingWindow.show())。

三、实战 2:系统级交互能力集成

让应用 “融入” 鸿蒙系统,需调用系统级 API,本节以 “系统通知”“文件关联”“全局快捷键” 三个高频场景为例。

3.1 场景 1:系统通知(任务完成时推送提醒)

3.1.1 主进程代码(main/notification.js)
const { Notification } = require('@harmonyos/electron');
const path = require('path');

// 发送系统通知
function sendSystemNotification(title, body) {
  // 检查通知权限(鸿蒙系统需手动授权)
  if (Notification.isSupported()) {
    const notification = new Notification({
      title: title,
      body: body,
      icon: path.join(__dirname, '../resources/icon.png'), // 通知图标
      harmonyOptions: {
        notificationType: 'reminder', // 通知类型(提醒型)
        importance: 1 // 重要性(1-3,3最高)
      }
    });

    // 监听通知点击事件(点击后激活主窗口)
    notification.on('click', () => {
      if (mainWindow) {
        mainWindow.show();
        mainWindow.focus();
      }
    });

    notification.show();
  }
}

// 示例:文档保存成功后发送通知
ipcMain.handle('save-document', async (event, content, path) => {
  // 模拟保存逻辑
  await fs.promises.writeFile(path, content);
  // 发送通知
  sendSystemNotification('文档保存成功', `文件已保存至:${path}`);
  return { success: true };
});
3.1.2 权限申请说明

鸿蒙系统中,POST_NOTIFICATIONS权限需用户手动授权,可在应用启动时触发授权弹窗:

// 主进程:应用就绪后请求通知权限
app.whenReady().then(async () => {
  const { notificationPermission } = require('@harmonyos/electron/system');
  const permission = await notificationPermission.request();
  console.log('通知权限状态:', permission); // granted/denied
});

3.2 场景 2:文件关联(双击鸿蒙系统中的.txt 文件,用当前应用打开)

3.2.1 配置文件关联(package.json)
"harmonyElectron": {
  "build": {
    "fileAssociations": [
      {
        "ext": ["txt", "md"], // 关联的文件后缀
        "name": "Text File",  // 文件类型名称
        "description": "Text and Markdown files",
        "icon": "resources/file-icons/{ext}.png" // 文件图标(按后缀区分)
      }
    ]
  }
}
3.2.2 主进程处理文件打开事件
// 主进程:监听文件打开事件(双击文件时触发)
app.on('open-file', (event, filePath) => {
  event.preventDefault(); // 阻止系统默认行为
  // 1. 如果应用未启动,先创建主窗口
  if (!mainWindow) {
    createMainWindow();
    // 窗口创建完成后读取文件
    mainWindow.on('ready-to-show', async () => {
      const content = await fs.promises.readFile(filePath, 'utf8');
      mainWindow.webContents.send('open-file', { path: filePath, content: content });
    });
  } else {
    // 2. 应用已启动,直接发送文件内容到主窗口
    fs.promises.readFile(filePath, 'utf8').then(content => {
      mainWindow.webContents.send('open-file', { path: filePath, content: content });
      mainWindow.show();
    });
  }
});
3.2.3 渲染进程接收文件内容

运行

// 渲染进程:接收并加载打开的文件
ipcRenderer.on('open-file', (event, { path, content }) => {
  document.getElementById('editor').value = content;
  document.title = `文档编辑器 - ${path}`; // 更新窗口标题为文件名
});

3.3 场景 3:全局快捷键(注册 “Ctrl+S” 保存、“Ctrl+P” 预览)

3.3.1 主进程注册全局快捷键
const { globalShortcut } = require('@harmonyos/electron');

// 应用就绪后注册快捷键
app.whenReady().then(() => {
  // 注册“Ctrl+S”保存快捷键
  const saveShortcut = globalShortcut.register('Ctrl+S', () => {
    if (mainWindow) {
      // 向主窗口发送保存指令
      mainWindow.webContents.send('trigger-save');
    }
  });

  // 注册“Ctrl+P”预览快捷键
  const previewShortcut = globalShortcut.register('Ctrl+P', () => {
    if (mainWindow) {
      // 先获取当前文档内容,再打开预览窗口
      mainWindow.webContents.executeJavaScript(`document.getElementById('editor').value`).then(content => {
        ipcRenderer.invoke('open-preview-window', content);
      });
    }
  });

  // 检查快捷键是否注册成功
  if (!saveShortcut) console.error('“Ctrl+S”快捷键注册失败(可能已被占用)');
  if (!previewShortcut) console.error('“Ctrl+P”快捷键注册失败(可能已被占用)');
});

// 应用退出时注销所有快捷键(避免残留)
app.on('will-quit', () => {
  globalShortcut.unregisterAll();
});
3.3.2 渲染进程响应快捷键指令

运行

// 渲染进程:监听“触发保存”指令
ipcRenderer.on('trigger-save', () => {
  // 执行保存逻辑(如调用保存接口)
  document.getElementById('saveBtn').click();
});

四、实战 3:鸿蒙原子化服务调用(进阶融合)

原子化服务是鸿蒙的核心特性之一,鸿蒙 Electron 可通过harmony:service API 调用系统或第三方原子化服务(如 “文件压缩”“OCR 识别”)。

4.1 场景需求

在文档编辑器中添加 “OCR 识别” 功能:用户上传图片,调用鸿蒙系统的 OCR 原子化服务,将图片中的文字提取到编辑器中。

4.2 核心实现代码

4.2.1 主进程调用原子化服务(main/ocr-service.js)
const { ipcMain, dialog } = require('@harmonyos/electron');
const { service } = require('@harmonyos/electron/harmony');

// 调用OCR原子化服务
async function callOcrService(imagePath) {
  try {
    // 1. 检查OCR服务是否可用
    const isAvailable = await service.checkAvailability('ohos.service.ocr');
    if (!isAvailable) {
      throw new Error('OCR原子化服务未安装,请在鸿蒙应用市场搜索“OCR识别”并安装');
    }

    // 2. 调用OCR服务(传递图片路径)
    const result = await service.invoke('ohos.service.ocr', 'recognizeText', {
      imagePath: imagePath,
      language: 'zh-CN' // 识别语言
    });

    // 3. 返回识别结果
    return result.text;
  } catch (error) {
    console.error('OCR服务调用失败:', error);
    throw error;
  }
}

// 监听“OCR识别”请求(来自渲染进程)
ipcMain.handle('ocr-recognize', async () => {
  // 1. 打开文件选择器,让用户选择图片
  const { filePaths } = await dialog.showOpenDialog({
    title: '选择图片文件',
    filters: [
      { name: 'Image Files', extensions: ['png', 'jpg', 'jpeg'] }
    ],
    properties: ['openFile']
  });

  if (!filePaths || filePaths.length === 0) {
    throw new Error('未选择图片文件');
  }

  const imagePath = filePaths[0];
  // 2. 调用OCR服务
  const recognizedText = await callOcrService(imagePath);
  // 3. 返回识别结果到渲染进程
  return { success: true, text: recognizedText };
});
4.2.2 渲染进程触发 OCR 识别(renderer/js/ocr.js)

运行

const { ipcRenderer } = require('@harmonyos/electron');
const ocrBtn = document.getElementById('ocrBtn');
const editor = document.getElementById('editor');

ocrBtn.addEventListener('click', async () => {
  try {
    const result = await ipcRenderer.invoke('ocr-recognize');
    if (result.success) {
      // 将识别结果插入到编辑器光标位置
      const start = editor.selectionStart;
      const end = editor.selectionEnd;
      const currentContent = editor.value;
      editor.value = currentContent.substring(0, start) + result.text + currentContent.substring(end);
      alert('OCR识别成功,已插入文本');
    }
  } catch (error) {
    alert('OCR识别失败:' + error.message);
  }
});

4.3 关键说明

  1. 原子化服务依赖:调用第三方原子化服务前,需确保用户设备已安装该服务(可通过service.checkAvailability检查)。
  2. 服务参数格式:不同原子化服务的调用参数不同,需参考服务提供方的文档(如 OCR 服务的imagePath参数需为绝对路径)。
  3. 权限要求:调用原子化服务可能需要额外权限(如 “访问其他应用”),需在harmony.json中添加对应权限。

五、进阶开发避坑指南

进阶开发中,易遇到 “权限不足”“窗口内存泄漏”“服务调用失败” 等问题,以下为高频坑点及解决方案。

5.1 坑点 1:系统 API 调用提示 “权限不足”

  • 现象:调用系统通知、文件关联等 API 时,报错 “permission denied”。
  • 原因
    1. harmony.json中未配置对应权限。
    2. 部分权限(如通知、存储)需用户手动授权,未触发授权流程。
  • 解决方案
    1. 检查harmony.jsonpermissions字段,确保包含所需权限(参考本文 1.2 节)。
    2. 调用权限相关 API 时,先通过@harmonyos/electron/system模块请求授权:

    运行

    const { notificationPermission, storagePermission } = require('@harmonyos/electron/system');
    // 请求通知权限
    const notifyPerm = await notificationPermission.request();
    // 请求存储权限
    const storagePerm = await storagePermission.request();
    

5.2 坑点 2:窗口关闭后内存泄漏

  • 现象:多次打开并关闭子窗口后,应用内存占用持续升高。
  • 原因:未清空窗口实例,或窗口关闭时未注销事件监听。
  • 解决方案
    1. 窗口关闭时,将实例设为null(参考本文 2.2.1 节settingWindow.on('closed'))。
    2. 注销窗口相关的事件监听(如ipcMain.removeListener):

    运行

    settingWindow.on('closed', () => {
      ipcMain.removeListener('update-config', updateConfigHandler); // 注销事件
      settingWindow = null;
    });
    

5.3 坑点 3:原子化服务调用失败

  • 现象:调用service.invoke时,报错 “service not found” 或 “invalid parameters”。
  • 原因
    1. 服务未安装或服务 ID 错误。
    2. 传递的参数格式不符合服务要求。
  • 解决方案
    1. 通过service.checkAvailability(服务ID)确认服务是否可用,若不可用则提示用户安装。
    2. 查阅服务官方文档,确认参数格式(如图片路径需为绝对路径,且文件存在)。

六、鸿蒙 Electron 进阶资源汇总

6.1 官方进阶工具与文档

  1. 鸿蒙 Electron 系统能力调试工具https://developer.harmonyos.com/cn/develop/deveco-studio/tool/harmony-tools-0000001578624551
  2. 原子化服务调用手册https://developer.harmonyos.com/cn/docs/design/electron/service-invocation-0000001578624549
  3. 鸿蒙系统权限列表https://developer.harmonyos.com/cn/docs/design/electron/permission-list-0000001578624545

6.2 第三方进阶案例库

  1. 鸿蒙 Electron 多窗口示例https://github.com/harmonyos/electron-samples/tree/main/multi-window
  2. 系统交互功能示例https://github.com/harmonyos/electron-samples/tree/main/system-interaction

七、总结与未来方向

鸿蒙 Electron 的进阶开发,核心是 “从‘能用’到‘好用’” 的跨越 —— 通过多窗口协同提升复杂场景体验,通过系统级交互贴近原生感受,通过原子化服务扩展功能边界。本文的实战案例可直接复用于文档编辑、代码编辑器、办公软件等桌面应用场景。

未来,鸿蒙 Electron 将进一步深化与鸿蒙生态的融合,例如支持鸿蒙的 “多端 UI 自适应”(一套 UI 自动适配 PC、平板、手机)、“分布式文件系统”(跨设备访问文件)等能力。建议开发者持续关注官方更新,将新能力快速应用到实际项目中。

如果在进阶开发中遇到特定场景的问题,可通过鸿蒙开发者论坛的 “Electron 进阶” 板块提问,或参考本文提供的示例代码与工具链接排查问题。

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

Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐