Appearance
第17章:拓展学习方向
17.1 Electron 高级API应用
托盘菜单进阶
托盘菜单是桌面应用的重要组成部分,通过 Electron 的 Tray 模块可以实现丰富的托盘功能:
javascript
const { app, Tray, Menu } = require('electron');
const path = require('path');
let tray = null;
app.whenReady().then(() => {
// 创建托盘图标
tray = new Tray(path.join(__dirname, 'tray-icon.png'));
// 创建托盘菜单
const contextMenu = Menu.buildFromTemplate([
{ label: '显示主窗口', click: () => mainWindow.show() },
{ label: '最小化到托盘', click: () => mainWindow.hide() },
{ type: 'separator' },
{
label: '设置',
submenu: [
{ label: '常规设置', click: () => openSettings('general') },
{ label: '外观设置', click: () => openSettings('appearance') },
{ label: '高级设置', click: () => openSettings('advanced') }
]
},
{ type: 'separator' },
{ label: '退出', click: () => app.quit() }
]);
// 设置托盘菜单
tray.setContextMenu(contextMenu);
// 托盘图标点击事件
tray.on('click', () => {
if (mainWindow.isVisible()) {
mainWindow.hide();
} else {
mainWindow.show();
}
});
// 托盘图标鼠标悬停提示
tray.setToolTip('我的 Electron 应用');
});系统对话框定制
Electron 的 dialog 模块可以创建自定义的系统对话框:
javascript
const { dialog } = require('electron');
// 自定义消息框
dialog.showMessageBox({
type: 'question',
title: '确认操作',
message: '你确定要执行此操作吗?',
detail: '此操作将删除所有本地数据,无法恢复。',
buttons: ['取消', '确认'],
cancelId: 0,
defaultId: 1,
icon: path.join(__dirname, 'warning-icon.png')
}).then((result) => {
if (result.response === 1) {
// 用户点击了确认
deleteLocalData();
}
});
// 自定义文件选择对话框
dialog.showOpenDialog({
title: '选择文件',
defaultPath: app.getPath('documents'),
filters: [
{ name: '文本文件', extensions: ['txt', 'md'] },
{ name: '所有文件', extensions: ['*'] }
],
properties: ['openFile', 'multiSelections']
}).then((result) => {
if (!result.canceled) {
const selectedFiles = result.filePaths;
processFiles(selectedFiles);
}
});17.2 桌面应用性能优化
深度优化主进程
避免阻塞主进程
- 使用
setTimeout或setImmediate处理耗时操作 - 对于复杂计算,考虑使用 Worker 线程
- 批量处理 IPC 消息,减少通信开销
- 使用
内存管理
- 及时释放不再使用的资源
- 避免内存泄漏,特别是事件监听器
- 使用 Chrome DevTools 分析内存使用情况
启动性能优化
- 延迟加载非关键模块
- 优化应用启动流程
- 使用
ready-to-show事件减少启动闪烁
优化渲染进程
渲染性能
- 使用 CSS hardware acceleration
- 避免大量 DOM 操作
- 使用虚拟列表处理长列表
脚本优化
- 减少主线程 JavaScript 执行时间
- 使用 Web Workers 处理复杂计算
- 优化渲染进程与主进程的通信
资源加载优化
- 压缩和合并静态资源
- 使用适当的图片格式和大小
- 实现资源预加载策略
17.3 企业级Electron应用开发
大型项目架构
模块化设计
- 将应用拆分为多个功能模块
- 使用依赖注入管理模块间依赖
- 采用清晰的目录结构
状态管理
- 使用 Redux 或 MobX 管理应用状态
- 实现状态持久化
- 处理多窗口状态同步
构建流程
- 使用 Webpack 或 Vite 构建前端资源
- 实现自动化测试
- 配置 CI/CD 流程
团队协作
代码规范
- 使用 ESLint 和 Prettier 统一代码风格
- 制定团队代码规范文档
- 使用 Git 分支策略管理代码
文档管理
- 维护项目架构文档
- 编写 API 文档
- 记录开发决策和技术债务
测试策略
- 单元测试
- 集成测试
- 端到端测试
17.4 Electron 与原生应用集成
调用C++/Python脚本
- 使用 Node.js 子进程
javascript
const { spawn } = require('child_process');
// 调用 Python 脚本
const pythonProcess = spawn('python', ['script.py', 'arg1', 'arg2']);
pythonProcess.stdout.on('data', (data) => {
console.log(`Python 输出: ${data}`);
});
pythonProcess.stderr.on('data', (data) => {
console.error(`Python 错误: ${data}`);
});
pythonProcess.on('close', (code) => {
console.log(`Python 进程退出,代码: ${code}`);
});- 使用 Node.js C++ 扩展
对于性能要求高的场景,可以使用 Node.js C++ 扩展:
cpp
// addon.cc
#include <node.h>
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(String::NewFromUtf8(
isolate, "世界,你好!").ToLocalChecked());
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "hello", Method);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
}17.5 跨平台适配进阶
不同系统差异处理
- 平台检测
javascript
const { platform } = require('os');
// 检测当前平台
const currentPlatform = platform();
if (currentPlatform === 'win32') {
// Windows 特定代码
} else if (currentPlatform === 'darwin') {
// macOS 特定代码
} else if (currentPlatform === 'linux') {
// Linux 特定代码
}- 路径处理
javascript
const path = require('path');
// 使用 path 模块处理跨平台路径
const appDataPath = path.join(app.getPath('appData'), 'MyApp');- 系统托盘差异
- Windows: 托盘图标显示在任务栏通知区域
- macOS: 托盘图标显示在菜单栏右侧
- Linux: 取决于桌面环境,通常显示在系统托盘区域
- 窗口行为差异
- Windows: 支持最小化到托盘
- macOS: 支持全屏模式和 Spaces
- Linux: 窗口管理器行为可能有所不同
- 文件系统差异
- Windows: 使用反斜杠作为路径分隔符
- macOS/Linux: 使用正斜杠作为路径分隔符
- 权限系统和文件访问控制有所不同
