electron+react+mysql快速构建桌面应用程序

在electron中创建react项目

创建react项目

npx create-react-app electron-react-ipc-app

安装electron相关模块

npm install electron electron-builder wait-on
npm install electron-is-dev concurrently

electron-builder:用于为 macOS、Windows 和 Linux 打包和构建准备好分发的 Electron 应用程序,并提供开箱即用的“自动更新”支持。
wait-on:用于等待资源的简单命令行实用程序和 Node.js API,确保开发环境下electron能够访问到react服务。
electron-is-dev:仅在开发期间用于启用调试功能。 这个包必须在 Electron 主进程中使用。
concurrently:同时运行多个命令。像 npm run watch-js 和 npm run watch-less 但更好。

创建electron文件夹和main.js

const {app, BrowserWindow} = require('electron');
const isDev = require('electron-is-dev');   
const path = require('path');

let win ;

function createWindow() {
    win  = new BrowserWindow({
        width:800,
        height:600,
    });
    // const startURL = isDev ? 'http://localhost:3000' :
    //  `file://${path.join(__dirname, '../build/index.html')}`;
    // win.loadURL(startURL); //npm run dev
    const startURL = `file://${path.join(__dirname, '../build/index.html')}`;//npm run estart
    win.loadURL(startURL);
 
     // Open the DevTools.
    if (isDev) {
        win.webContents.openDevTools({ mode: 'detach' });
    } 
   
}

app.whenReady().then(()=> {
    createWindow();
})

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

app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
});

修改package.json

添加main属性指向electron的启动js及项目主页的url

"main": "electron/main.js",
"homepage": "./",

添加electron启动和打包脚本

"dev": "concurrently \"npm start\" \"wait-on http://localhost:3000 && electron .\"",
"ebuild": "npm run build && electron-builder",
"estart": "npm run build && electron ."

防止electron打包报错

  1. 添加devDependencies属性,并将dependencies属性中的electron和electron-builder移动到devDependencies中。

  2. electron-builder打包默认是去找 public/electron.js,如果想把electron代码放在别处(如electron/main.js),则需要在package.js中添加如下配置,它指定了electron-builder打包文件及额外的配置文件(main的路径),然后将react和electron的清单文件分离。

    "build": {
        "files": [
          "build/**/*",
          "node_modules/**/*",
          "electron/**/*"
        ],
        "directories":{
          "buildResources": "assets"
        },
        "extraMetadata": {
          "main": "electron/main.js"
        }
    } 
    

修改后的package.json如下:

{
  "name": "electron-react-ipc-app",
  "main": "electron/main.js",
  "homepage": "./",
  "description":"test",
  "author":"test",
  "version": "0.1.0",
  "private": true,
  "devDependencies": {
    "electron": "^22.0.0",
    "electron-builder": "^23.6.0"
  },
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "concurrently": "^7.6.0",
    "electron-is-dev": "^2.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "wait-on": "^7.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "dev": "concurrently \"npm start\" \"wait-on  http://10.10.10.5:3000 && electron .\"",
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "ebuild": "npm run build && electron-builder",
    "estart": "npm run build && electron ."
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "build": {
    "files": [
      "build/**/*",
      "node_modules/**/*",
      "electron/**/*"
    ],
    "directories":{
      "buildResources": "assets"
    },
    "extraMetadata": {
      "main": "electron/main.js"
    }
    
  }
}

创建.env文件

BROWSER=none (避免react服务启动时打开浏览器)

此时electron-react的目录结构

当前目录结构

启动electron

npm run dev (会先启动react服务,再启动electron服务访问localhost:4000)
npm run ebuild(会先打包react项目,再打包electron)
npm run estart(会先打包react项目,再启动electron访问react打包后的静态文件)

安装mysql2模块

electron可以通过安装mysql2模块,建立连接并访问数据库,还可以创建数据库连接池。

npm install mysql2

测试安装

测试代码

const mysql = require('mysql2');

const connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: '123456',
    port: '3306',                   
    database: 'test'
});

connection.connect();

var  sql = 'SELECT * FROM material';
connection.query(sql,function (err, result) {
    if(err){
    console.log('[SELECT ERROR] - ',err.message);
    return;
    }
console.log(result);
  
});

connection.end();

控制台输入 node .\electron\connection.js
控制台输出
接着将connection引入到electron中即可完成增删改查操作。

利用IPC模块完成数据交互

现在通过electron可以访问数据库数据,可以通过electron的IPC(进程间通信)模块完成数据交互和渲染。在react项目利用Elctron的ipcRenderer.invoke()函数从渲染器请求主进程数据,主进程通过ipcMain.handle()函数监听渲染器请求数据完成数据交互。这里访问数据库直接采用 mysql2/promise 模块。

创建预加载文件preload.js

为了安全起见,electron主进程不会直接暴露整个 ipcRenderer.invoke API。 确保尽可能限制渲染器对 Electron API 的访问。

const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI',{
  findAll: () => ipcRenderer.invoke('findAll')
})

修改main.js

main.js中指定预加载文件路径

win  = new BrowserWindow({
        width:800,
        height:600,
        //指定预加载文件的路径
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
});

main.js中添加ipcMain.handle()监听事件

ipcMain.handle('findAll', async() => {
    var sql = 'SELECT * FROM material';
    var [rows,fields] = await connection.query(sql);
    return rows;
    
})

react代码中调用electron API

function App() {
  const [result,setResult] = useState('');
  const handleClick = async () => {
      const a = await window.electronAPI.findAll();
      console.log(a)
      setResult(JSON.stringify(a))
  }
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <button onClick={handleClick}>发送请求</button>
        <textarea value={result}/>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

运行效果
运行效果

完整源码