React+Electron搭建桌面应用(开发OS)

1,225 阅读6分钟

记录工作中使用 electron + react 搭建桌面应用

------- 2023.3.1

代码仓库:gitee.com/zhangning18…

1. 在使用 Electron 开发之前,需要安装node等环境

gogogo:

1. 使用脚手架创建一个 react 项目

这里可以看 react 的官网

npx create-react-app my-app 
// 有一说一,react 创建项目是真的方便
cd my-app
// 有需要的童鞋,可以暴露下 webpack 配置,毕竟所有的操作自己把控还是比较舒心的
npm run eject   

就是这么简单,以上就创建了一个简单的 react 项目

2. 安装 electron ,迎来今天de猪脚

# 安装 electron 包
npm i --save-dev electron

3. 运行主进程

在 Electron 应用程序得入口都是 main 文件,这个文件控制了 主进程,它运行在一个完整的 Node.js 环境中,负责控制应用的生命周期,显示原生界面,执行特殊操作并管理渲染进程。

这里缺少 main.js 文件,我们就创建一个

main.js

在react项目中,已经有了index.html页面,怎样才能把页面插入到 Electron 中呢?

这里需要两个模块:

  • app 模块,控制应用程序的事件生命周期
  • BrowserWindow 模块,创建和管理应用程序窗口

因为主进程运行着 Node.js,可以在 main.js 文件头部将他们导入作为 CommonJS模块

// app 模块,它控制应用程序的事件生命周期。
// BrowserWindow 模块,它创建和管理应用程序 窗口。
const {app, BrowserWindow} = require('electron');

然后,添加一个 createWindow() 方法,将 index.html 加载进一个新的 BrowserWindow 实例。

// 添加一个createWindow()方法来将index.html加载进一个新的BrowserWindow实例。
const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,// 窗口宽度
    height: 600 // 窗口高度
  });
  // 加载应用 --开发阶段  需要运行 npm run start
  win.loadURL('http://localhost:3000/');
};

接着调用 createWindow() 来打开窗口

// 调用createWindow()函数来打开您的窗口。
// 在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口。
// 您可以通过使用 app.whenReady() API来监听此事件。
// 在whenReady()成功后调用createWindow()。
app.whenReady().then(() => {
  createWindow();
});

4. 管理窗口的生命周期

虽然现在可以打开一个浏览器窗口,但还需要一些额外的模板代码使其看起来更像是各个平台原生的。应用程序窗口在每个 OS 下有不同的行为,Electron 将在 app 中实现这些约定的责任交给开发者们。

4.1 关闭所有窗口时退出应用

在 window 和 Linux 上,关闭所有窗口通常会完全退出一个应用程序。

app.on('window-all-closed', ()=>{
  // macOS(darwin) 
  if (process.platform !== 'darwin') app.quit();
})
4.2 如果没有窗口则打开一个窗口

当 Linux 和 Windows 应用在没有窗口打开时退出了,macOS 应用通常在没有打开任何窗口的情况下也继续运行,并且在没有窗口可用的情况下激活应用时会打开新的窗口。

实现这个特性,则监听app模块的 activate 事件。如果没有任何浏览器窗口是打开的,则调用 createWindow() 方法。

  // 当 Linux 和 Windows 应用在没有窗口打开时退出了,macOS 应用通常在没有打开任何窗口的情况下也继续运行,
  // 并且在没有窗口可用的情况下激活应用时会打开新的窗口。
  // 实现这个特性,则监听app模块的 activate 事件。如果没有任何浏览器窗口是打开的,则调用 createWindow() 方法。
  // 因为窗口无法在 ready 事件前创建,你应当在你的应用初始化后仅监听 activate 事件,在 whenReady() 回调中附上您的事件监听器来完成这个操作
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });

这个时候,窗口的控件功能应该已经齐全

完整的 main.js 文件

/*
* @author: zhangning
* @date: 2023/3/2 9:59
* @Description: 创建 main.js 文件,配置 Electron
**/
// app 模块,它控制应用程序的事件生命周期。
// 事件调用app.on('eventName', callback),方法调用app.functionName(arg)
// BrowserWindow 模块,它创建和管理应用程序 窗口。
// new BrowserWindow([options]) 事件和方法调用同app
const {app, BrowserWindow, nativeImage} = require('electron');

// const url = require('url');
// const path = require('path');

// 添加一个createWindow()方法来将index.html加载进一个新的BrowserWindow实例。
const createWindow = () => {
  let win = new BrowserWindow({
    width: 800,// 窗口宽度
    height: 600, // 窗口高度
    title: 'Electron', // 窗口标题,如果由loadURL()加载的HTML文件中含有标签<title>,该属性可忽略
    // "string" || nativeImage.createFromPath('src/image/icons/256x256.ico')从位于 path 的文件创建新的 NativeImage 实例
    icon: nativeImage.createFromPath('src/public/favicon.ico'),
    webPreferences: { // 网页功能设置
      nodeIntegration: true, // 是否启用node集成 渲染进程的内容有访问node的能力
      webviewTag: true, // 是否使用<webview>标签 在一个独立的 frame 和进程里显示外部 web 内容
      webSecurity: false, // 禁用同源策略
      nodeIntegrationInSubFrames: true // 是否允许在子页面(iframe)或子窗口(child window)中集成Node.js
    }
  });
  // 加载应用 --开发阶段  需要运行 npm run start
  win.loadURL('http://localhost:3000/');

  // __dirname 字符串指向当前正在执行脚本的路径
  // path.join API 将多个路径联结在一起,创建一个跨平台的路径字符串。
  // win.loadURL(url.format({
  //   pathname: path.join(__dirname, './build/index.html'),
  //   protocol: 'file:',
  //   slashes: true
  // }));

  // 解决应用启动白屏问题
  win.on('ready-to-show', () => {
    win.show();
    win.focus();
  });

  // 当窗口关闭时发出。在你收到这个事件后,你应该删除对窗口的引用,并避免再使用它。
  win.on('closed', () => {
    win = null;
  });
};

// 调用createWindow()函数来打开您的窗口。
// 在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口。
// 您可以通过使用 app.whenReady() API来监听此事件。
// 在whenReady()成功后调用createWindow()。
app.whenReady().then(() => {
  createWindow();

  // 当 Linux 和 Windows 应用在没有窗口打开时退出了,macOS 应用通常在没有打开任何窗口的情况下也继续运行,
  // 并且在没有窗口可用的情况下激活应用时会打开新的窗口。
  // 实现这个特性,则监听app模块的 activate 事件。如果没有任何浏览器窗口是打开的,则调用 createWindow() 方法。
  // 因为窗口无法在 ready 事件前创建,你应当在你的应用初始化后仅监听 activate 事件,在 whenReady() 回调中附上您的事件监听器来完成这个操作
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

// 关闭所有窗口时退出应用
// 在 window 和 Linux 上,关闭所有窗口通常会完全退出一个应用程序。
app.on('window-all-closed', () => {
  // macOS(darwin)
  if (process.platform !== 'darwin') app.quit();
});

2. 修改 package.json 文件

上面主进程文件创建完成了,这时候来修改 package.json 文件。主要是:

  1. 配置启动文件,添加 main 字段,也就是 main.js 文件,如果没有添加,Electron 将尝试加载包含 package.json 文件目录中的 index.js 文件。
  2. 配置运行命令 使用 "electron-start": "electron ."
{
  "name": "zn-electron-app",
  "version": "0.1.0",
  "private": true,
  "main": "electron/main.js",
  "homepage": ".",
  "dependencies": {
    "scripts": {
      "start": "node scripts/start.js",
    	"build": "node scripts/build.js",
    	"test": "node scripts/test.js",
    	"electron-start": "electron .",
    	"dev": "concurrently "npm run start" "npm run electron-start""
  	},
    ...
  }
  ...
}

1. 启动 Electron

main.js 文件配置了 react 项目地址 mainWindow.loadURL('http://localhost:3000/'), 则需要启动 react 项目

# 启动 react 项目
npm start
# 启动 electron 项目
npm run electron-start

2. 可以看到上面配置了 dev ,就是合并运行的意思

2.1 安装 concurrently
npm install concurrently --save
2.2 编写 package.json
"dev": "concurrently "npm run start" "npm run electron-start""
2.3 执行 dev 命令
npm run dev

发现项目已经运行成功,(这也很 easy 吗!狗头)

后面继续更新 build 打包方面的配置