0%

[Node.js] 基於 React 的 Electron 開發環境建立與打包

前言

Node.js 盛行的年代,使用 JavaScript 開發各種應用程式不在話下,隨著 Electron 套件的推出,讓前端工程師不須學習額外語言,即可撰寫跨平台桌面應用程式。除此之外,由於 Electron 桌面應用程式是使用 html/css/js 來建立,所以我們可以整合其他前端框架 (React/Vue/Angular 等) 作為應用程式 UI 開發工具。這裡要分享的是如何建立基於 React 的 Electron 開發環境,並詳細記載最重要的打包步驟。

React + Electron 實際專案

環境

  • node 14.16.1

建立開發環境

CRA

首先建立 React 專案 (CRA, Create React App)

1
npx create-react-app react-based-electron
  • npx:使用 create-react-app 套件,但不將該套件下載到本地端。
  • react-based-electron:此為 CRA 的專案名稱,可任意更改。

Electron 相關套件

安裝開發 Electron 桌面應用程式時所需套件

1
2
3
4
5
cd react-based-electron

npm install --save-dev electron
npm install --save electron-is-dev
npm install --save wait-on
  • electron:為 Electron 套件本身。
  • electron-is-dev:能夠得知 Electron 應用目前運行狀態是處於開發階段還是產品階段,讓開發者能輕鬆對不同情況做邏輯分流

    1
    2
    3
    4
    5
    6
    7
    const isDev = require('electron-is-dev');

    if (isDev) {
    console.log('Running in development');
    } else {
    console.log('Running in production');
    }
  • wait-on:先等待某些服務 (files/ports/sockets/http(s)) 正常運作,再執行接下來的指令

    1
    wait-on tcp:3000 && electron .    # 直到 TCP 3000 正常運作,才會執行 electron .

main.js/preload.js

於專案根目錄建立兩個檔案,分別為 Electron 的進入點 main.js (內容如下) 與 preload.js (空白檔案)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// main.js
const { app, BrowserWindow } = require('electron')
const path = require('path')
const isDev = require('electron-is-dev');

function createWindow () {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})

if (isDev) {
// 開發階段直接與 React 連線
mainWindow.loadURL('http://localhost:3000/');
// 開啟 DevTools.
mainWindow.webContents.openDevTools()
} else {
// 產品階段直接讀取 React 打包好的
mainWindow.loadFile('./build/index.html');
}
}

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

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

入口點與啟動腳本

package.json 中加入 main 資訊與 homepage 資訊,將 Electron 入口點設為 main.js。最後加上 Electron 的啟動腳本 electron-start 與修改 CRA 的啟動腳本 start 內容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"name": "react-based-electron",
"version": "0.1.0",
"private": true,
"homepage": ".",
"main": "main.js",
"dependencies": {
...
},
"scripts": {
"start": "set BROWSER=none && react-scripts start",
...
"electron-start": "wait-on tcp:3000 && electron ."
},
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"name": "react-based-electron",
"version": "0.1.0",
"private": true,
"homepage": ".",
"main": "main.js",
"dependencies": {
...
},
"scripts": {
"start": "export BROWSER=none && react-scripts start",
...
"electron-start": "wait-on tcp:3000 && electron ."
},
...
}
  • "start"BROWSER=none 讓 CRA 啟動的時候不要跳出瀏覽器。
  • "electron-start"wait-on tcp:3000 先等待 CRA 服務啟動,再執行 electron . 啟動桌面應用程式。

運行測試

開啟兩個 cmd,一個用來啟動 CRA (npm run start),另一個則是用來啟動 Electron (npm run electron-start)。啟動沒有順序之分,因為我們專案使用 wait-on 套件協助等待 CRA 服務 (預設為 Port 3000) 運行起來,再執行 Electron 桌面應用程式

npm run start & electron-startnpm run start & electron-start

等待指令運行一小段時間,就會看到 Electron 桌面應用程式中顯示 CRA 預設畫面與瀏覽器的開發工具。到目前為止,已經正確建立基於 React 的 Electron 開發專案環境囉!接著便可以放心的開始使用 React 框架開發桌面應用了

基於 React 的 Electron 桌面應用程式運行基於 React 的 Electron 桌面應用程式運行

開啟兩個 terminal,一個用來啟動 CRA (npm run start),另一個則是用來啟動 Electron (npm run electron-start)。啟動沒有順序之分,因為我們專案使用 wait-on 套件協助等待 CRA 服務 (預設為 Port 3000) 運行起來,再執行 Electron 桌面應用程式

npm run start & electron-startnpm run start & electron-start

等待指令運行一小段時間,就會看到 Electron 桌面應用程式中顯示 CRA 預設畫面與瀏覽器的開發工具。到目前為止,已經正確建立基於 React 的 Electron 開發專案環境囉!接著便可以放心的開始使用 React 框架開發桌面應用了

基於 React 的 Electron 桌面應用程式運行基於 React 的 Electron 桌面應用程式運行

打包應用程式

當我們桌面應用程式開發完成後,下一步就是將專案打包並發佈給其他人使用,讓沒有 Node.js 開發環境的使用者也能來使用服務。

安裝套件

安裝 Electron 打包套件 electron-builder

1
npm install --save-dev electron-builder

icon.png

在根目錄放置一個格式為 png 的 Icon 並命名為 icon.png,會將其用作顯示應用程式的代表圖案,圖案大小建議至少 256x256。若讀者手邊沒有圖檔可以使用,這裡提供個人設計的 Logo,僅供練習使用

Logo (WRX, Wei-Ren Xue)Logo (WRX, Wei-Ren Xue)

build.js

於專案根目錄建立一個檔案,為 Electron 的打包配置檔 build.js,其內容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const path = require('path');
const builder = require('electron-builder');

builder.build({
projectDir: path.resolve(__dirname),
win: ['portable', 'nsis'], // portable 為 Windows 的免安裝程式,nsis 為安裝程式
config: {
'appId': 'io.github.weirenxue.react-electron-demo', // 應用程式 ID
'productName': 'React Based Electron', // 應用程式名稱
'copyright': 'Copyright © 2021 Wei-Ren Xue', // 授權宣告
'directories': {
'output': 'electron-build/win' // 打包後的應用程式放置在 electron-build/win
},
// 設定打包後的 icon
'win': {
'icon': path.resolve(__dirname, 'icon.png'),
},
// 打包需要用到的原始碼、模組,皆需要寫到 files 內
'files': [
'build/**/*',
'node_modules/**/*',
'package.json',
'main.js',
'preload.js',
],
'extends': null,
}
}).then(
(data) => console.log(data),
(err) => console.error(err)
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const path = require('path');
const builder = require('electron-builder');

builder.build({
projectDir: path.resolve(__dirname),
win: ['portable', 'nsis'], // portable 為 Windows 的免安裝程式,nsis 為安裝程式
mac: ['dmg'], // dmg 為 Mac 常見的打包方式,若開發環境為 Windows 無法打包,需註解掉,否則會出錯
config: {
'appId': 'io.github.weirenxue.react-electron-demo', // 應用程式 ID
'productName': 'React Based Electron', // 應用程式名稱
'copyright': 'Copyright © 2021 Wei-Ren Xue', // 授權宣告
'directories': {
'output': 'electron-build/win' // 打包後的應用程式放置在 electron-build/win
},
// 設定打包後的 icon
'win': {
'icon': path.resolve(__dirname, 'icon.png'),
},
'mac': {
'icon': path.resolve(__dirname, 'icon.png'),
},
// 打包需要用到的原始碼、模組,皆需要寫到 files 內
'files': [
'build/**/*',
'node_modules/**/*',
'package.json',
'main.js',
'preload.js',
],
'extends': null,
}
}).then(
(data) => console.log(data),
(err) => console.error(err)
)

打包腳本

package.json 加入 electron-build 打包腳本

1
2
3
4
5
6
7
8
9
{
...
"scripts": {
...
"electron-start": "wait-on tcp:3000 && electron .",
"electron-build": "npm run build && node build.js"
},
...
}
  • "electron-build"npm run build 先建置 CRA 專案,node build.js 再建置 Electron 專案。

執行打包腳本

第一次執行打包會需要較久時間,因為需要下載相關套件,請耐心等待

1
npm run electron-build

這裡提供成功打包的 Log 做為參考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
PS C:\Users\wrxue\source\nodejs\react-based-electron> npm run electron-build

> react-based-electron@0.1.0 electron-build C:\Users\wrxue\source\nodejs\react-based-electron
> npm run build && node build.js


> react-based-electron@0.1.0 build C:\Users\wrxue\source\nodejs\react-based-electron
> react-scripts build

Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

41.34 KB build\static\js\2.e86009e0.chunk.js
1.63 KB build\static\js\3.a8f12dc9.chunk.js
1.18 KB build\static\js\runtime-main.6c97c200.js
600 B build\static\js\main.6d3c4ff6.chunk.js
556 B build\static\css\main.a617e044.chunk.css

The project was built assuming it is hosted at ./.
You can control this with the homepage field in your package.json.

The build folder is ready to be deployed.

Find out more about deployment here:

https://cra.link/deployment

• electron-builder version=22.11.7 os=10.0.19042
• description is missed in the package.json appPackageFile=C:\Users\wrxue\source\nodejs\react-based-electron\package.json
• author is missed in the package.json appPackageFile=C:\Users\wrxue\source\nodejs\react-based-electron\package.json
• writing effective config file=electron-build\win\builder-effective-config.yaml
• packaging platform=win32 arch=x64 electron=13.1.7 appOutDir=electron-build\win\win-unpacked
• building target=portable file=electron-build\win\React Based Electron 0.1.0.exe archs=x64
• building target=nsis file=electron-build\win\React Based Electron Setup 0.1.0.exe archs=x64 oneClick=true perMachine=false
• building block map blockMapFile=electron-build\win\React Based Electron Setup 0.1.0.exe.blockmap
[
'C:\\Users\\wrxue\\source\\nodejs\\react-based-electron\\electron-build\\win\\React Based Electron 0.1.0.exe',
'C:\\Users\\wrxue\\source\\nodejs\\react-based-electron\\electron-build\\win\\React Based Electron Setup 0.1.0.exe.blockmap',
'C:\\Users\\wrxue\\source\\nodejs\\react-based-electron\\electron-build\\win\\React Based Electron Setup 0.1.0.exe'
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
wrxue@ip233 react-based-electron % npm run electron-build

> react-based-electron@0.1.0 electron-build /Users/wrxue/Desktop/nodejs/react-based-electron
> npm run build && node build.js


> react-based-electron@0.1.0 build /Users/wrxue/Desktop/nodejs/react-based-electron
> react-scripts build

Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

41.34 KB build/static/js/2.e86009e0.chunk.js
1.63 KB build/static/js/3.a8f12dc9.chunk.js
1.18 KB build/static/js/runtime-main.6c97c200.js
600 B build/static/js/main.6d3c4ff6.chunk.js
556 B build/static/css/main.a617e044.chunk.css

The project was built assuming it is hosted at ./.
You can control this with the homepage field in your package.json.

The build folder is ready to be deployed.

Find out more about deployment here:

https://cra.link/deployment

• electron-builder version=22.11.7 os=20.5.0
• description is missed in the package.json appPackageFile=/Users/wrxue/Desktop/nodejs/react-based-electron/package.json
• author is missed in the package.json appPackageFile=/Users/wrxue/Desktop/nodejs/react-based-electron/package.json
• writing effective config file=electron-build/win/builder-effective-config.yaml
• rebuilding native dependencies dependencies=fsevents@1.2.13, fsevents@1.2.13 platform=darwin arch=x64
• packaging platform=darwin arch=x64 electron=13.1.7 appOutDir=electron-build/win/mac
• signing file=electron-build/win/mac/React Based Electron.app identityName=Apple Development: a0981355215@gmail.com (PZRJD74VXK) identityHash=755CB8F653511BDB2E6C567A905176EE278DD9D8 provisioningProfile=none
• building target=DMG arch=x64 file=electron-build/win/React Based Electron-0.1.0.dmg
• rebuilding native dependencies dependencies=fsevents@1.2.13, fsevents@1.2.13 platform=win32 arch=x64
• packaging platform=win32 arch=x64 electron=13.1.7 appOutDir=electron-build/win/win-unpacked
• building block map blockMapFile=electron-build/win/React Based Electron-0.1.0.dmg.blockmap
• building target=portable file=electron-build/win/React Based Electron 0.1.0.exe archs=x64
• building target=nsis file=electron-build/win/React Based Electron Setup 0.1.0.exe archs=x64 oneClick=true perMachine=false
• building block map blockMapFile=electron-build/win/React Based Electron Setup 0.1.0.exe.blockmap
[
'/Users/wrxue/Desktop/nodejs/react-based-electron/electron-build/win/React Based Electron-0.1.0.dmg.blockmap',
'/Users/wrxue/Desktop/nodejs/react-based-electron/electron-build/win/React Based Electron-0.1.0.dmg',
'/Users/wrxue/Desktop/nodejs/react-based-electron/electron-build/win/React Based Electron 0.1.0.exe',
'/Users/wrxue/Desktop/nodejs/react-based-electron/electron-build/win/React Based Electron Setup 0.1.0.exe.blockmap',
'/Users/wrxue/Desktop/nodejs/react-based-electron/electron-build/win/React Based Electron Setup 0.1.0.exe'
]

查看執行檔

由於我們在 build.js 中有指定 'output': 'electron-build/win',所以 electron-builder 會將打包好的安裝檔放置於 electron-build/win 目錄下

portablensis 在 Windows 上才可以使用,portable 執行檔啟動較慢但不需安裝,nsis 則是安裝一次後便能啟動快速

打包好的安裝檔打包好的安裝檔

portablensis 在 Windows 上才可以使用,portable 的執行檔啟動較慢但不需安裝,nsis 則是安裝一次後便能啟動快速。而 dmg 是給 MacOS 使用的,開啟後將應用程式拖曳到 Applications 目錄便安裝完成

打包好的安裝檔打包好的安裝檔

原始碼

Windows MacOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
* 1338c5d (macos) 加入打包腳本
* 56b90c1 建立 build.js
* 9dc95f5 加入 icon.png
* 1398a05 安裝 electron-builder
* 6e4ed7c 入口點與啟動腳本
| * 928d60b (windows) 加入打包腳本
| * 890669a 建立 build.js
| * a98f6c3 加入 icon.png
| * 6cb08c7 安裝 electron-builder
| * b61b238 入口點與啟動腳本
|/
* 06de8dd (master) main.js/preload.js
* f9cc184 安裝 Electron 相關套件
* aad6a81 Initialize project using Create React App

在實作中遇到困難是難免的,這裡提供原始碼作為參考,若仍然無法解決歡迎至下方討論區留言。

很高興能在這裡幫助到您,歡迎登入 Liker 為我鼓掌 5 次,或者成為我的讚賞公民,鼓勵我繼續創造優質文章。
以最優質的內容回應您的鼓勵