React集成 #

一、项目搭建 #

1.1 使用 Vite 创建项目 #

bash
# 创建 React + TypeScript 项目
npm create vite@latest my-electron-react -- --template react-ts

# 进入项目目录
cd my-electron-react

# 安装 Electron
npm install electron --save-dev
npm install electron-builder --save-dev

# 安装开发工具
npm install vite-plugin-electron --save-dev
npm install electron-reload --save-dev

1.2 项目结构 #

text
my-electron-react/
├── electron/
│   ├── main.ts           # 主进程
│   ├── preload.ts        # 预加载脚本
│   └── utils/            # 工具函数
├── src/
│   ├── App.tsx           # React 根组件
│   ├── main.tsx          # 入口文件
│   ├── components/       # 组件目录
│   ├── hooks/            # 自定义 Hooks
│   ├── stores/           # 状态管理
│   ├── utils/            # 工具函数
│   └── types/            # 类型定义
├── public/               # 静态资源
├── vite.config.ts        # Vite 配置
├── electron-builder.yml  # 打包配置
└── package.json

1.3 Vite 配置 #

typescript
// 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({
    plugins: [
        react(),
        electron([
            {
                entry: 'electron/main.ts',
                onstart(options) {
                    options.startup();
                },
                vite: {
                    build: {
                        outDir: 'dist-electron',
                        rollupOptions: {
                            external: ['electron']
                        }
                    }
                }
            },
            {
                entry: 'electron/preload.ts',
                onstart(options) {
                    options.reload();
                },
                vite: {
                    build: {
                        outDir: 'dist-electron'
                    }
                }
            }
        ])
    ],
    resolve: {
        alias: {
            '@': path.resolve(__dirname, './src')
        }
    },
    base: './',
    build: {
        outDir: 'dist'
    }
});

二、主进程配置 #

2.1 主进程入口 #

typescript
// electron/main.ts
import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';

const isDev = !app.isPackaged;

let mainWindow: BrowserWindow | null = null;

function createWindow() {
    mainWindow = new BrowserWindow({
        width: 1200,
        height: 800,
        webPreferences: {
            preload: path.join(__dirname, 'preload.js'),
            nodeIntegration: false,
            contextIsolation: true
        }
    });

    if (isDev) {
        mainWindow.loadURL('http://localhost:5173');
        mainWindow.webContents.openDevTools();
    } else {
        mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
    }

    mainWindow.on('closed', () => {
        mainWindow = null;
    });
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        app.quit();
    }
});

app.on('activate', () => {
    if (mainWindow === null) {
        createWindow();
    }
});

2.2 预加载脚本 #

typescript
// electron/preload.ts
import { contextBridge, ipcRenderer } from 'electron';

contextBridge.exposeInMainWorld('electronAPI', {
    getAppInfo: () => ipcRenderer.invoke('get-app-info'),
    openFile: () => ipcRenderer.invoke('open-file'),
    saveFile: (content: string) => ipcRenderer.invoke('save-file', content)
});

三、React 集成 #

3.1 类型定义 #

typescript
// src/types/electron.d.ts
export interface ElectronAPI {
    getAppInfo: () => Promise<{ version: string; platform: string }>;
    openFile: () => Promise<string | null>;
    saveFile: (content: string) => Promise<boolean>;
}

declare global {
    interface Window {
        electronAPI: ElectronAPI;
    }
}

3.2 自定义 Hook #

typescript
// src/hooks/useElectron.ts
import { useState, useEffect } from 'react';

export function useAppInfo() {
    const [info, setInfo] = useState<{ version: string; platform: string } | null>(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        window.electronAPI.getAppInfo()
            .then(setInfo)
            .finally(() => setLoading(false));
    }, []);

    return { info, loading };
}

export function useFileOperations() {
    const openFile = async () => {
        return await window.electronAPI.openFile();
    };

    const saveFile = async (content: string) => {
        return await window.electronAPI.saveFile(content);
    };

    return { openFile, saveFile };
}

3.3 React 组件示例 #

tsx
// src/App.tsx
import { useState } from 'react';
import { useAppInfo, useFileOperations } from './hooks/useElectron';

function App() {
    const { info, loading } = useAppInfo();
    const { openFile, saveFile } = useFileOperations();
    const [content, setContent] = useState('');

    const handleOpen = async () => {
        const fileContent = await openFile();
        if (fileContent) {
            setContent(fileContent);
        }
    };

    const handleSave = async () => {
        await saveFile(content);
    };

    if (loading) {
        return <div>Loading...</div>;
    }

    return (
        <div className="app">
            <header>
                <h1>Electron + React App</h1>
                <p>Version: {info?.version}</p>
                <p>Platform: {info?.platform}</p>
            </header>
            
            <main>
                <div className="toolbar">
                    <button onClick={handleOpen}>Open</button>
                    <button onClick={handleSave}>Save</button>
                </div>
                
                <textarea
                    value={content}
                    onChange={(e) => setContent(e.target.value)}
                    placeholder="Enter text here..."
                />
            </main>
        </div>
    );
}

export default App;

四、状态管理 #

4.1 使用 Zustand #

bash
npm install zustand
typescript
// src/stores/appStore.ts
import { create } from 'zustand';

interface AppState {
    theme: 'light' | 'dark';
    sidebarOpen: boolean;
    setTheme: (theme: 'light' | 'dark') => void;
    toggleSidebar: () => void;
}

export const useAppStore = create<AppState>((set) => ({
    theme: 'light',
    sidebarOpen: true,
    setTheme: (theme) => set({ theme }),
    toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen }))
}));

4.2 使用组件 #

tsx
// src/components/ThemeToggle.tsx
import { useAppStore } from '../stores/appStore';

export function ThemeToggle() {
    const { theme, setTheme } = useAppStore();

    return (
        <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
            Current theme: {theme}
        </button>
    );
}

五、路由配置 #

5.1 安装 React Router #

bash
npm install react-router-dom

5.2 路由配置 #

tsx
// src/router.tsx
import { createBrowserRouter } from 'react-router-dom';
import App from './App';
import Home from './pages/Home';
import Settings from './pages/Settings';
import About from './pages/About';

export const router = createBrowserRouter([
    {
        path: '/',
        element: <App />,
        children: [
            { index: true, element: <Home /> },
            { path: 'settings', element: <Settings /> },
            { path: 'about', element: <About /> }
        ]
    }
]);
tsx
// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
import { router } from './router';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
        <RouterProvider router={router} />
    </React.StrictMode>
);

六、打包配置 #

6.1 package.json #

json
{
    "name": "my-electron-react",
    "version": "1.0.0",
    "main": "dist-electron/main.js",
    "scripts": {
        "dev": "vite",
        "build": "tsc && vite build && electron-builder",
        "preview": "vite preview"
    },
    "dependencies": {
        "react": "^18.2.0",
        "react-dom": "^18.2.0",
        "react-router-dom": "^6.20.0",
        "zustand": "^4.4.7"
    },
    "devDependencies": {
        "@types/react": "^18.2.43",
        "@types/react-dom": "^18.2.17",
        "@vitejs/plugin-react": "^4.2.1",
        "electron": "^28.0.0",
        "electron-builder": "^24.9.1",
        "typescript": "^5.3.3",
        "vite": "^5.0.8",
        "vite-plugin-electron": "^0.28.0"
    }
}

6.2 electron-builder.yml #

yaml
appId: com.example.myapp
productName: My Electron React App

directories:
    output: release

files:
    - dist/**/*
    - dist-electron/**/*

mac:
    icon: build/icon.icns

win:
    icon: build/icon.ico

linux:
    icon: build/icon.png

七、最佳实践 #

7.1 开发建议 #

markdown
- [ ] 使用 TypeScript 类型安全
- [ ] 使用 Vite 加速开发
- [ ] 组件化开发
- [ ] 状态管理统一
- [ ] IPC 通信封装

7.2 性能优化 #

typescript
// 使用 React.lazy 懒加载
const Settings = React.lazy(() => import('./pages/Settings'));

// 使用 Suspense
<Suspense fallback={<div>Loading...</div>}>
    <Settings />
</Suspense>

八、总结 #

8.1 核心要点 #

要点 说明
Vite 推荐的构建工具
TypeScript 类型安全
IPC 封装 自定义 Hook 封装
状态管理 Zustand/Redux

8.2 下一步 #

现在你已经掌握了 React 集成,接下来让我们学习 Vue集成,深入了解 Electron 与 Vue 的集成开发!

最后更新:2026-03-28