萬物皆可快速上手之Electron(第一彈)

前端森林發表於2020-11-16

image
最近在開發一款桌面端應用,用到了ElectronReact

React作為日常使用比較頻繁的框架,這裡就不詳細說明了,這裡主要是想通過幾篇文章讓大家快速上手Electron以及與React完美融合。

本篇是系列文章的第一篇,主要是給大家分享Electron的一些概念,讓大家對Electron有一個初步的認知。

先來了解一下什麼是Electron吧,可能很多小夥伴還沒有聽過Electron,相信很多小夥伴此時的表情是這樣的:

看下官網的自我介紹:

Electron 是一個可以使用 Web 技術如 JavaScriptHTMLCSS 來建立跨平臺原生桌面應用的框架。藉助 Electron,我們可以使用純 JavaScript 來呼叫豐富的原生 APIs

Electronweb 頁面作為它的 GUI,而不是繫結了 GUI 庫的 JavaScript。它結合了 ChromiumNode.js 和用於呼叫作業系統本地功能的 APIs(如開啟檔案視窗、通知、圖示等)。

上面這張圖很好的說明了Electron的強大之處。

正因如此,現在已經有很多由Electron開發的應用,比如AtomVisual Studio Code等。我們可以在Apps Built on Electron看到所有由Electron構建的專案。

快速開始

前面說了那麼多廢話,下面進入正題,帶大家用五分鐘(為什麼是五分鐘?我猜的 ? )的時間執行一個ElectronHello World

安裝

這一步很簡單:

npm install electron -g

第一個 Electron 應用

一個最簡單的 Electron 應用目錄結構如下:

hello-world/
├── package.json
├── main.js
└── index.html

package.json的格式和 Node 的完全一致,並且那個被 main 欄位宣告的指令碼檔案是你的應用的啟動指令碼,它執行在主程式上。你應用裡的 package.json 看起來應該像:

{
  "name": "hello-world",
  "version": "0.1.0",
  "main": "main.js"
}

建立main.js檔案並新增如下程式碼:

const { app, BrowserWindow } = require("electron");
const isDev = require("electron-is-dev");
const path = require("path");
let mainWindow;

app.on("ready", () => {
  mainWindow = new BrowserWindow({
    width: 1024,
    height: 680,
    webPreferences: {
      nodeIntegration: true,
      // https://stackoverflow.com/questions/37884130/electron-remote-is-undefined
      enableRemoteModule: true,
    },
  });
  // https://www.electronjs.org/docs/api/browser-window#event-ready-to-show
  // 在載入頁面時,渲染程式第一次完成繪製時,如果視窗還沒有被顯示,渲染程式會發出 ready-to-show 事件 。 在此事件後顯示視窗將沒有視覺閃爍
  mainWindow.once("ready-to-show", () => {
    mainWindow.show();
  });
  const urlLocation = `file://${__dirname}/index.html`;
  mainWindow.loadURL(urlLocation);
});

然後是index.html檔案:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Hello World!</title>
    <style media="screen">
      .version {
        color: red;
      }
    </style>
  </head>
  <body>
    <h1>Hi! 我是柯森!</h1>
  </body>
</html>

到這裡main.jsindex.htmlpackage.json 這幾個檔案都有了。萬事俱備,來執行這個專案。因為前面已經全域性安裝了electron,所以我們可以使用 electron 命令來執行專案。在 hello-world/ 目錄裡面執行下面的命令:

$ electron .

你會發現會彈出一個 electron 應用客戶端,如圖所示:

到這裡,我們已經完成了一個最簡單的electron 應用。

但你一定會對上面用到的一些api有疑惑,下面我將帶大家深入淺出的瞭解一下electron的常用概念和api

相關概念

Electron 的程式分為主程式和渲染程式。在說這個之前,我覺得有必要先說一下程式和執行緒的概念。

程式和執行緒

這裡參考的是廖雪峰老師關於程式和執行緒概念的闡述,我覺得說的清晰明瞭。

對於作業系統來說,一個任務就是一個程式(Process),比如開啟一個瀏覽器就是啟動一個瀏覽器程式,開啟一個記事本就啟動了一個記事本程式,開啟兩個記事本就啟動了兩個記事本程式,開啟一個Word就啟動了一個Word程式。

有些程式還不止同時幹一件事,比如Word,它可以同時進行打字、拼寫檢查、列印等事情。在一個程式內部,要同時幹多件事,就需要同時執行多個“子任務”,我們把程式內的這些“子任務”稱為執行緒(Thread)。

主程式和渲染程式

主程式

electron 裡面,執行 package.json 裡面 main 指令碼的程式被稱為主程式。主程式控制整個應用的生命週期,在主程式中可以建立 Web 形式的 GUI,而且整個 Node API 是內建其中。

渲染程式

由於 Electron 使用 Chromium 來展示頁面,所以 Chromium 的多程式架構也被充分利用。每個 Electron 的頁面都在執行著自己的程式,這樣的程式我們稱之為渲染程式

在一般瀏覽器中,網頁通常會在沙盒環境下執行,並且不允許訪問原生資源。然而,Electron 使用者擁有與底層作業系統直接互動的能力。

主程式與渲染程式的區別

主程式使用BrowserWindow例項建立頁面。每個BrowserWindow例項都在自己的渲染程式裡執行頁面。當一個BrowserWindow例項被銷燬後,相應的渲染程式也會被終止。

主程式管理所有頁面和與之對應的渲染程式。每個渲染程式都是相互獨立的,並且只關心他們自己的頁面。

electron 中,頁面不直接呼叫底層 APIs,而是通過主程式進行呼叫。所以如果你想在網頁裡使用 GUI 操作,其對應的渲染程式必須與主程式進行通訊,請求主程式進行相關的 GUI 操作。

electron 中,主程式和渲染程式的通訊主要有以下幾種方式:

  • ipcMain、ipcRender
  • Remote 模組

程式通訊將稍後在下文詳細介紹。

BrowserWindow 的建立

BrowserWindow用於建立和控制瀏覽器視窗。像上面的hello-world中:

mainWindow = new BrowserWindow({
  width: 1024,
  height: 680,
  webPreferences: {
    nodeIntegration: true,
    // https://stackoverflow.com/questions/37884130/electron-remote-is-undefined
    enableRemoteModule: true,
  },
});

const urlLocation = `file://${__dirname}/index.html`;
mainWindow.loadURL(urlLocation);

建立了一個1024*680的視窗,並通過loadURL方法來載入了一個本地的html檔案。

這裡一般會通過區分環境載入對應不同的檔案。

程式間的通訊

在計算機系統設計中,不同的程式間記憶體資源都是相互隔離的,因此程式間的資料交換,會使用程式間通訊方式達成。而不同於一般的原生應用開發,Electron 的渲染程式與主程式分別屬於獨立的程式中,而且程式間會存在頻繁的資料交換,這時選擇一個合理的程式間通訊方式顯得尤為重要。下面是 Electron 中官方提供的程式間通訊方式:

window.postMessage,LocalStorage

在前端開發中,鑑於瀏覽器對本地資料有嚴格的訪問限制,所以一般通過該兩種方式進行視窗間的資料通訊,該方式同樣適用於 Electron 開發中。然而因為 API 設計目的僅僅是為了前端視窗間簡單的資料傳輸,大量以及頻繁的資料通訊會導致應用結構鬆散,同時傳輸效率也值得懷疑。

使用IPC進行通訊

Electron 中提供了 ipcRenderipcMain 作為主程式以及渲染程式間通訊的橋樑,該方式屬於 Electron 特有傳輸方式,不適用於其他前端開發場景。Electron 沿用 Chromium 中的 IPC 方式,不同於 sockethttp 等通訊方式,Chromium 使用的是命名管道 IPC ,能夠提供更高的效率以及安全性。

主程式收發資訊

詳細參考ipcMain
  • 主程式接收渲染程式傳送的資訊
ipcMain.on("message", (e, msg) => {
  console.log(msg);
});
  • 主程式(主視窗)傳送資訊給渲染程式
mainWindow.webContents.send('message', { name: 'from the main by cosen' });

渲染程式收發資訊

通過ipcRenderer傳送或接收
  • 渲染程式接收主程式傳送的資訊
ipcRenderer.on("message", (e, msg) => {
  console.log(msg);
});
  • 渲染程式傳送資訊給主程式
ipcRenderer.send("message", { name: "Cosen" });

使用remote實現跨程式訪問

remote 模組提供了一種在渲染程式(網頁)和主程式之間進行程式間通訊(IPC)的簡便途徑。

Electron中, 與GUI相關的模組(如 dialog, menu 等)只存在於主程式,而不在渲染程式中 。為了能從渲染程式中使用它們,需要用ipc模組來給主程式傳送程式間訊息。使用 remote 模組,可以呼叫主程式物件的方法,而無需顯式地傳送程式間訊息。

總結

本小節我們大概的瞭解了Electron的一些概念以及執行了一個入門的hello-world程式。但這遠遠還不夠,下一節我會講一下如何將ElectronReact完美融合,畢竟還是要更貼近業務的~

好了,不早了,我要去開啟我的網易雲時光了 ?

❤️ 愛心三連擊

1.如果覺得這篇文章還不錯,來個分享、點贊、在看三連吧,讓更多的人也看到~

2.關注公眾號前端森林,定期為你推送新鮮乾貨好文。

3.特殊階段,帶好口罩,做好個人防護。
image

相關文章