Electron 從零到一

蘑菇街前端團隊正式入駐掘金,但願你們不要吝嗇大家手中的贊(比心)!javascript

零、 介紹

Electron 咱們會出一個系列,這篇文章主要是介紹 Electron 基礎相關的東西,後續會出咱們在實戰中更多的技巧以及問題的解決方案,但願你們持續關注咱們。html

本文主要包括:前端

  1. Electron 簡介
  2. 快速開始
  3. 進程
  4. 打包
  5. 踩坑總結

1、Electron 簡介

Electron 是一個賦力前端進行跨平臺開發的框架,讓開發人員使用 JavaScript, HTML 和 CSS 等前端技術構建跨平臺的桌面應用。 Electron 經過將 ChromiumNode.js 合併到同一個運行時環境中,並將其打包爲 Mac,Windows 和 Linux 系統下的應用,而開發人員只需關注前端代碼的開發。java

2、快速開始 Electron + React

electron 提供了一個名爲 electron-quick-start 的項目,能夠 clone 下來當成模版使用,本文仍是使用 create-react-app 來一步一步學習。node

建立一個 react 項目

# 安裝 create-react-app 命令,若是已將安裝請忽略
npm install -g create-react-app

# 建立 electron-react 項目
create-react-app electron-react

# 啓動項目
cd electron-react && npm start
複製代碼

瀏覽器打開 localhost:3000 出現下面的畫面:react

配置 electron 環境

在 public 文件夾下新建 index.html,隨便寫點內容:webpack

...
<div>hello world</div>
...
複製代碼

接下來建立 electron 主線程文件,public/main.js,建議寫在 public 路徑下面。git

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

// 建立全局變量並在下面引用,避免被GC
let win

function createWindow () {
    // 建立瀏覽器窗口並設置寬高
    win = new BrowserWindow({ width: 800, height: 600 })
    
    // 加載頁面
    win.loadFile('./index.html')
    
    // 打開開發者工具
    win.webContents.openDevTools()
    
    // 添加window關閉觸發事件
    
    win.on('closed', () => {
        win = null  // 取消引用
    })
}

// 初始化後 調用函數
app.on('ready', createWindow)  

// 當所有窗口關閉時退出。
app.on('window-all-closed', () => {
   // 在 macOS 上,除非用戶用 Cmd + Q 肯定地退出,
   // 不然絕大部分應用及其菜單欄會保持激活。
   if (process.platform !== 'darwin') {
        app.quit()
   }
})
  
app.on('activate', () => {
// 在macOS上,當單擊dock圖標而且沒有其餘窗口打開時,
// 一般在應用程序中從新建立一個窗口。
    if (win === null) {
      createWindow()
    }
})

複製代碼

最後,修改 package.json 中的 main 字段對應的路徑, 並添加 start 命令github

{
    ...
    "main": "main.js",
    "scripts": "electron ."
}
複製代碼

執行 npm start, 就會彈出以下界面:web

png

這裏我簡單寫了一個頁面,你們也能夠寫一寫本身感興趣的東西。

因而,一個簡單的桌面應用就開發好了,真的 so easy.

3、 進程

electron 的進程分爲主進程渲染進程

主進程&渲染進程

先來看看 electron 項目基本目錄結構

app
└─public
    └─index.html---------------入口文件
├─main.js----------------------程序啓動入口,主進程
├─ipc--------------------------進程間模塊
├─appNetwork-------------------應用通訊模塊
└─src--------------------------窗口管理,渲染進程
    ├─components---------------通用組件模塊
    ├─store--------------------數據共享模塊
    ├─statics------------------靜態資源模塊
    └─pages----------------------窗口業務模塊
      ├─窗口A----------------窗口
      └─窗口B----------------窗口
複製代碼

package.json 中的 main 字段對應的文件的進程是主進程。Electron集成了Chromium來展現窗口界面,窗口中所看到的內容使用的都是HTML渲染出來的。 Chromium自己是多進程渲染頁面的架構(在默認狀況下,Chromium的默認策略是對每個tab新開一個進程,以確保每一個頁面是獨立且互不影響的。避免一個頁面的崩潰致使所有頁面沒法使用),因此Electron在展現窗口時,也會使用到Chromium的多進程架構。而這種多進程渲染架構在Electron中,就被稱之爲渲染進程(render process)

進程間通訊

在 electron 中,GUI 相關的模塊(如 dialog,menu 等)僅在主進程可用,在渲染進程中不可用。爲了在渲染進程中使用它們,須要使用 ipc 模塊向主進程發送消息,下面是幾種進程間通信的方法。

  1. ipcMain & ipcRenderer

從主進程到渲染進程的異步通訊,也能夠將消息從主進程發送到渲染進程,參考 webContents.send.

發送消息時,事件名稱爲 channel。

回覆同步消息時,須要設置 event.returnValue。

將異步消息發送回發送方,可使用 event.reply(...),這個輔助方法將自動處理來自渲染進程的消息,然而 event.sender.send(...) 這個方法則始終將消息發送給主進程。

下面是在渲染和主進程之間發送和處理消息的一個例子:

// 在主進程中
const { ipcMain } = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
    console.log(arg); // 輸出 'ping'
    event.reply('asynchronous-reply', 'pong');
})

ipcMain.on('synchronous-message', (event, arg) => {
    console.log(arg) // 輸出 ‘ping’
    event.returnValue = 'pong'
})
複製代碼
// 在渲染進程(網頁)中
const { ipcRenderer } = require('electron')
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // 輸出 'pong'

ipcRenderer.on('asynchronous-reply', (event, arg) => {
    console.log(arg); // 輸出 'pong'
})

ipcRenderer.send('asynchronous-message', 'ping')
複製代碼
  1. remote 模塊

remote渲染進程和主進程通訊提供了一種簡單的方法。你能夠調用 main 進程對象的方法,而沒必要顯式發送進程間消息。例如:從渲染進程建立瀏覽器窗口

const { BrowserWindow } = require('electron').remote
let win = new BrowserWindow({ width: 800, height: 600 })
win.loadUrl('https://www.mogu.com')
複製代碼

注意: 反過來(若是須要從主進程訪問渲染進程),可使用 webContents.executeJavascript

  1. webContents

經過 channel 向渲染進程發送異步消息,能夠發送任意參數。在內部,參數會被序列化爲 JSON,所以參數對象傷的函數和原型鏈不會被髮送

除了以上這些方法,也可使用 localStorage、sessionStorage 等。

4、打包

開發完成後,還須要將應用打包成可執行文件,這一環節的坑仍是學習 electron 到如今踩的最多的。

目前主流的打包工具備 electron-packagerelectron-builder

electron-packager

安裝依賴:

npm i electron-packager --save-dev

打包:

electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> [optional flags...]

也能夠直接運行 npm run electron-packager . 打包。

electron-builder

官方解釋:

A complete solution to package and build a ready for distribution Electron, Proton Native or Muon app for macOS, Windows and Linux with 「auto update」 support out of the box.

簡單的說,electron-builder 有比 electron-packager 更豐富的功能,支持更多的平臺,同時也支持了自動更新。除了這幾點外,electron-builder 打出的包更爲輕量,而且能夠打包出不暴露源碼的 setup 安裝程序。另外使用下來感受比 electron-packager 的坑要少一點。

安裝依賴:

npm i electron-builder --save-dev

複製代碼

打包:

  • 在項目的 package.json 文件中定義 build 字段
{
    "build": {
        "appId": "com.xxx.app",
        "extends": null,
        "files": [
            "build/**/*"
        ],
        "mac": {
            "icon": "icons/icon.icns"
        },
        "win": {
            "target": "nsis",
            "icon": "icons/icon.png"
        }
    }
}

複製代碼

這是最基礎的配置,固然打包過程當中可能會碰到其餘的問題須要修改配置。一般 files 配置只寫一個 build 文件夾是不夠的,要根據項目結構和打包狀況添加其餘路徑。

  • 添加 scripts 命令
{
    "scripts": {
        "pack": "electron-builder"
    }
}

複製代碼
  • 運行 npm run pack 打包

打包完成後在 dist 目錄下有可執行文件,打開後若是沒有報錯,則說明打包成功。

5、踩坑總結

大部分都是打包遇到的坑

  • 使用 electron-packager 打包報錯

Generated checksum for "electron-v6.0.2-darwin-x64.zip" did not match expected checksum。node 版本升級到 8.x 以上就好。

  • 打開打包生成的可執行文件報錯

png

出現這種問題可能有如下幾個緣由:

  1. 項目中可能直接訪問了本地路徑, 瀏覽器爲了安全考慮不容許訪問。
  2. package.json 中的 build 配置問題,假如 main.js 在一個很深的路徑中,須要在下面單獨添加 main.js 的路徑
"build": {
    ...
+ "public/main.js"
    ...
}

複製代碼
  1. webpack 配置中的路徑直接使用了 __dirname, 可使用 remote 模塊的 getAppPath 方法取得路徑
const remote = require('remote')
const app = remote.require('app')
console.log(app.getAppPath());

複製代碼

參考 github.com/electron/el…

  • dependencies & devDependencies

在 electron 打包時,必定要分清哪些是生產環境依賴,哪些是開發環境依賴。避免出現此類錯誤:

png

  • 關於打包慢的問題:npm & cnpm

cnpm 裝的各類 node_modules,這種方式下全部的包都是扁平化的安裝,一會兒 node_modules 展開就有很是多的文件,致使打包的過程很是慢。可是若是該用 npm 來安裝 node_modules 的話,全部的包都是樹狀結構,層級變深。可是打包速度會快不少。具體見:electron打包過了2小時都沒好?

推薦文章

  1. imweb.io/topic/5b681…
  2. juejin.im/post/5ba06b…

參考文章

  1. juejin.im/post/5c9041…
  2. electronjs.org
  3. juejin.im/post/5b86b7…
  4. imweb.io/topic/5b681…
  5. juejin.im/post/5ba06b…
相關文章
相關標籤/搜索