文档
Electron 桌面记事本
目标
用 Electron 创建跨平台桌面记事本应用,展示主进程/渲染进程通信、原生菜单和文件保存。
完整代码
1. 项目初始化
mkdir electron-notepad && cd electron-notepad
npm init -y
npm install electron --save-dev
2. package.json
{
"name": "electron-notepad",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder"
}
}
3. main.js
const { app, BrowserWindow, Menu, dialog, ipcMain } = require('electron');
const path = require('path');
const fs = require('fs');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
mainWindow.loadFile('index.html');
// 原生菜单
const menuTemplate = [
{
label: '文件',
submenu: [
{
label: '打开',
accelerator: 'CmdOrCtrl+O',
click: async () => {
const { filePaths } = await dialog.showOpenDialog({ filters: [{ name: '文本文件', extensions: ['txt'] }] });
if (filePaths[0]) {
const content = fs.readFileSync(filePaths[0], 'utf-8');
mainWindow.webContents.send('file-opened', content);
}
},
},
{
label: '保存',
accelerator: 'CmdOrCtrl+S',
click: () => mainWindow.webContents.send('trigger-save'),
},
{ type: 'separator' },
{ label: '退出', accelerator: 'CmdOrCtrl+Q', click: () => app.quit() },
],
},
{
label: '帮助',
submenu: [
{ label: '关于', click: () => dialog.showMessageBox({ title: '关于', message: '桌面记事本 v1.0\n毕设参考项目' }) },
],
},
];
Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplate));
}
// IPC: 保存文件
ipcMain.handle('save-file', async (event, content) => {
const { filePath } = await dialog.showSaveDialog({ filters: [{ name: '文本文件', extensions: ['txt'] }] });
if (filePath) {
fs.writeFileSync(filePath, content, 'utf-8');
return { success: true, path: filePath };
}
return { success: false };
});
app.whenReady().then(createWindow);
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow(); });
4. preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
onFileOpened: (callback) => ipcRenderer.on('file-opened', (_, content) => callback(content)),
onTriggerSave: (callback) => ipcRenderer.on('trigger-save', () => callback()),
saveFile: (content) => ipcRenderer.invoke('save-file', content),
});
5. index.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>桌面记事本</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: system-ui; }
.toolbar { display: flex; gap: 8px; padding: 10px; background: #f1f5f9; border-bottom: 1px solid #e2e8f0; }
.toolbar button { padding: 6px 14px; border: 1px solid #cbd5e1; background: white; border-radius: 4px; cursor: pointer; }
.toolbar button:hover { background: #e2e8f0; }
.info { margin-left: auto; color: #64748b; font-size: 0.85em; display: flex; align-items: center; }
textarea { width: 100%; height: calc(100vh - 48px); padding: 16px; font-size: 16px; border: none; resize: none; outline: none; font-family: 'Fira Code', monospace; line-height: 1.6; }
</style>
</head>
<body>
<div class="toolbar">
<button onclick="newFile()">📄 新建</button>
<button onclick="saveFile()">💾 保存</button>
<span class="info" id="status"></span>
</div>
<textarea id="editor" placeholder="在此输入..." autofocus></textarea>
<script>
const editor = document.getElementById('editor');
const status = document.getElementById('status');
// 打开文件时加载内容
window.electronAPI?.onFileOpened((content) => {
editor.value = content;
status.textContent = '已打开文件';
});
// 菜单保存触发
window.electronAPI?.onTriggerSave(() => saveFile());
async function saveFile() {
const result = await window.electronAPI?.saveFile(editor.value);
if (result?.success) {
status.textContent = `已保存: ${result.path}`;
setTimeout(() => status.textContent = '', 2000);
}
}
function newFile() {
if (editor.value && !confirm('未保存的内容将丢失,确认新建?')) return;
editor.value = '';
status.textContent = '';
}
// 字数统计
editor.addEventListener('input', () => {
document.title = `记事本 - ${editor.value.length} 字`;
});
// Ctrl+S 快捷键
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
e.preventDefault();
saveFile();
}
});
</script>
</body>
</html>
运行步骤
npm start
预期输出
- 打开桌面窗口,显示文本编辑器
- 菜单栏 → 文件 → 打开/保存(原生对话框)
- Ctrl+S 保存文件到本地
- 窗口标题显示实时字数