Webpack實戰-構建 Electron 應用

浩麟發表於2017-12-24

認識 Electron

Electron 可以讓你使用開發 Web 的技術去開發跨平臺的桌面端應用,由 Github 主導和開源,大家熟悉的 Atom 和 VSCode 編輯器就是使用 Electron 開發的。

Electron 是 Node.js 和 Chromium 瀏覽器的結合體,用 Chromium 瀏覽器顯示出的 Web 頁面作為應用的 GUI,通過 Node.js 去和作業系統互動。 當你在 Electron 應用中的一個視窗操作時,實際上是在操作一個網頁。當你的操作需要通過作業系統去完成時,網頁會通過 Node.js 去和作業系統互動。

採用這種方式開發桌面端應用的優點有:

  • 降低開發門檻,只需掌握網頁開發技術和 Node.js 即可,大量的 Web 開發技術和現成庫可以複用於 Electron;
  • 由於 Chromium 瀏覽器和 Node.js 都是跨平臺的,Electron 能做到寫一份程式碼在不同的作業系統執行。

在執行 Electron 應用時,會從啟動一個主程式開始。主程式的啟動是通過 Node.js 去執行一個入口 JavaScript 檔案實現的,這個入口檔案 main.js 內容如下:

const { app, BrowserWindow } = require('electron')

// 保持一個對於 window 物件的全域性引用,如果你不這樣做,
// 當 JavaScript 物件被垃圾回收, window 會被自動地關閉
let win

// 開啟主視窗
function createWindow() {
  // 建立瀏覽器視窗
  win = new BrowserWindow({ width: 800, height: 600 })

  // 載入應用的 index.html
  const indexPageURL = `file://${__dirname}/dist/index.html`;
  win.loadURL(indexPageURL);

  // 當 window 被關閉,這個事件會被觸發
  win.on('closed', () => {
    // 取消引用 window 物件
    win = null
  })
}

// Electron 會在建立瀏覽器視窗時呼叫這個函式。
app.on('ready', createWindow)

// 當全部視窗關閉時退出
app.on('window-all-closed', () => {
  // 在 macOS 上,除非使用者用 Cmd + Q 確定地退出
  // 否則絕大部分應用會保持啟用
  if (process.platform !== 'darwin') {
    app.quit()
  }
})
複製程式碼

主程式啟動後會一直駐留在後臺執行,你眼睛所看得的和操作的視窗並不是主程式,而是由主程式新啟動的視窗子程式。

應用從啟動到退出有一系列生命週期事件,通過 electron.app.on() 函式去監聽生命週期事件,在特定的時刻做出反應。 例如在 app.on('ready') 事件中通過 BrowserWindow 去展示應用的主視窗,具體用法見 BrowserWindow 的 API 文件

啟動的視窗其實是一個網頁,啟動時會去載入在 loadURL 中傳入的網頁地址。 每個視窗都是一個單獨的網頁程式,視窗之間的通訊需要藉助主程式傳遞訊息。

Electron 應用架構圖

總體來說開發 Electron 應用和開發 Web 應用很相似,區別在於 Electron 的執行環境同時內建了瀏覽器和 Node.js 的 API,在開發網頁時除了可以使用瀏覽器提供的 API 外,還可以使用 Node.js 提供的 API。

接入 Webpack

接下來做一個簡單的 Electron 應用,要求為應用啟動後顯示一個主視窗,在主視窗裡有一個按鈕,點選這個按鈕後新顯示一個視窗,且使用 React 開發網頁。

由於 Electron 應用中的每一個視窗對應一個網頁,所以需要開發2個網頁,分別是主視窗的 index.html 和新開啟的視窗 login.html。 也就是說專案由2個單頁應用組成,這和3-10管理多個單頁應用 中的專案非常相似,讓我們來把它改造成一個 Electron 應用。

需要改動的地方如下:

  • 在專案根目錄下新建主程式的入口檔案 main.js,內容和上面提到的一致;
  • 主視窗網頁的程式碼如下:
import React, { Component } from 'react';
import { render } from 'react-dom';
import { remote } from 'electron';
import path from 'path';
import './index.css';

class App extends Component {

  // 在按鈕被點選時
  handleBtnClick() {
    // 新視窗對應的頁面的 URI 地址
    const modalPath = path.join('file://', remote.app.getAppPath(), 'dist/login.html');
    // 新視窗的大小
    let win = new remote.BrowserWindow({ width: 400, height: 320 })
    win.on('close', function () {
      // 視窗被關閉時清空資源
      win = null
    })
    // 載入網頁
    win.loadURL(modalPath)
    // 顯示視窗
    win.show()
  }

  render() {
    return (
      <div>
        <h1>Page Index</h1>
        <button onClick={this.handleBtnClick}>Open Page Login</button>
      </div>
    )
  }
}

render(<App/>, window.document.getElementById('app'));
複製程式碼

其中最關鍵的部分在於在按鈕點選事件裡通過 electron 庫裡提供的 API 去新開啟一個視窗,並載入網頁檔案所在的地址。

頁面部分的程式碼已經修改完成,接下來修改構建方面的程式碼。 這裡構建需要做到以下幾點:

  • 構建出2個可在瀏覽器裡執行的網頁,分別對應2個視窗的介面;
  • 由於在網頁的 JavaScript 程式碼裡可能會有呼叫 Node.js 原生模組或者 electron 模組,也就是輸出的程式碼依賴這些模組。但由於這些模組都是內建支援的,構建出的程式碼不能把這些模組打包進去。

要完成以上要求非常簡單,因為 Webpack 內建了對 Electron 的支援。 只需要給 Webpack 配置檔案加上一行程式碼即可,如下:

target: 'electron-renderer',
複製程式碼

這句配置曾在2-7其它配置項-Target中提到,意思是指讓 Webpack 構建出用於 Electron 渲染程式用的 JavaScript 程式碼,也就是這2個視窗需要的網頁程式碼。

以上修改都完成後重新執行 Webpack 構建,對應的網頁需要的程式碼都輸出到了專案根目錄下的 dist 目錄裡。

為了以 Electron 應用的形式執行,還需要安裝新依賴:

# 安裝 Electron 執行環境到專案中
npm i -D electron
複製程式碼

安裝成功後在專案目錄下執行 electron . 你就能成功看到啟動的桌面應用了,效果如圖:

Electron 應用執行效果圖

本例項提供專案完整程式碼

Webpack實戰-構建 Electron 應用

《深入淺出Webpack》全書線上閱讀連結

閱讀原文

相關文章