前言
基于前面文章中验证的高保真原型,本文将详细介绍如何构建现代化的前端工程。我们的前端项目位于 code/frontend/ 目录,采用 Electron + React + TypeScript 技术栈,实现了从Web原型到桌面应用的完美转换。
前端技术架构设计
整体架构概览
我们的前端架构采用了现代化的工程化方案:
code/frontend/
├── src/ # 源代码目录
│ ├── components/ # React组件
│ ├── App.tsx # 主应用组件
│ └── main.tsx # 应用入口
├── electron/ # Electron配置
│ ├── main.ts # 主进程
│ └── preload.ts # 预加载脚本
├── public/ # 静态资源
├── dll/ # C++ DLL集成
├── package.json # 项目配置
├── vite.config.ts # Vite构建配置
└── tsconfig.json # TypeScript配置
技术栈选择分析
Electron:桌面应用框架
选择理由:
- 跨平台支持: 一次开发,多平台运行
- Web技术栈: 利用现有的前端技术积累
- 系统API访问: 可以调用操作系统原生功能
- C++集成: 通过koffi无缝集成C++ DLL
从 code/frontend/package.json 中可以看到核心依赖:
{
"name": "ILMS",
"main": "dist-electron/main.js",
"scripts": {
"dev": "vite",
"build": "tsc && vite build && electron-builder",
"preview": "vite preview",
"test": "playwright test"
},
"dependencies": {
"koffi": "^2.12.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"chart.js": "^4.5.0"
},
"devDependencies": {
"electron": "^28.0.0",
"electron-builder": "^24.9.1",
"vite": "^5.0.8",
"@playwright/test": "^1.54.1",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17"
}
}
React 18 + TypeScript:用户界面开发
技术优势:
- 组件化开发: 提高代码复用性和维护性
- 虚拟DOM: 优化大数据量场景下的渲染性能
- 类型安全: TypeScript提供编译时类型检查
- 生态丰富: 丰富的第三方组件库
从 code/frontend/tsconfig.json 配置可以看出严格的类型检查设置:
{
"compilerOptions": {
"target": "ESNext",
"lib": ["ESNext", "DOM"],
"module": "ESNext",
"strict": true,
"noImplicitAny": true,
"exactOptionalPropertyTypes": true
}
}
Vite:现代化构建工具
性能优势:
- 快速启动: 基于ESBuild的超快冷启动
- 热重载: 毫秒级的模块热更新
- 按需编译: 只编译当前页面需要的模块
- 插件生态: 丰富的插件支持
项目工程化配置
1. Vite构建优化
code/frontend/vite.config.ts 的关键配置:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import electron from 'vite-plugin-electron'
import path from 'path'
export default defineConfig({
build: {
outDir: 'dist-electron',
rollupOptions: {
external: ['koffi']
}
},
plugins: [
react(),
electron([
{
entry: 'electron/main.ts',
vite: {
build: {
outDir: 'dist-electron',
rollupOptions: {
external: ['electron', 'koffi'],
},
minify: false,
}
}
},
{
entry: 'electron/preload.ts',
vite: {
build: {
outDir: 'dist-electron',
rollupOptions: {
external: ['koffi']
},
minify: false,
}
},
onstart(options) {
options.reload()
},
}
])
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
optimizeDeps: {
exclude: ['koffi']
}
})
配置亮点:
- 开发调试支持: 支持VSCode调试模式
- 源码映射: 生产环境保留源码映射便于调试
- 外部依赖: koffi作为外部依赖,避免打包冲突
2. TypeScript类型系统
为了确保类型安全,我们建立了完整的类型定义体系:
// src/types/index.ts
export interface Book {
id: string;
title: string;
author: string;
isbn: string;
category: BookCategory;
status: BookStatus;
publishDate: Date;
addedDate: Date;
}
export interface User {
id: string;
name: string;
email: string;
phone: string;
role: UserRole;
status: UserStatus;
registeredDate: Date;
}
export interface BorrowRecord {
id: string;
bookId: string;
userId: string;
borrowDate: Date;
dueDate: Date;
returnDate?: Date;
status: BorrowStatus;
}
export type BookCategory = 'literature' | 'science' | 'history' | 'art' | 'other';
export type BookStatus = 'available' | 'borrowed' | 'reserved' | 'maintenance';
export type UserRole = 'admin' | 'librarian' | 'reader';
export type UserStatus = 'active' | 'suspended' | 'inactive';
export type BorrowStatus = 'active' | 'overdue' | 'returned';
3. 代码质量保证
ESLint配置
code/frontend/eslint.config.js 提供了严格的代码规范:
import tseslint from 'typescript-eslint'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
export default tseslint.config([
{
files: ['**/*.{ts,tsx}'],
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/explicit-function-return-type': 'warn'
}
}
])
Tailwind CSS样式管理
实际项目使用的是 @tailwindcss/postcss7-compat 版本,code/frontend/tailwind.config.js 配置:
module.exports = {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
同时在 package.json 中使用兼容版本:
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17"
## 核心组件架构
### 1. 主应用组件结构
`src/App.tsx` 实现了应用的主体结构:
```typescript
import React, { useState } from 'react';
import { Dashboard } from './components/Dashboard';
import { BookManager } from './components/BookManager';
import { UserManager } from './components/UserManager';
import { BorrowManager } from './components/BorrowManager';
import { StatisticsReport } from './components/StatisticsReport';
type ActiveView = 'dashboard' | 'books' | 'users' | 'borrow' | 'statistics';
const App: React.FC = () => {
const [activeView, setActiveView] = useState<ActiveView>('dashboard');
const renderActiveComponent = (): JSX.Element => {
switch (activeView) {
case 'dashboard':
return <Dashboard />;
case 'books':
return <BookManager />;
case 'users':
return <UserManager />;
case 'borrow':
return <BorrowManager />;
case 'statistics':
return <StatisticsReport />;
default:
return <Dashboard />;
}
};
return (
<div className="flex h-screen bg-gray-100">
{/* 侧边栏导航 */}
<nav className="w-64 bg-white shadow-sm">
<div className="p-4">
<h1 className="text-xl font-bold text-gray-800">图书馆管理系统</h1>
</div>
<ul className="mt-6">
{navigationItems.map((item) => (
<li key={item.id}>
<button
onClick={() => setActiveView(item.id)}
className={`w-full text-left px-4 py-2 hover:bg-gray-50 ${
activeView === item.id ? 'bg-blue-50 text-blue-600' : 'text-gray-700'
}`}
>
<i className={`${item.icon} mr-3`}></i>
{item.label}
</button>
</li>
))}
</ul>
</nav>
{/* 主内容区域 */}
<main className="flex-1 overflow-auto">
{renderActiveComponent()}
</main>
</div>
);
**[截图占位]**: React应用运行截图,展示侧边栏导航和主内容区域
};
export default App;
设计特点:
- 状态管理: 使用React Hook管理当前视图状态
- 组件懒加载: 按需渲染当前激活的组件
- 响应式布局: 使用Tailwind CSS实现响应式设计
2. 功能组件设计模式
Dashboard组件示例
// src/components/Dashboard.tsx
import React, { useEffect, useState } from 'react';
import { useBackendAPI } from '../hooks/useBackendAPI';
interface DashboardStats {
totalBooks: number;
totalUsers: number;
activeBorrows: number;
overdueBooks: number;
}
export const Dashboard: React.FC = () => {
const [stats, setStats] = useState<DashboardStats | null>(null);
const [loading, setLoading] = useState(true);
const { getDashboardStats } = useBackendAPI();
useEffect(() => {
loadDashboardData();
}, []);
const loadDashboardData = async (): Promise<void> => {
try {
setLoading(true);
const data = await getDashboardStats();
setStats(data);
} catch (error) {
console.error('Failed to load dashboard data:', error);
} finally {
setLoading(false);
}
};
if (loading) {
return <div className="flex justify-center items-center h-full">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
</div>;
}
return (
<div className="p-6">
<h2 className="text-2xl font-bold text-gray-800 mb-6">仪表板概览</h2>
{/* 统计卡片 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<StatCard
title="图书总数"
value={stats?.totalBooks || 0}
icon="fas fa-book"
color="blue"
/>
<StatCard
title="用户总数"
value={stats?.totalUsers || 0}
icon="fas fa-users"
color="green"
/>
<StatCard
title="借阅中"
value={stats?.activeBorrows || 0}
icon="fas fa-hand-holding"
color="yellow"
/>
<StatCard
title="逾期图书"
value={stats?.overdueBooks || 0}
icon="fas fa-exclamation-triangle"
color="red"
/>
</div>
{/* 图表区域 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<BorrowTrendChart />
<CategoryDistributionChart />
</div>
</div>
);
};
组件设计原则:
- 单一职责: 每个组件只负责一个功能领域
- 状态管理: 使用React Hooks管理组件状态
- 错误处理: 完善的loading和error状态处理
- 类型安全: 完整的TypeScript类型定义
3. 自定义Hook封装
useBackendAPI Hook
// src/hooks/useBackendAPI.ts
import { useCallback } from 'react';
import { Book, User, BorrowRecord } from '../types';
interface BackendAPI {
// 图书管理
getBookList: () => Promise<Book[]>;
addBook: (book: Omit<Book, 'id'>) => Promise<boolean>;
updateBook: (id: string, book: Partial<Book>) => Promise<boolean>;
deleteBook: (id: string) => Promise<boolean>;
// 用户管理
getUserList: () => Promise<User[]>;
addUser: (user: Omit<User, 'id'>) => Promise<boolean>;
updateUser: (id: string, user: Partial<User>) => Promise<boolean>;
// 借阅管理
getBorrowRecords: () => Promise<BorrowRecord[]>;
borrowBook: (bookId: string, userId: string) => Promise<boolean>;
returnBook: (recordId: string) => Promise<boolean>;
// 仪表板数据
getDashboardStats: () => Promise<DashboardStats>;
}
export const useBackendAPI = (): BackendAPI => {
// 通过koffi调用C++ DLL的实现
const callBackend = useCallback(async (functionName: string, params?: any) => {
try {
// 这里会在下一篇文章中详细介绍koffi集成
const result = await window.electronAPI.callBackend(functionName, params);
return result;
} catch (error) {
console.error(`Backend call failed: ${functionName}`, error);
throw error;
}
}, []);
return {
getBookList: () => callBackend('GetBookList'),
addBook: (book) => callBackend('AddBook', book),
updateBook: (id, book) => callBackend('UpdateBook', { id, ...book }),
deleteBook: (id) => callBackend('DeleteBook', { id }),
getUserList: () => callBackend('GetUserList'),
addUser: (user) => callBackend('AddUser', user),
updateUser: (id, user) => callBackend('UpdateUser', { id, ...user }),
getBorrowRecords: () => callBackend('GetBorrowRecords'),
borrowBook: (bookId, userId) => callBackend('BorrowBook', { bookId, userId }),
returnBook: (recordId) => callBackend('ReturnBook', { recordId }),
getDashboardStats: () => callBackend('GetDashboardStats'),
};
};
Electron集成实践
1. 主进程配置
electron/main.ts 配置了应用的主进程:
import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';
const createWindow = (): void => {
const mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 1000,
minHeight: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
enableRemoteModule: false,
preload: path.join(__dirname, 'preload.js')
},
titleBarStyle: 'default',
icon: path.join(__dirname, '../assets/icon.png')
});
// 开发环境加载Vite开发服务器
if (process.env.NODE_ENV === 'development') {
mainWindow.loadURL('http://localhost:5173');
mainWindow.webContents.openDevTools();
} else {
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
}
};
app.whenReady().then(createWindow);
// 处理应用激活
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// 处理窗口关闭
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
// IPC通信处理
ipcMain.handle('call-backend', async (event, functionName, params) => {
// 在下一篇文章中详细介绍C++ DLL调用实现
try {
return await callCppDLL(functionName, params);
} catch (error) {
throw error;
}
});
2. 预加载脚本
electron/preload.ts 提供了安全的API接口:
import { contextBridge, ipcRenderer } from 'electron';
// 向渲染进程暴露安全的API
contextBridge.exposeInMainWorld('electronAPI', {
callBackend: (functionName: string, params?: any) =>
ipcRenderer.invoke('call-backend', functionName, params),
// 文件操作API
selectFile: () => ipcRenderer.invoke('select-file'),
saveFile: (data: any, filename: string) =>
ipcRenderer.invoke('save-file', data, filename),
// 应用控制API
minimize: () => ipcRenderer.invoke('minimize-window'),
maximize: () => ipcRenderer.invoke('maximize-window'),
close: () => ipcRenderer.invoke('close-window'),
});
// 类型定义
declare global {
interface Window {
electronAPI: {
callBackend: (functionName: string, params?: any) => Promise<any>;
selectFile: () => Promise<string>;
saveFile: (data: any, filename: string) => Promise<boolean>;
minimize: () => Promise<void>;
maximize: () => Promise<void>;
close: () => Promise<void>;
};
}
}
性能优化实践
1. 组件级别优化
// 使用React.memo优化组件重渲染
export const BookListItem = React.memo<BookListItemProps>(({ book, onEdit, onDelete }) => {
return (
<tr className="hover:bg-gray-50">
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{book.title}
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{book.author}
</td>
<td className="px-6 py-4 whitespace-nowrap">
<StatusBadge status={book.status} />
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button onClick={() => onEdit(book)} className="text-blue-600 hover:text-blue-900 mr-2">
编辑
</button>
<button onClick={() => onDelete(book.id)} className="text-red-600 hover:text-red-900">
删除
</button>
</td>
</tr>
);
});
// 使用useCallback优化事件处理器
const BookManager: React.FC = () => {
const [books, setBooks] = useState<Book[]>([]);
const handleEditBook = useCallback((book: Book) => {
// 编辑逻辑
}, []);
const handleDeleteBook = useCallback((bookId: string) => {
// 删除逻辑
}, []);
return (
<div>
{books.map(book => (
<BookListItem
key={book.id}
book={book}
onEdit={handleEditBook}
onDelete={handleDeleteBook}
/>
))}
</div>
);
};
2. 虚拟滚动优化
对于大数据量的表格展示,我们实现了虚拟滚动:
import { FixedSizeList as List } from 'react-window';
const VirtualizedBookList: React.FC<{ books: Book[] }> = ({ books }) => {
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
<div style={style}>
<BookListItem book={books[index]} />
</div>
);
return (
<List
height={600}
itemCount={books.length}
itemSize={60}
width="100%"
>
{Row}
</List>
);
};
3. 数据缓存策略
// 使用React Query进行数据缓存
import { useQuery, useMutation, useQueryClient } from 'react-query';
export const useBooks = () => {
const { getBookList, addBook, updateBook, deleteBook } = useBackendAPI();
return useQuery(['books'], getBookList, {
staleTime: 5 * 60 * 1000, // 5分钟内数据视为新鲜
cacheTime: 10 * 60 * 1000, // 10分钟缓存时间
});
};
export const useAddBook = () => {
const queryClient = useQueryClient();
const { addBook } = useBackendAPI();
return useMutation(addBook, {
onSuccess: () => {
queryClient.invalidateQueries(['books']);
},
});
};
开发工作流
1. 开发环境启动
# code/frontend/ 目录下
npm install # 安装依赖
npm run dev # 启动开发服务器
npm run electron # 启动Electron应用
[截图占位]: 开发环境启动过程截图,展示终端命令执行和应用启动
2. 构建流程
npm run build # 构建生产版本
npm run preview # 预览构建结果
npm run dist # 打包Electron应用
3. 调试配置
在VSCode中配置调试:
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Electron Main",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/code/frontend",
"runtimeExecutable": "${workspaceFolder}/code/frontend/node_modules/.bin/electron",
"args": ["--inspect=5858", "."],
"env": {
"NODE_ENV": "development"
}
}
]
}
下期预告
在下一篇文章中,我们将深入介绍后端C++ DLL的开发实践,包括模块化设计、CMake构建配置,以及如何实现高性能的业务逻辑处理。
总结
本文详细介绍了基于Electron + React + TypeScript的前端工程化实践,包括:
- 现代化技术栈: 选择合适的技术组合
- 工程化配置: 完善的构建和开发工具链
- 组件化架构: 可维护的代码结构
- 性能优化: 多层次的性能优化策略
- 开发工作流: 高效的开发和调试流程
通过这些实践,我们成功地将高保真原型转化为了高质量的桌面应用前端,为后续的前后端集成奠定了坚实的基础。
系列文章目录
- 项目架构设计与技术选型
- 高保真原型设计与用户体验测试
- 前端工程化实践:Electron + React + TypeScript
- 后端C++ DLL开发与模块化设计
- 前后端集成:koffi调用与接口设计
- Google Test单元测试实践
- CMake构建系统与持续集成
- 性能优化与部署发布
通过这个系列文章,您将学习到现代桌面应用开发的完整流程和最佳实践。
1646

被折叠的 条评论
为什么被折叠?



