🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 开发环境配置(React + Electron) ## React + Electron 环境搭建 ① 安装 React:`npx create-react-app projName` ② 安装 electron:`cnpm install electron --save-dev`(用 npm 可能会卡住) ③ 运行:首先`npm start`运行 React,然后`npm run dev`使用 Electron 加载 localhost3000,需要配置一下 package.json(不使用其他工具的话需要开两个命令行窗口) ## 其他插件: 1)**Devtron**:便于调试 electron 项目 ``` # Install Devtron $ npm instsall --save-dev devtron // Run the following from the Console tab of your app's Devtools(main.js e.g) require('devtron').install() // You should now see a Devtron added to the DevTools ``` 2)**Concurrently**:跨平台同时运行多个命令。 [https://www.npmjs.com/package/concurrently](https://www.npmjs.com/package/concurrently) `npm install concurrently --save-dev` ![](https://img.kancloud.cn/aa/89/aa890901fd59bfeb96d142a5ede5b558_571x177.png =400x) 3)**wait-on**:用于使 Electron 在 React 加载完成后再启动。 [https://www.npmjs.com/package/wait-on](https://www.npmjs.com/package/wait-on) `npm install wait-on --save-dev` 例如: ``` wait-on http://localhost:3000 && electron . ``` 表示等待 localhost:3000 端口启动后(available)再启动 electron 4)**cross-env**:用于解决不同操作系统环境变量名称不同的问题 ``` "dev": "concurrently \"wait-on http://localhost:3000 && electron . \" \"corss-env BROWSER=none npm start\"" ``` 表示运行 npm start 时使用 BROWSER 这个环境变量,值为 none(React 提供,表示启动时不打开浏览器 tab),如果不这么做每次启动 electron 会在浏览器打开一个 tab。 5)**electron-is-dev**:判断 electron 开发环境的插件: `npm install electron-is-dev --save-dev` 使用: ``` const isDev = require('electron-is-dev') app.on('ready', () => { mainWindow = new BrowserWindow({ ... }) const urlLocation = isDev ? 'http://localhost:3000' : 'productURL' mainWindow.loadURL(urlLocation) }) ``` 目前为止 packjage.json 如下: ``` { "name": "cloud-doc", "version": "0.1.0", "main": "main.js", "private": true, "dependencies": { "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", "react": "^16.13.1", "react-dom": "^16.13.1", "react-scripts": "3.4.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "dev": "concurrently \"wait-on http://localhost:3000 && electron .\" \"cross-env BROWSER=none npm start\"" }, "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": { "concurrently": "^5.2.0", "cross-env": "^7.0.2", "devtron": "^1.4.0", "electron": "^9.0.0", "electron-is-dev": "^1.2.0", "wait-on": "^5.0.0" } } ``` # 基础概念 ## Electron 应用架构 ### 主进程与渲染进程 Electron 运行`package.json`的`main`脚本的进程被称为**主进程**。 在主进程中运行的脚本通过创建 web 页面来展示用户界面。 一个 Electron 应用总是有且只有一个主进程。 <br/> 由于 Electron 使用了 Chromium 来展示 web 页面,所以 Chromium 的多进程架构也被使用到。 每个 Electron 中的 web 页面运行在它的叫**渲染进程**的进程中。 ![](https://img.kancloud.cn/5e/af/5eaf27eeaf241ddb3f204b13b6664a1b_790x487.png) 上图是 Chrome 多进程架构的简图,简单来说,Main Process 负责 Renderer Process 的创建与管理,浏览器中的每个 tab 就对应于一个 Renderer Process。这样设计有很多好处,例如即使单个 tab 崩溃了也不会导致整个 Chrome 崩溃。 > 关于 Chrome 为何使用多进程而不是多线程,可以参考 [知乎——Chrome 为何使用多进程而不是多线程?](https://www.zhihu.com/question/368712837?utm_source=wechat_timeline) <br/> 在普通的浏览器中,web 页面通常在沙盒环境中运行,并且无法访问操作系统的原生资源。 然而 Electron 的用户在 Node.js 的 API 支持下可以在页面中和操作系统进行一些底层交互。 ### 主进程与渲染进程的区别 主进程使用`BrowserWindow`实例创建页面。 每个`BrowserWindow`实例都在自己的渲染进程里运行页面。 当一个`BrowserWindow`实例被销毁后,相应的渲染进程也会被终止。 <br/> 主进程管理所有的 web 页面和它们对应的渲染进程。 每个渲染进程都是独立的,它只关心它所运行的 web 页面。 <br/> 在页面中调用与 GUI 相关的原生 API 是不被允许的,因为在 web 页面里操作原生的 GUI 资源是非常危险的,而且容易造成资源泄露。 <span style="color: red;">如果你想在 web 页面里使用 GUI 操作,其对应的渲染进程必须与主进程进行通讯,请求主进程进行相关的 GUI 操作。</span> 概括来说,主进程有如下特点: - 可以使用和系统对接的 Electron API,如创建菜单,上传文件等 - 可以创建渲染进程 - 全面支持 Node.js - 有且只有一个,作为整个程序的入口点 渲染进程有如下特点: - 可以有多个,每个对应一个独立的窗口 - 每一个都是一个单独的进程 - 全面支持 Node.js 和 DOM API - 可以使用一部分 Electron 提供的 API ![](https://img.kancloud.cn/ac/44/ac44ea0d3fce7768f3515e6e72bdb4be_900x578.png) ### 主进程与渲染进程间通讯 1)使用 IPC 模块进行通讯 ``` // 在主进程中. const { ipcMain } = require('electron') ipcMain.on('asynchronous-message', (event, arg) => { console.log(arg) // prints "ping" event.reply('asynchronous-reply', 'pong') // send message back }) ipcMain.on('synchronous-message', (event, arg) => { console.log(arg) // prints "ping" event.returnValue = 'pong' }) ``` ``` //在渲染器进程 (网页) 中。 const { ipcRenderer } = require('electron') console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong" ipcRenderer.on('asynchronous-reply', (event, arg) => { console.log(arg) // prints "pong" }) ipcRenderer.send('asynchronous-message', 'ping') ``` 2)使用 remote 模块进行通讯 使用`remote`模块, 可以调用 main 进程对象的方法, 而不必显式发送进程间消息。 例如:从渲染进程创建浏览器窗口 ``` const { BrowserWindow } = require('electron').remote let win = new BrowserWindow({ width: 800, height: 600 }) win.loadURL('https://github.com') ``` 因为安全原因,remote 模块能在以下几种情况下被禁用: * [`BrowserWindow`](https://www.electronjs.org/docs/api/browser-window)\- 通过设置`enableRemoteModule`选项为`false`。 * [`<webview>`](https://www.electronjs.org/docs/api/webview-tag)\- 通过把`enableremotemodule`属性设置成`false`。