基础示例
完整的 Todo 应用示例
这个示例展示了如何使用 ipc-express 构建一个简单的 Todo 应用。
主进程代码
typescript
// main.ts
import { app, BrowserWindow, ipcMain } from 'electron';
import express from 'express';
import { IpcServer } from '@mizuka-wu/ipc-express';
import path from 'path';
const expressApp = express();
const ipcServer = new IpcServer(ipcMain);
// 模拟数据存储
let todos: Array<{ id: number; title: string; completed: boolean }> = [
{ id: 1, title: 'Learn Electron', completed: false },
{ id: 2, title: 'Learn ipc-express', completed: false },
];
// 中间件
expressApp.use(express.json());
// 日志中间件
expressApp.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
// API 路由
// 获取所有 Todo
expressApp.get('/api/todos', (req, res) => {
res.send(todos);
});
// 获取单个 Todo
expressApp.get('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const todo = todos.find(t => t.id === id);
if (!todo) {
return res.status(404).send({ error: 'Todo not found' });
}
res.send(todo);
});
// 创建 Todo
expressApp.post('/api/todos', (req, res) => {
const { title } = req.body;
if (!title) {
return res.status(400).send({ error: 'Title is required' });
}
const newTodo = {
id: Math.max(...todos.map(t => t.id), 0) + 1,
title,
completed: false,
};
todos.push(newTodo);
res.status(201).send(newTodo);
});
// 更新 Todo
expressApp.patch('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const todo = todos.find(t => t.id === id);
if (!todo) {
return res.status(404).send({ error: 'Todo not found' });
}
if (req.body.title !== undefined) {
todo.title = req.body.title;
}
if (req.body.completed !== undefined) {
todo.completed = req.body.completed;
}
res.send(todo);
});
// 删除 Todo
expressApp.delete('/api/todos/:id', (req, res) => {
const id = parseInt(req.params.id);
const index = todos.findIndex(t => t.id === id);
if (index === -1) {
return res.status(404).send({ error: 'Todo not found' });
}
const deleted = todos.splice(index, 1);
res.send(deleted[0]);
});
// 启动 IPC 服务
ipcServer.listen(expressApp);
// 创建窗口
function createWindow() {
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: false,
contextIsolation: true,
},
});
mainWindow.loadFile('index.html');
}
app.on('ready', createWindow);
app.on('before-quit', () => {
ipcServer.removeAllListeners();
});渲染进程代码
typescript
// renderer.ts
import { ipcRenderer } from 'electron';
import { IpcClient } from '@mizuka-wu/ipc-express/client';
interface Todo {
id: number;
title: string;
completed: boolean;
}
const client = new IpcClient(ipcRenderer);
// 获取所有 Todo
async function loadTodos() {
try {
const response = await client.get<Todo[]>('/api/todos');
const todos = response.data;
const todoList = document.getElementById('todo-list');
todoList!.innerHTML = '';
todos.forEach(todo => {
const li = document.createElement('li');
li.innerHTML = `
<input type="checkbox" ${todo.completed ? 'checked' : ''}
onchange="toggleTodo(${todo.id})">
<span>${todo.title}</span>
<button onclick="deleteTodo(${todo.id})">Delete</button>
`;
todoList!.appendChild(li);
});
} catch (error) {
console.error('Failed to load todos:', error);
}
}
// 创建 Todo
async function createTodo() {
const input = document.getElementById('todo-input') as HTMLInputElement;
const title = input.value.trim();
if (!title) return;
try {
const response = await client.post<Todo>('/api/todos', { title });
input.value = '';
loadTodos();
} catch (error) {
console.error('Failed to create todo:', error);
}
}
// 切换 Todo 完成状态
async function toggleTodo(id: number) {
try {
const response = await client.get<Todo>(`/api/todos/${id}`);
const todo = response.data;
await client.patch<Todo>(`/api/todos/${id}`, {
completed: !todo.completed,
});
loadTodos();
} catch (error) {
console.error('Failed to toggle todo:', error);
}
}
// 删除 Todo
async function deleteTodo(id: number) {
try {
await client.delete(`/api/todos/${id}`);
loadTodos();
} catch (error) {
console.error('Failed to delete todo:', error);
}
}
// 初始化
document.addEventListener('DOMContentLoaded', () => {
loadTodos();
const addButton = document.getElementById('add-button');
addButton?.addEventListener('click', createTodo);
});HTML 模板
html
<!DOCTYPE html>
<html>
<head>
<title>Todo App</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
}
#todo-list {
list-style: none;
padding: 0;
}
#todo-list li {
padding: 10px;
border: 1px solid #ddd;
margin: 5px 0;
display: flex;
align-items: center;
gap: 10px;
}
#todo-list input[type="checkbox"] {
cursor: pointer;
}
#todo-list span {
flex: 1;
}
#todo-list button {
background: #ff6b6b;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
#todo-input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
}
#add-button {
padding: 10px 20px;
background: #51cf66;
color: white;
border: none;
cursor: pointer;
}
</style>
</head>
<body>
<h1>Todo App</h1>
<div class="input-group">
<input id="todo-input" type="text" placeholder="Add a new todo...">
<button id="add-button">Add</button>
</div>
<ul id="todo-list"></ul>
<script src="renderer.js"></script>
</body>
</html>关键点
- 类型安全 - 使用泛型参数确保类型安全
- 错误处理 - 使用 try-catch 处理请求错误
- 状态码 - 正确使用 HTTP 状态码(201 创建、404 未找到等)
- 中间件 - 使用 Express 中间件处理日志、解析等
- RESTful API - 遵循 REST 设计原则
下一步
查看 高级用法 了解更多复杂的场景。