示例一:企业 IM 桌面客户端(Electron)
项目名称
XX 企业即时通讯桌面端(Electron)
项目背景
公司内部沟通使用 Web 版 IM,但员工反馈 Web 版功能受限,体验差,需要桌面客户端。选择 Electron 复用现有 Web 技术栈,快速开发 Windows/Mac 双平台客户端。
业务场景
- 即时通讯:单聊、群聊、文件传输、屏幕共享
- 视频会议:多人音视频、屏幕演示、会议录制
- 组织架构:部门管理、员工通讯录、在线状态
- 工作协同:日程安排、任务管理、公告通知
- 文件管理:企业云盘、文件同步、版本管理
核心职责
1. 本地缓存 - 聊天记录
业务场景: 员工需要查询历史聊天记录、搜索文件
问题
- Web 版刷新后聊天记录丢失
- 查询历史消息需要请求服务器,慢
- 无法离线查看聊天记录
解决方案
- 使用 better-sqlite3 本地数据库存储
- 消息实时保存到本地
- 支持全文搜索、按时间筛选
- 媒体文件存储到用户文档目录
技术实现
// main.js (主进程)
const Database = require('better-sqlite3');
const path = require('path');
const { app } = require('electron');
class MessageDB {
constructor() {
const dbPath = path.join(app.getPath('userData'), 'messages.db');
this.db = new Database(dbPath);
this.init();
}
init() {
this.db.exec(`
CREATE TABLE IF NOT EXISTS messages (
id TEXT PRIMARY KEY,
conversation_id TEXT,
sender_id TEXT,
content TEXT,
timestamp INTEGER,
type TEXT,
media_path TEXT
);
CREATE INDEX IF NOT EXISTS idx_conversation ON messages(conversation_id);
CREATE INDEX IF NOT EXISTS idx_timestamp ON messages(timestamp);
`);
}
saveMessage(msg) {
const stmt = this.db.prepare(`
INSERT OR REPLACE INTO messages
(id, conversation_id, sender_id, content, timestamp, type, media_path)
VALUES (?, ?, ?, ?, ?, ?, ?)
`);
stmt.run(msg.id, msg.conversationId, msg.senderId, msg.content,
msg.timestamp, msg.type, msg.mediaPath);
}
getMessages(conversationId, limit = 50) {
const stmt = this.db.prepare(`
SELECT * FROM messages
WHERE conversation_id = ?
ORDER BY timestamp DESC
LIMIT ?
`);
return stmt.all(conversationId, limit);
}
searchMessages(keyword) {
const stmt = this.db.prepare(`
SELECT * FROM messages
WHERE content LIKE ?
ORDER BY timestamp DESC
`);
return stmt.all(`%${keyword}%`);
}
}
module.exports = new MessageDB();
项目成果
- 聊天记录加载时间从 2 秒降到 100ms
- 支持离线查看所有历史消息
- 全文搜索 1 万条消息只需 200ms
2. 启动优化 - 快速启动
业务场景: 员工上班打开客户端,需要快速进入工作状态
问题
- 冷启动需要 5 秒,白屏时间长
- 首次加载资源多,渲染慢
- 登录后还要加载大量数据
解决方案
- 窗口预创建,提前初始化主窗口
- 使用 v8-compile-cache 加速 JS 加载
- 资源本地化,减少网络请求
- 数据预加载,并行请求关键数据
技术实现
// main.js
require('v8-compile-cache');
const { app, BrowserWindow } = require('electron');
let mainWindow = null;
// 预创建窗口
app.on('ready', () => {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
show: false, // 先不显示
backgroundColor: '#fff',
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true
}
});
// 加载本地文件
mainWindow.loadFile('index.html');
// 首次渲染完成后显示
mainWindow.once('ready-to-show', () => {
mainWindow.show();
});
// 优化渲染进程
mainWindow.webContents.on('did-finish-load', () => {
mainWindow.webContents.send('preload-data');
});
});
// 预加载子窗口
function preloadWindows() {
const videoWindow = new BrowserWindow({
width: 800,
height: 600,
show: false
});
videoWindow.loadFile('video.html');
}
app.on('ready', () => {
setTimeout(preloadWindows, 3000);
});
项目成果
- 启动时间从 5 秒优化到 1.2 秒
- 窗口切换瞬间响应
- 登录到工作台 2 秒内完成
3. 离线存储 - 文件同步
业务场景: 员工发送的文件需要本地保存,支持离线访问
问题
- 文件每次都从服务器下载,浪费流量
- 大文件下载慢,影响使用
- 无法离线查看文件
解决方案
- 文件自动下载到本地目录
- 使用文件 hash 去重,节省空间
- 支持大文件断点续传
- 定期清理过期文件
技术实现
// file-manager.js
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { app } = require('electron');
class FileManager {
constructor() {
this.cacheDir = path.join(app.getPath('userData'), 'files');
if (!fs.existsSync(this.cacheDir)) {
fs.mkdirSync(this.cacheDir, { recursive: true });
}
}
async downloadFile(url, fileId) {
const filePath = path.join(this.cacheDir, fileId);
// 已存在直接返回
if (fs.existsSync(filePath)) {
return filePath;
}
// 下载文件
const response = await fetch(url);
const buffer = await response.arrayBuffer();
// 保存到本地
fs.writeFileSync(filePath, Buffer.from(buffer));
return filePath;
}
getFileHash(filePath) {
const buffer = fs.readFileSync(filePath);
return crypto.createHash('md5').update(buffer).digest('hex');
}
cleanOldFiles(days = 30) {
const files = fs.readdirSync(this.cacheDir);
const now = Date.now();
const maxAge = days * 24 * 60 * 60 * 1000;
files.forEach(file => {
const filePath = path.join(this.cacheDir, file);
const stats = fs.statSync(filePath);
if (now - stats.mtimeMs > maxAge) {
fs.unlinkSync(filePath);
}
});
}
getCacheSize() {
let totalSize = 0;
const files = fs.readdirSync(this.cacheDir);
files.forEach(file => {
const filePath = path.join(this.cacheDir, file);
const stats = fs.statSync(filePath);
totalSize += stats.size;
});
return totalSize;
}
}
module.exports = new FileManager();
项目成果
- 文件加载速度提升 90%
- 流量消耗减少 70%
- 支持离线查看所有文件
4. Native 能力 - 系统集成
业务场景: 新消息通知、截图、文件拖拽、托盘图标
问题
- Web 版无法实现系统级功能
- 需要调用 Windows/Mac 原生 API
- 双平台差异需要处理
解决方案
- 封装统一的 Native API
- 使用 Electron API 实现跨平台
- 平台差异通过 process.platform 判断
- 第三方库补充不支持的功能
技术实现
// native-api.js
const { Notification, clipboard, nativeImage, screen, desktopCapturer } = require('electron');
const screenshot = require('screenshot-desktop');
class NativeAPI {
// 显示通知
showNotification(title, body, onclick) {
const notification = new Notification({ title, body });
notification.onclick = onclick;
notification.show();
}
// 复制到剪贴板
copyToClipboard(text) {
clipboard.writeText(text);
}
// 截图
async takeScreenshot() {
try {
if (process.platform === 'darwin') {
// Mac 使用 screencapture
return await this._macScreenshot();
} else {
// Windows 使用 screenshot-desktop
const img = await screenshot({ format: 'png' });
return img;
}
} catch (error) {
console.error('截图失败:', error);
return null;
}
}
// 获取屏幕信息
getScreens() {
return screen.getAllDisplays();
}
// 文件拖拽
setupFileDrop(window) {
window.webContents.on('will-navigate', (event, url) => {
event.preventDefault();
});
window.webContents.on('drop-files', (event, files) => {
event.preventDefault();
window.webContents.send('files-dropped', files);
});
}
}
module.exports = new NativeAPI();
项目成果
- 支持系统通知、托盘、快捷键
- 截图功能使用率 80%
- 文件拖拽发送效率提升 60%
5. 崩溃监控 - 稳定性保障
业务场景: 客户端偶尔崩溃,影响员工工作
问题
- 崩溃后无日志,无法定位
- 不知道哪些用户遇到问题
- 修复缺少数据支持
解决方案
- 接入 electron-sentry 收集崩溃
- 主进程和渲染进程分别监控
- 记录用户操作路径
- 崩溃后自动重启
技术实现
// main.js
const { app, crashReporter } = require('electron');
const Sentry = require('@sentry/electron');
// 初始化 Sentry
Sentry.init({
dsn: 'https://xxx@sentry.io/xxx',
beforeSend(event) {
// 添加额外信息
event.user = {
id: global.userId,
username: global.username
};
return event;
}
});
// 崩溃报告
crashReporter.start({
productName: 'XX IM',
companyName: 'XX Company',
submitURL: 'https://crash.example.com/report',
uploadToServer: true
});
// 未捕获异常
process.on('uncaughtException', (error) => {
Sentry.captureException(error);
// 崩溃后重启
app.relaunch();
app.exit(0);
});
// 渲染进程崩溃
app.on('render-process-gone', (event, webContents, details) => {
Sentry.captureMessage('Renderer crashed', {
level: 'error',
extra: { details }
});
// 重新加载
if (details.reason !== 'clean-exit') {
webContents.reload();
}
});
项目成果
- 崩溃率从 5% 降到 0.5%
- 问题定位时间从 1 天缩短到 30 分钟
- 发现并修复 20+ 个稳定性问题
6. 自动更新 - 版本管理
业务场景: 客户端需要更新功能、修复 bug
问题
- 员工版本不一致,支持困难
- 手动下载安装繁琐
- 更新覆盖率低
解决方案
- 使用 electron-updater 自动更新
- 支持全量更新和增量更新
- 静默更新 + 强制更新
- 更新失败自动回滚
技术实现
// updater.js
const { autoUpdater } = require('electron-updater');
const { dialog } = require('electron');
class Updater {
constructor(mainWindow) {
this.mainWindow = mainWindow;
this.init();
}
init() {
// 设置更新服务器
autoUpdater.setFeedURL({
provider: 'generic',
url: 'https://update.example.com/releases'
});
// 检查更新
autoUpdater.on('checking-for-update', () => {
console.log('检查更新...');
});
// 发现新版本
autoUpdater.on('update-available', (info) => {
dialog.showMessageBox(this.mainWindow, {
type: 'info',
title: '发现新版本',
message: `版本 ${info.version} 可用`,
buttons: ['立即更新', '稍后']
}).then(result => {
if (result.response === 0) {
autoUpdater.downloadUpdate();
}
});
});
// 无可用更新
autoUpdater.on('update-not-available', () => {
console.log('已是最新版本');
});
// 下载进度
autoUpdater.on('download-progress', (progress) => {
this.mainWindow.webContents.send('download-progress', progress.percent);
});
// 下载完成
autoUpdater.on('update-downloaded', () => {
dialog.showMessageBox(this.mainWindow, {
type: 'info',
title: '更新完成',
message: '新版本已下载,是否立即重启?',
buttons: ['立即重启', '稍后']
}).then(result => {
if (result.response === 0) {
autoUpdater.quitAndInstall();
}
});
});
// 错误处理
autoUpdater.on('error', (error) => {
console.error('更新失败:', error);
});
}
checkForUpdates() {
autoUpdater.checkForUpdates();
}
}
module.exports = Updater;
项目成果
- 更新覆盖率从 60% 提升到 95%
- 版本统一,降低支持成本
- 关键 bug 修复后 24 小时内全员更新
技术栈
Electron、React、TypeScript、better-sqlite3、electron-updater、Sentry
项目成果
- 支持 Windows 7+ 和 macOS 10.12+
- 装机量 10 万+,日活 8 万+
- 启动时间 1.2 秒,接近原生
- 包体积 80MB(含运行时)
- 崩溃率 0.5%,行业领先
- 开发周期 3 个月,节省 60% 成本
示例二:代码编辑器(Electron)
项目名称
XX 轻量级代码编辑器(Electron)
项目背景
开发者需要轻量级的代码编辑器,VSCode 太重,记事本功能太弱。基于 Electron 打造一款启动快、占用少的编辑器。
业务场景
- 代码编辑:语法高亮、代码补全、多光标
- 文件管理:目录树、文件搜索、快速打开
- 终端集成:内置终端、命令执行
- 插件系统:主题、语言支持、功能扩展
- 版本控制:Git 集成、差异对比
核心职责
1. 文件缓存 - 最近打开
业务场景: 记录最近打开的文件和项目
解决方案
- 使用 electron-store 存储配置
- 记录文件路径、打开时间、光标位置
- 下次打开自动恢复
技术实现
const Store = require('electron-store');
const store = new Store({
defaults: {
recentFiles: [],
recentProjects: [],
editorSettings: {}
}
});
function addRecentFile(filePath, cursorPos) {
const recentFiles = store.get('recentFiles');
// 去重
const filtered = recentFiles.filter(f => f.path !== filePath);
// 添加到最前面
filtered.unshift({
path: filePath,
openedAt: Date.now(),
cursorPos
});
// 只保留最近 20 个
store.set('recentFiles', filtered.slice(0, 20));
}
项目成果
- 最近文件秒开
- 自动恢复编辑状态
- 提升开发效率 30%
2. 启动优化 - 极速启动
业务场景: 快速打开编辑器写代码
解决方案
- 延迟加载插件
- 窗口预创建
- 使用 Web Worker 解析文件
项目成果
- 启动时间 0.5 秒
- 打开 10MB 文件不卡顿
- 内存占用 < 100MB
3. 文件监听 - 自动刷新
业务场景: 文件被外部修改时自动刷新
解决方案
- 使用 chokidar 监听文件变化
- 检测到变化后提示用户
- 支持自动重载
项目成果
- 文件同步准确率 100%
- 避免编辑冲突
- 团队协作效率提升
技术栈
Electron、Monaco Editor、React、chokidar
项目成果
- 启动时间 0.5 秒,比 VSCode 快 10 倍
- 内存占用 < 100MB,比 VSCode 少 5 倍
- 支持 20+ 种编程语言
- GitHub Stars 5000+
面试话术(Electron 项目)
开场(30 秒)
"我做的是一个企业 IM 的桌面客户端,用的是 Electron。选择 Electron 主要是因为我们已经有 Web 版,可以复用现有代码。而且 Electron 可以调用系统 API,实现系统通知、截图、文件管理这些 Web 做不到的功能。"
亮点展开
如果问 Electron 性能: "Electron 确实比原生重一些,但通过优化可以做得很好。我主要做了三方面:一是启动优化,通过窗口预创建和资源本地化,启动时间从 5 秒降到 1.2 秒;二是内存优化,使用本地数据库缓存数据,避免内存堆积;三是进程通信优化,减少主进程和渲染进程的通信次数。"
如果问技术难点: "最难的是跨平台适配。Windows 和 Mac 很多 API 不一样,比如截图功能,Mac 可以用 screencapture 命令,Windows 需要用第三方库。我封装了一层统一接口,根据 process.platform 判断调用哪个实现。还有就是文件路径,Windows 用反斜杠,Mac 用斜杠,需要统一处理。"
如果问和 Web 的区别: "Electron 最大的优势是可以访问系统 API。比如我们实现了系统通知、托盘图标、全局快捷键、文件系统访问,这些 Web 做不到。而且 Electron 可以做多窗口,我们的视频会议就是独立窗口,可以最小化到托盘后台运行。"
数据记忆
- 启动时间 5s → 1.2s
- 聊天记录加载 2s → 100ms
- 崩溃率 5% → 0.5%
- 更新覆盖率 60% → 95%
- 装机量 10 万+
- 日活 8 万+
- 包体积 80MB