6 changed files with 426 additions and 11 deletions
@ -0,0 +1,407 @@ |
|||||||
|
--- |
||||||
|
title: "使用Electron+React开发跨平台桌面应用" |
||||||
|
date: 2019-07-29T19:59:59+08:00 |
||||||
|
draft: false |
||||||
|
toc: true |
||||||
|
tags: [electron, react] |
||||||
|
categories: web |
||||||
|
images: ["img/electron.png"] |
||||||
|
--- |
||||||
|
|
||||||
|
使用Electron开发跨平台桌面应用,已经被越来越多的人接受。从开发者的角度,以前需要很多代码才能做到的自动布局、脏区裁剪、图像栅格化、GPU加速,现在通通不用管了,即使要处理,也是几行代码的事;从企业角度,前端开发人员众多,比较容易招聘,另外,资深前端相对于资深C++客户端,薪资还是有差距的,也节省了不少成本。本文主要着重说明使用Electron+React搭建开发环境的步骤。 |
||||||
|
|
||||||
|
<!--more--> |
||||||
|
|
||||||
|
## 技术栈 |
||||||
|
|
||||||
|
- [**Electron**](https://electronjs.org/) |
||||||
|
- [**React - Create React App**](https://github.com/facebook/create-react-app) |
||||||
|
- [**Rescripts**](https://github.com/harrysolovay/rescripts) |
||||||
|
- [**Electron Builder**](https://github.com/electron-userland/electron-builder) |
||||||
|
|
||||||
|
## 开发环境设置 |
||||||
|
|
||||||
|
创建一个新的React项目 |
||||||
|
|
||||||
|
```shell |
||||||
|
npx create-react-app web-designer-test |
||||||
|
cd web-designer-test |
||||||
|
``` |
||||||
|
|
||||||
|
添加依赖库 |
||||||
|
|
||||||
|
```shell |
||||||
|
yarn add electron wait-on concurrently --dev |
||||||
|
yarn add electron-is-dev |
||||||
|
``` |
||||||
|
|
||||||
|
- electron 用于界面开发的核心框架 |
||||||
|
- electron-builder 用于构建安装包 |
||||||
|
- wait-on concurrently 由于electron需要在react启动之后启动,所以增加这两个库用于同步进程启动顺序 |
||||||
|
- electron-is-dev 判断当前运行环境 |
||||||
|
|
||||||
|
创建文件`public/main.js` |
||||||
|
|
||||||
|
```js |
||||||
|
const electron = require('electron'); |
||||||
|
const app = electron.app; |
||||||
|
const BrowserWindow = electron.BrowserWindow; |
||||||
|
|
||||||
|
const path = require('path'); |
||||||
|
const isDev = require('electron-is-dev'); |
||||||
|
|
||||||
|
let mainWindow; |
||||||
|
|
||||||
|
function createWindow() { |
||||||
|
mainWindow = new BrowserWindow({width: 900, height: 680}); |
||||||
|
mainWindow.loadURL(isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`); |
||||||
|
if (isDev) { |
||||||
|
// Open the DevTools. |
||||||
|
//BrowserWindow.addDevToolsExtension('<location to your react chrome extension>'); |
||||||
|
mainWindow.webContents.openDevTools(); |
||||||
|
} |
||||||
|
mainWindow.on('closed', () => mainWindow = null); |
||||||
|
} |
||||||
|
|
||||||
|
app.on('ready', createWindow); |
||||||
|
|
||||||
|
app.on('window-all-closed', () => { |
||||||
|
if (process.platform !== 'darwin') { |
||||||
|
app.quit(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
app.on('activate', () => { |
||||||
|
if (mainWindow === null) { |
||||||
|
createWindow(); |
||||||
|
} |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
增加下面的命令到`package.json`文件中的`scripts`标签内 |
||||||
|
|
||||||
|
```js |
||||||
|
"electron-dev": "concurrently \"yarn start\" \"wait-on http://localhost:3000 && electron .\"" |
||||||
|
``` |
||||||
|
|
||||||
|
增加入口文件到`package.json`文件中 |
||||||
|
|
||||||
|
```javascript |
||||||
|
"main": "public/main.js" |
||||||
|
``` |
||||||
|
|
||||||
|
到现在为止,`package.json`文件应该类似于下面这样 |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"name": "web-designer-test", |
||||||
|
"version": "0.1.0", |
||||||
|
"private": true, |
||||||
|
"dependencies": { |
||||||
|
"electron-is-dev": "^1.1.0", |
||||||
|
"react": "^16.8.7", |
||||||
|
"react-dom": "^16.8.6", |
||||||
|
"react-scripts": "3.0.1" |
||||||
|
}, |
||||||
|
"main": "public/main.js", |
||||||
|
"scripts": { |
||||||
|
"start": "rescripts start", |
||||||
|
"build": "rescripts build", |
||||||
|
"test": "rescripts test", |
||||||
|
"eject": "react-scripts eject", |
||||||
|
"electron-dev": "concurrently \"yarn start\" \"wait-on http://localhost:3000 && 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": { |
||||||
|
"concurrently": "^4.1.1", |
||||||
|
"electron": "^5.0.8", |
||||||
|
"wait-on": "^3.3.0" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
``` |
||||||
|
|
||||||
|
为了开发环境下,不启动默认浏览器,需要设置`BROWSER`环境变量,在根目录创建`.env`文件 |
||||||
|
|
||||||
|
```ini |
||||||
|
BROWSER=non |
||||||
|
``` |
||||||
|
|
||||||
|
这个时候可以使用下面命令运行程序 |
||||||
|
|
||||||
|
```shell |
||||||
|
yarn electron-dev |
||||||
|
``` |
||||||
|
|
||||||
|
如果在新的程序窗口内出现了React的欢迎页,则表示一切已经准备OK。但是现在JavaScript的运行环境是浏览器,无法访问宿主机的资源,比如读取文件或注册表,所以需要切换到Node.js环境,使用`electron-renderer`作为[Webpack target](https://webpack.js.org/configuration/target/),我们使用`Rescripts`来处理这个问题。 |
||||||
|
|
||||||
|
安装依赖库 |
||||||
|
|
||||||
|
```shell |
||||||
|
yarn add @rescripts/cli @rescripts/rescript-env --dev |
||||||
|
``` |
||||||
|
|
||||||
|
修改`package.json`文件中`scripts`标签内的启动脚本 |
||||||
|
|
||||||
|
```json |
||||||
|
"start": "react-scripts start", |
||||||
|
"build": "react-scripts build", |
||||||
|
"test": "react-scripts test", |
||||||
|
``` |
||||||
|
|
||||||
|
修改为 |
||||||
|
|
||||||
|
```json |
||||||
|
"start": "rescripts start", |
||||||
|
"build": "rescripts build", |
||||||
|
"test": "rescripts test", |
||||||
|
``` |
||||||
|
|
||||||
|
在根目录新建文件`.rescriptsrc.js` |
||||||
|
|
||||||
|
```javascript |
||||||
|
module.exports = [require.resolve('./.webpack.config.js')] |
||||||
|
``` |
||||||
|
|
||||||
|
最后,在根目录新建另一个文件`.webpack.config.js` |
||||||
|
|
||||||
|
```javascript |
||||||
|
// define child rescript |
||||||
|
module.exports = config => { |
||||||
|
config.target = 'electron-renderer'; |
||||||
|
return config; |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
现在就切换到了Node.js运行环境,可以随意访问主机资源了。 |
||||||
|
|
||||||
|
## 打包环境设置 |
||||||
|
|
||||||
|
首先,要添加依赖库 |
||||||
|
|
||||||
|
```shell |
||||||
|
yarn add electron-builder --dev |
||||||
|
``` |
||||||
|
|
||||||
|
CRA(**C**reate **R**eactive **A**pplication)默认创建的`index.html`,会使用绝对路径来访问资源,在Electron中会加载资源失败,所以需要修改配置,在`package.json`中增加`homepage`属性 |
||||||
|
|
||||||
|
```json |
||||||
|
"homepage": "./", |
||||||
|
``` |
||||||
|
|
||||||
|
接下来添加打包命令,在`package.json`中的`scripts`标签中 |
||||||
|
|
||||||
|
```json |
||||||
|
"postinstall": "electron-builder install-app-deps", |
||||||
|
"preelectron-pack": "yarn build", |
||||||
|
"electron-pack": "electron-builder -mw" |
||||||
|
``` |
||||||
|
|
||||||
|
- `"postinstall": "electron-builder install-app-deps"`用于确保本地依赖库都已经安装 |
||||||
|
- `"preelectron-pack": "yarn build"`会保证在打包前构建应用 |
||||||
|
- `"electron-pack": "electron-builder -mw"`会为Mac(m)和Windows(w)平台进行App打包 |
||||||
|
|
||||||
|
在执行打包命令前,还需要设置打包参数,在`package.json`中添加如下信息 |
||||||
|
|
||||||
|
```json |
||||||
|
"author": { |
||||||
|
"name": "lniwn", |
||||||
|
"email": "[email protected]", |
||||||
|
"url": "https://oaoa.me" |
||||||
|
}, |
||||||
|
"build": { |
||||||
|
"appId": "me.oaoa.web-designer-test", |
||||||
|
"productName": "WebDesignerTest", |
||||||
|
"copyright": "Copyright © 2019 ${author}", |
||||||
|
"mac": { |
||||||
|
"category": "public.productivity.utilities" |
||||||
|
}, |
||||||
|
"win": { |
||||||
|
"icon": "assets/icon.png", |
||||||
|
"target": "nsis" |
||||||
|
}, |
||||||
|
"nsis": { |
||||||
|
"allowToChangeInstallationDirectory": true, |
||||||
|
"allowElevation": false, |
||||||
|
"createDesktopShortcut": true, |
||||||
|
"menuCategory": true, |
||||||
|
"oneClick": false |
||||||
|
}, |
||||||
|
"files": [ |
||||||
|
"build/**/*", |
||||||
|
"node_modules/**/*" |
||||||
|
], |
||||||
|
"directories": { |
||||||
|
"buildResources": "assets" |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
可以在[这里](https://www.electron.build/configuration/configuration)查看Electron Builder的所有选项。 |
||||||
|
|
||||||
|
创建一个assets文件夹,用于存放图片资源。 |
||||||
|
|
||||||
|
最好在一个平台只构建当前平台的可执行程序,这里针对Windows平台的构建进行了详细设置。 |
||||||
|
|
||||||
|
最终,package.json文件内容如下: |
||||||
|
|
||||||
|
```json |
||||||
|
{ |
||||||
|
"name": "web-designer-test", |
||||||
|
"version": "0.1.0", |
||||||
|
"private": true, |
||||||
|
"dependencies": { |
||||||
|
"electron-is-dev": "^1.1.0", |
||||||
|
"react": "^16.8.6", |
||||||
|
"react-dom": "^16.8.6", |
||||||
|
"react-scripts": "3.0.1" |
||||||
|
}, |
||||||
|
"main": "public/main.js", |
||||||
|
"homepage": "./", |
||||||
|
"description": "Web页面设计器", |
||||||
|
"scripts": { |
||||||
|
"start": "rescripts start", |
||||||
|
"build": "rescripts build", |
||||||
|
"test": "rescripts test", |
||||||
|
"eject": "react-scripts eject", |
||||||
|
"electron-dev": "concurrently \"yarn start\" \"wait-on http://localhost:3000 && electron .\"", |
||||||
|
"postinstall": "electron-builder install-app-deps", |
||||||
|
"preelectron-pack": "yarn build", |
||||||
|
"electron-pack": "electron-builder -mw", |
||||||
|
"electron-pack-win": "electron-builder -c.extraMetadata.main=build/main.js --win --x64" |
||||||
|
}, |
||||||
|
"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": { |
||||||
|
"@rescripts/cli": "^0.0.11", |
||||||
|
"@rescripts/rescript-env": "^0.0.10", |
||||||
|
"concurrently": "^4.1.1", |
||||||
|
"electron": "^5.0.8", |
||||||
|
"electron-builder": "^21.1.5", |
||||||
|
"wait-on": "^3.3.0" |
||||||
|
}, |
||||||
|
"author": { |
||||||
|
"name": "lniwn", |
||||||
|
"email": "[email protected]", |
||||||
|
"url": "https://oaoa.me" |
||||||
|
}, |
||||||
|
"build": { |
||||||
|
"appId": "me.oaoa.web-designer-test", |
||||||
|
"productName": "WebDesignerTest", |
||||||
|
"copyright": "Copyright © 2019 ${author}", |
||||||
|
"mac": { |
||||||
|
"category": "public.productivity.utilities" |
||||||
|
}, |
||||||
|
"win": { |
||||||
|
"icon": "assets/icon.png", |
||||||
|
"target": "nsis" |
||||||
|
}, |
||||||
|
"nsis": { |
||||||
|
"allowToChangeInstallationDirectory": true, |
||||||
|
"allowElevation": false, |
||||||
|
"createDesktopShortcut": true, |
||||||
|
"menuCategory": true, |
||||||
|
"oneClick": false |
||||||
|
}, |
||||||
|
"files": [ |
||||||
|
"build/**/*", |
||||||
|
"node_modules/**/*" |
||||||
|
], |
||||||
|
"directories": { |
||||||
|
"buildResources": "assets" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
``` |
||||||
|
|
||||||
|
由于Electron默认入口文件为`build/Electron.js`,如果想自定义,需要在参数中指定入口文件,否则编译会报错 |
||||||
|
|
||||||
|
`"electron-pack-win": "electron-builder -c.extraMetadata.main=build/main.js --win --x64"` |
||||||
|
|
||||||
|
最后,执行打包命令 |
||||||
|
|
||||||
|
```shell |
||||||
|
yarn electron-pack-win |
||||||
|
``` |
||||||
|
|
||||||
|
会在dist文件夹下生成对应安装包 |
||||||
|
|
||||||
|
文件夹结构如下: |
||||||
|
|
||||||
|
```txt |
||||||
|
F:\Project\electron\web-designer-test |
||||||
|
├.env |
||||||
|
├.gitignore |
||||||
|
├.rescriptsrc.js |
||||||
|
├.webpack.config.js |
||||||
|
├assets |
||||||
|
│ ├icon.png |
||||||
|
├build |
||||||
|
│ ├asset-manifest.json |
||||||
|
│ ├favicon.ico |
||||||
|
│ ├index.html |
||||||
|
│ ├main.js |
||||||
|
│ ├manifest.json |
||||||
|
│ ├precache-manifest.3ef21b1d6090801b808aaff5b52f1a17.js |
||||||
|
│ ├service-worker.js |
||||||
|
│ ├static |
||||||
|
├dist |
||||||
|
│ ├.icon-ico |
||||||
|
│ ├builder-effective-config.yaml |
||||||
|
│ ├WebDesignerTest Setup 0.1.0.exe |
||||||
|
│ ├WebDesignerTest Setup 0.1.0.exe.blockmap |
||||||
|
│ ├win-unpacked |
||||||
|
├node_modules |
||||||
|
├package.json |
||||||
|
├public |
||||||
|
│ ├favicon.ico |
||||||
|
│ ├index.html |
||||||
|
│ ├main.js |
||||||
|
│ ├manifest.json |
||||||
|
├README.md |
||||||
|
├src |
||||||
|
│ ├App.css |
||||||
|
│ ├App.js |
||||||
|
│ ├App.test.js |
||||||
|
│ ├index.css |
||||||
|
│ ├index.js |
||||||
|
│ ├logo.svg |
||||||
|
│ ├serviceWorker.js |
||||||
|
├yarn.lock |
||||||
|
|
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*参考文档* |
||||||
|
|
||||||
|
- https://www.codementor.io/randyfindley/how-to-build-an-electron-app-using-create-react-app-and-electron-builder-ss1k0sfer |
||||||
|
- https://juejin.im/post/5c356a396fb9a049e30848f0 |
||||||
Loading…
Reference in new issue