前言
鸿蒙 OS(HarmonyOS)凭借 “一次开发、多端部署” 的分布式架构优势,正在快速构建起完善的生态体系。而 Electron 作为前端跨端开发的经典框架,以 HTML/CSS/JS 技术栈为核心,让开发者能轻松打造跨 Windows、macOS、Linux 的桌面应用。对于前端开发者来说,无需学习 ArkTS 鸿蒙原生开发语言,只需借助 Electron 的 Linux 版本与鸿蒙的 Linux 兼容层,就能快速开发适配鸿蒙系统的应用 —— 这是切入鸿蒙生态的 “捷径”,也是兼顾开发效率与生态布局的最优解。
本文将从原理到实战,手把手教你开发一个适配鸿蒙系统的 Electron 应用,包含完整代码案例、鸿蒙适配要点、调试部署全流程,即使是鸿蒙生态新手也能快速上手。
一、核心逻辑:鸿蒙与 Electron 为何能 “无缝结合”?
在动手开发前,我们首先要理解两者的适配底层逻辑,避免踩坑。
1. 底层兼容基础
鸿蒙 OS 3.0 及以上版本内置了Linux 容器和兼容层技术,能够直接运行 Linux 原生应用。而 Electron 的本质是 “Chromium 内核 + Node.js 运行时” 的组合体,其 Linux 版本的应用包,可通过鸿蒙的 Linux 兼容层直接运行,无需修改 Electron 的核心源码。
2. 关键适配要点
- 架构匹配:鸿蒙 PC、平板等设备主流为 x64 架构,需将 Electron 应用打包为 Linux-x64 版本;
- 权限与模式:鸿蒙设备需开启开发者模式,同时启用 USB 调试、Linux 环境兼容、未知来源应用安装权限;
- 通信配置:关闭 Electron 的上下文隔离(
contextIsolation: false),确保 Node.js API 在鸿蒙环境中正常调用; - 视觉适配:遵循鸿蒙的扁平化设计风格,优化窗口样式、交互逻辑,贴合鸿蒙系统的视觉体验。
二、环境搭建:极简配置步骤
1. 必备环境清单
| 工具 / 环境 | 版本要求 | 作用说明 |
|---|---|---|
| 鸿蒙 OS | 3.0+(PC / 平板 / 手机) | 应用运行的目标设备 |
| 开发机系统 | Windows 10+/macOS 12+ | 编写代码与打包应用 |
| Node.js | 16.x LTS / 18.x LTS | Electron 的运行依赖 |
| Electron | 22.x(稳定版) | 跨端应用开发框架(兼容性最优) |
| electron-packager | 最新版 | 打包 Electron 应用为系统可执行文件 |
| VS Code | 任意稳定版 | 代码编辑(推荐安装 Electron 插件) |
2. 环境安装与验证
(1)安装 Node.js 与 Electron
首先下载 Node.js 16.x LTS 版本(官网地址),安装时勾选 “Add to PATH”。安装完成后,在终端执行以下命令验证:
bash
运行
node -v # 输出v16.20.2即为成功 npm -v # 输出对应版本号即为成功接着全局安装 Electron 与打包工具:
bash
运行
npm install -g electron@22.3.2 electron-packager验证 Electron 安装:
bash
运行
electron -v # 输出v22.3.2即为成功(2)鸿蒙设备配置
- 打开鸿蒙设备,进入「设置」→「系统和更新」→「关于手机 / 平板 / PC」;
- 连续点击「版本号」7 次,激活开发者模式;
- 返回「系统和更新」,进入「开发者选项」,开启:
- USB 调试
- 允许安装未知来源的应用
- Linux 环境兼容(部分设备在「开发人员选项」的高级设置中)
三、实战开发:鸿蒙 Electron 应用(待办工具)
本节将开发一个鸿蒙适配的 Electron 待办事项工具,支持待办添加、删除、状态切换功能,代码结构清晰,可直接运行。
1. 项目初始化
bash
运行
# 1. 创建项目目录 mkdir harmony-electron-todo cd harmony-electron-todo # 2. 初始化package.json npm init -y # 3. 安装依赖(electron + 热重载工具nodemon) npm install electron@22.3.2 --save npm install nodemon --save-dev2. 配置 package.json
修改package.json文件,添加入口文件、启动与打包脚本:
json
{ "name": "harmony-electron-todo", "version": "1.0.0", "main": "main.js", "scripts": { "start": "nodemon --exec electron .", "package": "electron-packager . harmony-todo --platform=linux --arch=x64 --out=dist" }, "devDependencies": { "nodemon": "^3.1.0" }, "dependencies": { "electron": "^22.3.2" } }3. 核心代码实现
(1)主进程文件:main.js(窗口管理 + IPC 通信)
主进程是 Electron 应用的核心,负责窗口创建、系统交互与渲染进程通信:
javascript
运行
const { app, BrowserWindow, ipcMain } = require('electron'); const path = require('path'); const fs = require('fs'); // 定义待办数据存储路径(鸿蒙设备的用户目录) const TODO_FILE_PATH = path.join(app.getPath('userData'), 'todo-list.json'); let mainWindow; // 读取待办数据 function readTodoList() { try { // 若文件不存在,创建空数组文件 if (!fs.existsSync(TODO_FILE_PATH)) { fs.writeFileSync(TODO_FILE_PATH, JSON.stringify([]), 'utf8'); return []; } const data = fs.readFileSync(TODO_FILE_PATH, 'utf8'); return JSON.parse(data); } catch (error) { console.error('读取待办数据失败:', error); return []; } } // 写入待办数据 function writeTodoList(todoList) { try { fs.writeFileSync(TODO_FILE_PATH, JSON.stringify(todoList, null, 2), 'utf8'); return true; } catch (error) { console.error('写入待办数据失败:', error); return false; } } // 创建应用窗口 function createWindow() { mainWindow = new BrowserWindow({ width: 600, height: 800, title: '鸿蒙待办工具', webPreferences: { nodeIntegration: true, contextIsolation: false, // 鸿蒙兼容必需:关闭上下文隔离 preload: path.join(__dirname, 'preload.js') }, // 鸿蒙样式适配:去除默认边框,采用扁平化设计 frame: false, titleBarStyle: 'hidden' }); // 加载渲染进程页面 mainWindow.loadFile('index.html'); // 打开开发者工具(调试用,发布时可注释) mainWindow.webContents.openDevTools(); // 窗口关闭事件 mainWindow.on('closed', () => { mainWindow = null; }); } // IPC通信:监听获取待办列表请求 ipcMain.on('get-todo-list', (event) => { const todoList = readTodoList(); event.reply('todo-list-reply', todoList); }); // IPC通信:监听添加待办请求 ipcMain.on('add-todo', (event, todoText) => { const todoList = readTodoList(); const newTodo = { id: Date.now().toString(), text: todoText, completed: false }; todoList.push(newTodo); const isSuccess = writeTodoList(todoList); event.reply('add-todo-reply', { isSuccess, todo: newTodo }); }); // IPC通信:监听切换待办状态请求 ipcMain.on('toggle-todo', (event, todoId) => { const todoList = readTodoList(); const todo = todoList.find(item => item.id === todoId); if (todo) { todo.completed = !todo.completed; writeTodoList(todoList); event.reply('toggle-todo-reply', todoList); } }); // IPC通信:监听删除待办请求 ipcMain.on('delete-todo', (event, todoId) => { let todoList = readTodoList(); todoList = todoList.filter(item => item.id !== todoId); writeTodoList(todoList); event.reply('delete-todo-reply', todoList); }); // IPC通信:监听窗口控制请求(关闭/最小化) ipcMain.on('window-control', (event, action) => { switch (action) { case 'close': mainWindow.close(); break; case 'minimize': mainWindow.minimize(); break; } }); // 应用就绪后创建窗口 app.whenReady().then(createWindow); // 关闭所有窗口时退出应用(Windows/Linux) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } }); // macOS:激活应用时重建窗口 app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } });(2)预加载脚本:preload.js(安全通信桥接)
预加载脚本作为渲染进程与主进程的安全桥梁,避免渲染进程直接访问 Node.js API 带来的安全风险:
javascript
运行
const { ipcRenderer, contextBridge } = require('electron'); // 向渲染进程暴露安全的API contextBridge.exposeInMainWorld('electronAPI', { // 获取待办列表 getTodoList: () => { return new Promise((resolve) => { ipcRenderer.send('get-todo-list'); ipcRenderer.once('todo-list-reply', (event, data) => { resolve(data); }); }); }, // 添加待办 addTodo: (text) => { return new Promise((resolve) => { ipcRenderer.send('add-todo', text); ipcRenderer.once('add-todo-reply', (event, data) => { resolve(data); }); }); }, // 切换待办状态 toggleTodo: (id) => { return new Promise((resolve) => { ipcRenderer.send('toggle-todo', id); ipcRenderer.once('toggle-todo-reply', (event, data) => { resolve(data); }); }); }, // 删除待办 deleteTodo: (id) => { return new Promise((resolve) => { ipcRenderer.send('delete-todo', id); ipcRenderer.once('delete-todo-reply', (event, data) => { resolve(data); }); }); }, // 窗口控制 windowControl: (action) => { ipcRenderer.send('window-control', action); } });(3)渲染进程:index.html(UI 界面 + 交互逻辑)
渲染进程负责页面展示与用户交互,采用鸿蒙扁平化设计风格,适配鸿蒙系统的视觉体验:
html
预览
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>鸿蒙待办工具</title> <style> /* 鸿蒙扁平化设计风格适配 */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: "HarmonyOS Sans SC", "Microsoft YaHei", sans-serif; } body { background-color: #f5f5f7; color: #1d1d1f; } /* 窗口头部(鸿蒙风格标题栏) */ .window-header { display: flex; justify-content: space-between; align-items: center; padding: 12px 20px; background-color: #ffffff; border-bottom: 1px solid #e5e5e7; } .window-title { font-size: 18px; font-weight: 500; } .control-buttons { display: flex; gap: 12px; } .control-btn { width: 32px; height: 32px; border: none; background: transparent; cursor: pointer; border-radius: 50%; transition: background-color 0.2s ease; font-size: 16px; } .control-btn:hover { background-color: #f0f0f2; } /* 待办添加区域 */ .todo-add { padding: 20px; background-color: #ffffff; margin: 20px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } .todo-input { width: 100%; padding: 12px 16px; border: 1px solid #e5e5e7; border-radius: 8px; font-size: 16px; margin-bottom: 12px; outline: none; } .todo-input:focus { border-color: #007aff; } .add-btn { padding: 12px 24px; background-color: #007aff; color: #ffffff; border: none; border-radius: 8px; font-size: 16px; cursor: pointer; transition: background-color 0.2s ease; } .add-btn:hover { background-color: #0066cc; } /* 待办列表区域 */ .todo-list { margin: 0 20px; } .todo-item { display: flex; align-items: center; padding: 16px; background-color: #ffffff; border-radius: 12px; margin-bottom: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } .todo-checkbox { width: 20px; height: 20px; margin-right: 16px; cursor: pointer; } .todo-text { flex: 1; font-size: 16px; } .todo-text.completed { text-decoration: line-through; color: #86868b; } .delete-btn { padding: 8px 12px; background-color: #ff3b30; color: #ffffff; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.2s ease; } .delete-btn:hover { background-color: #cc2e25; } /* 空状态提示 */ .empty-tip { padding: 40px; text-align: center; color: #86868b; font-size: 16px; } </style> </head> <body> <!-- 窗口头部 --> <div class="window-header"> <div class="window-title">鸿蒙待办工具</div> <div class="control-buttons"> <button class="control-btn" onclick="window.electronAPI.windowControl('minimize')">—</button> <button class="control-btn" onclick="window.electronAPI.windowControl('close')">✕</button> </div> </div> <!-- 待办添加区域 --> <div class="todo-add"> <input type="text" class="todo-input" id="todoInput" placeholder="请输入待办事项..."> <button class="add-btn" onclick="addTodo()">添加待办</button> </div> <!-- 待办列表区域 --> <div class="todo-list" id="todoList"> <div class="empty-tip">暂无待办事项,添加一个吧!</div> </div> <script> // 页面加载完成后,加载待办列表 window.addEventListener('DOMContentLoaded', loadTodoList); // 加载待办列表 async function loadTodoList() { const todoList = await window.electronAPI.getTodoList(); renderTodoList(todoList); } // 渲染待办列表 function renderTodoList(todoList) { const todoListEl = document.getElementById('todoList'); if (todoList.length === 0) { todoListEl.innerHTML = '<div class="empty-tip">暂无待办事项,添加一个吧!</div>'; return; } let html = ''; todoList.forEach(todo => { html += ` <div class="todo-item"> <input type="checkbox" class="todo-checkbox" ${todo.completed ? 'checked' : ''} onchange="toggleTodo('${todo.id}')"> <div class="todo-text ${todo.completed ? 'completed' : ''}">${todo.text}</div> <button class="delete-btn" onclick="deleteTodo('${todo.id}')">删除</button> </div> `; }); todoListEl.innerHTML = html; } // 添加待办 async function addTodo() { const inputEl = document.getElementById('todoInput'); const text = inputEl.value.trim(); if (!text) { alert('请输入待办事项内容!'); return; } const result = await window.electronAPI.addTodo(text); if (result.isSuccess) { inputEl.value = ''; loadTodoList(); // 重新加载列表 } else { alert('添加待办失败,请重试!'); } } // 切换待办状态 async function toggleTodo(todoId) { const todoList = await window.electronAPI.toggleTodo(todoId); renderTodoList(todoList); } // 删除待办 async function deleteTodo(todoId) { if (confirm('确定要删除该待办吗?')) { const todoList = await window.electronAPI.deleteTodo(todoId); renderTodoList(todoList); } } </script> </body> </html>4. 代码核心说明
- 主进程(main.js):负责窗口创建、待办数据的本地存储(使用 Node.js fs 模块)、与渲染进程的 IPC 通信,同时配置鸿蒙兼容的关键参数(关闭上下文隔离);
- 预加载脚本(preload.js):通过
contextBridge暴露安全的 API 接口,实现渲染进程与主进程的安全通信; - 渲染进程(index.html):采用鸿蒙扁平化设计风格,实现待办的添加、删除、状态切换交互,通过暴露的
electronAPI调用主进程方法; - 鸿蒙适配亮点:窗口样式去除默认边框、打包为 Linux-x64 版本、数据存储到鸿蒙设备的用户目录(
app.getPath('userData'))。
四、调试与部署:鸿蒙设备运行应用
1. 本地调试(开发机预览)
在项目根目录执行以下命令,启动热重载开发模式:
bash
运行
npm start启动后会自动打开应用窗口,可调试待办功能与 UI 样式,修改代码后无需重启应用,提升开发效率。
2. 鸿蒙设备部署
(1)打包 Linux-x64 版本
执行打包命令,生成鸿蒙设备可运行的应用包:
bash
运行
npm run package打包完成后,项目根目录会生成dist/harmony-todo-linux-x64文件夹,包含可执行文件与依赖资源。
(2)传输应用到鸿蒙设备
- 方式 1:USB 传输:用 USB 数据线连接开发机与鸿蒙设备,将
harmony-todo-linux-x64文件夹拷贝到鸿蒙设备的任意目录(如 “文档” 文件夹); - 方式 2:网络共享:开发机开启文件夹共享,鸿蒙设备通过 “文件管理” 应用访问共享文件夹,下载应用包。
(3)鸿蒙设备运行应用
- 打开鸿蒙设备的 “终端” 应用(开发者模式下可在系统搜索中找到);
- 进入应用目录(以拷贝到 “文档” 为例):
bash
运行
cd /home/user/Documents/harmony-todo-linux-x64 - 赋予应用执行权限:
bash
运行
chmod +x harmony-todo - 启动应用:
bash
运行
./harmony-todo
启动成功后,鸿蒙设备上会显示待办工具窗口,可正常使用所有功能,数据会持久化存储在设备中。
3. 常见问题排查
| 问题现象 | 排查方向与解决方案 |
|---|---|
| 应用无法启动,提示 “权限不足” | 执行chmod +x harmony-todo赋予执行权限 |
| 启动后白屏无内容 | 检查 main.js 中contextIsolation: false是否配置 |
| 待办数据无法保存 | 确认鸿蒙设备的用户目录有读写权限(开发者模式下默认开启) |
| 应用闪退 | 确认鸿蒙 OS 版本≥3.0,低版本不支持 Linux 兼容层 |
五、进阶优化:鸿蒙特性深度适配
1. 调用鸿蒙系统通知
通过electron-harmony-adapter插件可调用鸿蒙原生通知功能,实现待办提醒:
bash
运行
# 安装插件 npm install electron-harmony-adapter --save在 main.js 的createWindow函数中添加通知逻辑:
javascript
运行
const { HarmonyAdapter } = require('electron-harmony-adapter'); const path = require('path'); function createWindow() { // ... 原有窗口配置 ... const harmonyAdapter = new HarmonyAdapter(mainWindow); // 监听添加待办成功事件,发送鸿蒙通知 ipcMain.on('todo-added', () => { harmonyAdapter.showNotification({ title: '鸿蒙待办工具', body: '新的待办事项已添加!', icon: path.join(__dirname, 'todo-icon.png') }); }); }2. 适配鸿蒙深色模式
在 index.html 中添加深色模式检测与样式适配:
javascript
运行
// 深色模式适配函数 function adaptDarkMode() { const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; if (isDark) { document.body.classList.add('dark-mode'); // 设置CSS变量 document.documentElement.style.setProperty('--bg-color', '#1a1a1a'); document.documentElement.style.setProperty('--card-bg', '#2c2c2e'); document.documentElement.style.setProperty('--text-color', '#f5f5f7'); } else { document.body.classList.remove('dark-mode'); document.documentElement.style.setProperty('--bg-color', '#f5f5f7'); document.documentElement.style.setProperty('--card-bg', '#ffffff'); document.documentElement.style.setProperty('--text-color', '#1d1d1f'); } } // 初始化适配+监听模式切换 adaptDarkMode(); window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', adaptDarkMode);添加深色模式 CSS 样式:
css
:root { --bg-color: #f5f5f7; --card-bg: #ffffff; --text-color: #1d1d1f; } .dark-mode { background-color: var(--bg-color); color: var(--text-color); } .dark-mode .window-header, .dark-mode .todo-add, .dark-mode .todo-item { background-color: var(--card-bg); border-color: #3a3a3c; } .dark-mode .todo-input { background-color: #3a3a3c; border-color: #4a4a4c; color: var(--text-color); }六、总结与展望
通过本文的实战案例,我们实现了一个适配鸿蒙系统的 Electron 待办应用,核心收获如下:
- 理解了鸿蒙与 Electron 的适配原理 —— 基于 Linux 兼容层实现无缝运行,无需学习 ArkTS;
- 掌握了鸿蒙 Electron 应用的开发流程:环境搭建→代码开发→调试→打包部署;
- 学会了鸿蒙适配的关键要点:架构匹配、权限配置、样式适配、通信安全。
未来,随着鸿蒙 OS 对 Linux 生态的进一步优化,Electron 与鸿蒙的结合将覆盖更多场景:如办公软件、开发工具、多媒体应用等。对于前端开发者而言,这是一个低门槛切入国产操作系统生态的绝佳机会,既能复用现有技术栈,又能抢占鸿蒙生态的发展红利。
如果本文对你有帮助,欢迎点赞、收藏、关注!后续将分享 Electron+Vue/React 适配鸿蒙、鸿蒙 HAP 包打包发布等进阶内容,敬请期待~
文末标签
#鸿蒙OS #Electron #跨端开发 #前端开发 #鸿蒙应用开发 #代码实战 #桌面应用
欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。