Electron+React的互相通信并构建本地exe应用

目录

说明

1 安装React

1.1 安装create-react-app

1.2 创建react应用

2 安装electron

2.1 在同级目录下安装electron

2.2  添加electron启动文件main.js

2.3  更改package.json,为electron添加入口

3 本地启动项目

3.1 启动react

3.2 启动electron 

4 打包生成本地应用

4.1 react打包应用

4.2 使用electron-packager打包electron应用

5 系统GUI和React程序之间的互相通信

5.1 Electron中主进程与渲染进程

5.2 主进程与渲染进程的区别

5.3 新建和编写render.js

5.4 在React中访问Electron


说明

  • 网上看了很多electron和react结合的示例,很多仍然没有做到很好的通信,有些一知半解,这里给出我自己的完整方案
  • 其中react只是一个生成单页面的工具,理论上可以结合任何前端js库使用,当然也可以替换成Vue,Angular等MV*框架
  • 这里以 Windows 10 系统为开发环境

1 安装React

这里使用create-react-app工具安装,前提是本地已经安装node环境

1.1 安装create-react-app

npm install create-react-app -g

1.2 创建react应用

create-react-app myapp

2 安装electron

2.1 在同级目录下安装electron

npm install electron --save-dev

2.2  添加electron启动文件main.js

在package.json同级目录下新建main.js,这里使用electron官网的配置,loadFile的目录需要修改

main.js内容如下,关于配置项官网有详细介绍,查看官网示例

// Modules to control application life and create native browser window
const {app, BrowserWindow} = require('electron')
const path = require('path')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow = null;

function createWindow () {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 900,
    height: 600,
    // transparent: true, // 透明窗口
    opacity: 1,  // 全局透明度
    frame: true,  // 隐藏所有操作栏
    webPreferences: {
        // 起用预加载文件,此文件可以使用node以及访问其余目录
        // preload: path.join(__dirname, 'public/render.js')
    }
  })
  // and load the index.html of the app.
  
  // 打包时使用
  // mainWindow.loadFile('./build/index.html') 

  // 本地调试时使用
  mainWindow.loadURL('http://localhost:3000/')

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on('closed', function () {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') app.quit()
})

app.on('activate', function () {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (mainWindow === null) createWindow()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

2.3  更改package.json,为electron添加入口

有三个地方需要更改,如下其中 "electron-start": "electron ." 是为了快捷启动electron的脚本

{
  "name": "electron-react",
  "version": "0.1.0",
  "private": true,
  "main": "main.js", // 指定main.js
  "homepage": ".",  // 添加homepage路径
  "dependencies": {
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-scripts": "3.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "electron-start": "electron ."  // 用于调试启动electron
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "electron": "^5.0.8",
    "electron-packager": "^14.0.3"
  }
}

3 本地启动项目

main.js文件中 mainWindow.loadURL('http://localhost:3000/') 是指将3000端口*(react默认端口)内存中的html文件载入electron执行,所以为了保证运行,需要先运行react,再执行electron

3.1 启动react

npm start

3.2 启动electron 

成功启动react后,本地浏览器3000端口会看到react默认启动页,此时不要关闭react服务器,再新建终端执行

npm run electron-start

执行成功后会自动开启新应用窗口

Electron+React的互相通信并构建本地exe应用_第1张图片

此时已经可以进行初步开发了,react的热部署也支持在该窗口中实时刷新 


4 打包生成本地应用

使用electron最重要的功能就是可以跨平台生成native应用,并且使用本地GUI中诸多特性

为了方便了解electron打包流程,这里提供了一个其余作者写的 参考教程 

由于引入了MV*框架生成了单HTML页面应用,那么如何进行打包呢?

4.1 react打包应用

这里我们只需要稍稍修改main.js,将载入的html文件换成build文件夹里面的入口html

需要注意的是 ./build/index.html 文件路径是相对于main.js,必须填对路径,否则electron打包会一直卡住并出错


mainWindow.loadFile('./build/index.html')
// mainWindow.loadURL('http://localhost:3000/')

由于是单页面应用,故所以需要先打包react应用

npm build

4.2 使用electron-packager打包electron应用

electron-packager是官方提供的打包工具,可以将electron应用打包成各个平台的本地程序

首先安装, 关于详细介绍和配置,参考官方 https://github.com/electron/electron-packager

npm install electron-packager --save-dev

由于4.1中已经生成了html文件,然后就可以直接进行打包,给出一个生成exe x64程序的示例

electron-packager . HelloWorld --platform=win32 --arch=x64 --out=./out --asar --app-version=0.0.1

打包可能需要等待几分钟时间,提示成功后,会在package.json同级下出现 out 文件夹,进入找到 .exe 打开即可成功执行


5 系统GUI和React程序之间的互相通信

electron强大之处在于同时拥有js迅速开发的优点和跨平台系统程序的特性

故如若不使用本地程序GUI的功能,则仅仅只是react应用套了一层壳而已,失去了灵魂

在electron中,本地程序GUI相关(main.js)称为主进程,载入的html文件(react等相关)称为渲染进程,官网有如下介绍

5.1 Electron中主进程与渲染进程

Electron 运行 package.json 的 main 脚本的进程被称为主进程。 在主进程中运行的脚本通过创建web页面来展示用户界面。 一个 Electron 应用总是有且只有一个主进程。

由于 Electron 使用了 Chromium 来展示 web 页面,所以 Chromium 的多进程架构也被使用到。 每个 Electron 中的 web 页面运行在它自己的渲染进程中。

5.2 主进程与渲染进程的区别

主进程使用 BrowserWindow 实例创建页面。 每个 BrowserWindow 实例都在自己的渲染进程里运行页面。 当一个 BrowserWindow 实例被销毁后,相应的渲染进程也会被终止。

在页面中调用与 GUI 相关的原生 API 是不被允许的,因为在 web 页面里操作原生的 GUI 资源是非常危险的,而且容易造成资源泄露。 如果你想在 web 页面里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。

Electron为主进程( main process)和渲染器进程(renderer processes)通信提供了多种实现方式,如可以使用ipcRenderer 和 ipcMain模块发送消息,使用 remote模块进行RPC方式通信 

这里使用remote举例通信

5.3 新建和编写render.js

还记得main.js中预加载了一个render.js文件

// main.js
webPreferences: {
     // preload: path.join(__dirname, 'public/render.js')
}

现在可以取消注释,在public目录下新建render.js(当然可以任意命名)

此文件可以访问任何 nodeAPI 以及 electron 中的实例对象,首先我们把electron挂载至node的global上

// render.js
global.electron = require('electron')

// 随便写两个官方例子测试下
exports.widthRendererCallback = mapper => {
  return [1,2,3].map(mapper);
}
exports.withLocalCallback = () => {
  return [1,2,3].map(x => x + 1);
}

 electron能够将global上的对象映射到main.js loadFile('./build/index.html') 所加载的 HTML 中的 window

5.4 在React中访问Electron

由于此时window中已有实例

在react任何文件中,都可以非常轻松的访问到electron,如App.js如下

import React from 'react';
import logo from './logo.svg';
import './App.css';

const electron = window.electron;

const { remote } = electron
const { BrowserWindow } = remote

const mapNumbers = remote.require('./public/render.js');
const widthRendererCb = mapNumbers.widthRendererCallback(x => x + 1);
const widthLocalCb = mapNumbers.withLocalCallback();

// 获取当前窗口,设置系统GUI进度条
remote.getCurrentWindow().setProgressBar(0.6);

let myNotification = new Notification('标题', {
  body: 'React触发应用更新'
})
myNotification.onclick = () => {
  
}

function App() {
  return (
    
logo

Edit src/App.js and save to reload.

Learn React
); } export default App;

使用remote模块进行RPC方式通信

remote可以使用nodejsAPI来requre获取src目录外部文件,也可以获取属于electron中的实例

你可能感兴趣的:(Electron+React的互相通信并构建本地exe应用)