0%

桌面應用 Electron React Quickstart

之前曾經使用 NW.js 與 Vue.js 開發一套可離線應用的桌面應用程序,資料存在客戶端的 localStorege 中,這裡我則用 Electron 搭配 React 建立了一個基本的開發架構,有興趣使用此架構的同仁可以很快的開始。

這裡客戶端我使用 React 程式庫取代了 Vue.js,因為我可以用同樣的 React 元件,透過 React Native 就可打造原生移動裝置的應用程序。React Native 僅僅需要使用一種編程語言 JavaScript ,同一個開發團隊就可以建構 Android、iOS 應用程序。其他的跨平台解決方案 PhoneGap、Cordova 和 Ionic 雖然也是可行,但在用戶體驗與性能上表現卻不佳。React Native 的亮點就在於它的性能無損,與使用 Objective-C、Swift 或 Java 建構的原生移動應用程序相比,性能沒有明顯的差異。

近年來,原生移動開發變得越來越昂貴,因此能夠開發跨平台應用程序的工程師越來愈搶手,React Native 僅僅使用單一主流框架就可以開發桌面、Web 以及移動應用程序,值得讓我們思考與組織架構的調整。開發團隊使用同一種技術可以簡化協同合作和共享,因此生產力也會提升。

這裡我們則先用 Electron 與 React 來建立桌上型應用程序。除了基本的架構範例可以作為專案的開始範本,我也建立了一個基本的展示範例,除了使用 Electron、React 外加 IndexDB 將資料存在客戶端。我將此兩個範例都打包成 git Repository,因此你除了要安裝 Visual Studio Code 外,也需要安裝 Git。

  • electron-react-quickstart.git
  • electron-react-idb-sample.git

這裡就從第一個基本架構開始。

Quick Start

首先用 Git 複製範例,打開 Visual Studio Code, 開啟一個終端螢幕,我電腦的 Z: 是指向 XXX111(\XXXXXFS01)。

I:\u01\projects>git clone Z:\DBA\Git\Depot\electron-react-quickstart.git

然後用 Visual Studio Code 開啟目錄 electron-react-quickstart 開始安裝程式庫。這裡我要用 Yarn 來取代 npm 管理 JavaScript 套件。 Yarn 是 Facebook 與 Exponent、 Google、Tilde 所合作開發的套件管理工具,其功能與 npm 相同,但 npm 最為人詬病的是安裝速度很慢、安裝相依套件沒有順序性,而 Yarn 不但解決了這些問題,還加入了方便的 Cache 機制使套件均只要下載一次,多個專案若使用相同套件時不必重新下載。 官方也表示 Yarn 是快速、安全、可靠的,且能支援多系統。

所以首先要先安裝 Yarn。可以測試一下:

I:\u01\projects\electron-react-quickstart>yarn --version
1.22.4

在你的 electron-react-quickstart 目錄下開始安裝所需要的程式庫:

I:\u01\projects\electron-react-quickstart>yarn install 

安裝完成就可以開啟應用程序:

I:\u01\projects\electron-react-quickstart>yarn start 

這裡是基本應用程序的畫面:

以下則是專案的目錄結構:

這個專案的程式碼都在 src 目錄中。使用 webpack 為開發及打包工具。簡單的來看看這幾段程式碼,可以從 package.json 的設定找到這個專案程序的入口點:

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "fire-sale",
"productName": "FireSale",
"version": "1.0.0",
"description": "My Electron application description",
"main": ".webpack/main",
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"publish": "electron-forge publish",
"lint": "echo \"No linting configured\""
},
...

第 6 行 “main”: “.webpack/main” 是這個專案程序的入口點,但這是經過 webpack 轉譯後的入口點,真正的入口點是對照到 src 目錄下的 main.js 程式檔。

package.json
54
55
56
57
58
59
60
"entryPoints": [
{
"html": "./src/index.html",
"js": "./src/renderer.js",
"name": "main_window"
}
]

package.json 的第 56,57 行則是 Electron 的習慣性基本的命名程式檔,簡單的看一下 Electron 的基本架構:

The main process

Main process 有幾個重要職責。它可以回應應用程序生命週期事件,例如啟動,退出,準備退出,進入後台,進入前台等等,Main process 還負責與本機操作系統 API 進行通信。 如果要顯示一個對話框以打開或保存文件,就得從 Main process 進行操作。

Renderer processes

Main process 可以使用 Electron 的 Browser-Window 模塊創建和銷毀 Renderer process。 Renderer process 可以加載網頁以顯示客戶端的 GUI。 每個進程都利用 Chronium 的多進程架構,並在其自己的線程(Thread)上運行。 然後,這些頁面可以加載其他 JavaScript 文件並在此線程中執行代碼。 與普通網頁不同,您可以從 Renderer process 訪問所有 Node API,從而可以使用本機模塊和較低級別的系統功能溝通。

main.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
36
37
38
39
const { app, BrowserWindow } = require('electron');
const path = require('path');

if (require('electron-squirrel-startup')) {
app.quit();
}

const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true
},
show: false,
});

mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);

mainWindow.once('ready-to-show', () => {
mainWindow.show();
});

// mainWindow.webContents.openDevTools();
};

app.on('ready', createWindow);

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

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

第 18 行的 MAIN_WINDOW_WEBPACK_ENTRY 是 webpack 特殊的全域變數,預設值是在前面 package.json 中設定的 “./src/index.html”,index.html 則會自動載入 “./src/renderer.js”

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello Tainan!</title>

</head>
<body>
<div id="application">
<div class="loading">Loading…</div>
</div>
</body>
</html>
renderer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';
import { render } from 'react-dom';
import './index.css';

render(
<div>
<h1>💖 Hello World! Hello 台南!!</h1>
<p>Welcome to your Electron application.</p>
</div>,
document.getElementById('application')
);

console.log('👋 此消息使用 webpack 透過 "renderer.js" 發出 👀');

這裡使用 React 與 ReactDOM 來渲染客戶端的 GUI,這裡使用 render 方法創建客戶端的 UI,其中使用了 React 的 JSX。JSX 是 JavaScript 的語法擴展,與 XML 有點類似。雖然不用 JSX 也可以建構 React 組件,但是 JSX 能夠使 React 更易於閱讀和維護。JSX 初看可能有些奇怪,但功能非常強大,深得廣大開發者的青睞。

第 3 行使用 import 直接載入 css 檔案,這是因為透過 webpack 的轉譯,可以不用透過 html 的 link 標籤,webpack 會將 css 直接轉譯為 javascript,最終則與其它程式檔打包成為一個單一的程式檔,這通常會命名為 bundle.js。

最後我們設計的應用程序必須打包讓使用者更容易安裝使用,webpack 將負責此工作。

I:\u01\projects\electron-react-quickstart>yarn run package

完成後在專案目錄下會產生一個 out 子目錄:

可以從檔案總管中直接執行 FireSale.exe,這個執行檔名稱也是在 package.json 中 “productName”: “FireSale” 定義的:

因為這是 Electron 應用程序,沒有 Web 伺服器,所以你必須將整個目錄打包成 zip 檔,讓使用下載解壓縮後直接執行 exe 檔。

下次再來探索另外一個範例 electron-react-idb-sample.git,將可以學到更多的 Electron 與 React。並看看如何將資料儲存在客戶端。

祝 Coding 愉快,身體健康。