【Electron】酷家樂客戶端開發實踐分享 — 入坑篇

做者:鍾離,酷家樂PC客戶端負責人javascript

原文地址:webfe.kujiale.com/electron-ku…css

酷家樂客戶端:下載地址 www.kujiale.com/activity/13…html

文章背景:在酷家樂客戶端在V12改版成功後,咱們積累了許多的寶貴的經驗和最佳實踐。前端社區裏關於Electron知識相對較少,所以但願將這些內容以系列文章的形式分享出來。前端

系列文章:java

本文的初衷

Electron所使用的技術棧(JavaScript、NodeJs、HTML、CSS)和web前端工程師完美契合。因而,愈來愈多的前端工程師,用Electron來開發桌面客戶端的開發,我也是其中的一員。linux

雖然Electron技術棧對前端工程師比較友好,可是概念較多,和web前端開發仍是有很大差異的,寫個入坑指南但願能幫助讀者快速上手Electron。git

瞭解客戶端

首先拋出一個問題,web應用是桌面客戶端嗎?顯然不是。那麼,問題來了,什麼樣的軟件纔是桌面客戶端呢?咱們既然要從web前端轉到客戶端開發,那麼就須要瞭解客戶端,就像咱們當初瞭解web應用同樣。github

回到剛剛那個問題,桌面客戶端有兩個重要的特色:web

  1. 獨立運行於操做系統上(桌面客戶端只是PC,那麼限定windows、MacOS、linux這幾個主流PC操做系統)
  2. 有本身的GUI(用戶圖形界面 graphical user interface)

web應用有本身的GUI,必須在瀏覽器中執行,所以不是桌面客戶端。ajax

瀏覽器能直接運行在操做系統上,並且有本身的GUI,所以瀏覽器是桌面客戶端。

Electron的能力

在剛剛接觸Electorn的時候,文檔看的我是眼花繚亂。在某個加班的深夜,我不由對天長嘆:這個東西到底能幹啥?

這東西能幹啥?在經歷了Electron的反覆摩擦以後,我總結了Electron的幾個關鍵能力:

  1. NodeJs所有能力,與操做系統交互
    • operation system 與操做系統相關的操做
    • HTTP(s)、HTTP2
    • process、child process 進程相關
    • file system 文件系統
    • ...省略
  2. Electron提供的基礎模塊,主要與操做系統交互
    • app 主進程聲明週期管理,控制MacOS任務欄dock、windows任務欄taskbar
    • BrowserWindow 控制窗口,在MacOS和windows中窗口很是重要!
    • screen 操做用戶顯示器
    • globalShortcut 系統級別快捷鍵
    • ...省略
  3. Chromium提供的能力,主要提供GUI圖形界面
    • 解析HTML、CSS、JS
    • ajax請求
    • cookie、localstorage
    • ...省略

能力越大,責任越大

若是用戶安裝了咱們的桌面客戶端,那麼咱們的軟件在用戶電腦上運行時,就有了很是大的權利,這是把雙刃劍。

用戶選擇了咱們的軟件,咱們也要對用戶的電腦負責。能力越大,責任也就越大:

1.注意內存的佔用,特別是chromium,簡直是內存怪獸。能夠經過os來獲取用戶電腦的配置,而後根據電腦的配置和可用資源,來制定合理的策略。

  1. 爲軟件增長代碼簽名,提高安全性
  2. 謹慎操做註冊表、用戶敏感目錄

一旦被貼上【流氓軟件】、【很差用】的標籤,就很難再改變用戶的印象了。

主進程、渲染進程

總體執行流程,展現了主進程和渲染進程的關係

生命週期

主進程:從整個應用啓動到結束,該進程一直存在。主進程只有一個

渲染進程:主進程可用建立/銷燬渲染進程,所以渲染進程的生命週期是不固定的。渲染進程能夠有多個。

執行環境

渲染進程可用模塊
主進程可用模塊

在Electron的API文檔中,會在文檔頂部標識該模塊在哪一個進程可用,例如:ipcRenderer

職責劃分

主進程 渲染進程
控制app的生命週期,爲app註冊關鍵事件 解析HTML,渲染窗口內容
阻止一些默認行爲,例如webContents的跳轉、download事件的默認行爲等等(在渲染進程沒法作到) 處理窗口的交互邏輯
建立BrowserWindow,也就是渲染進程。合理設置窗口的參數,控制窗口的生命週期(例如什麼時候銷燬窗口),決定BrowserWindow加載何處的HTML 與主進程通訊,實現高級交互

窗口、前端資源

咱們回顧一下剛剛講到的執行流程,其中有一個有趣的點,就是Electron的窗口會加載一個HTML來渲染窗口的內容。

HTML,以及HTML加載的css、js文件,統稱爲前端資源

若是不加載HTML的,客戶端還能用嗎?不妨來試試

// main process
    const win1 = new BrowserWindow();
    const win2 = new BrowserWindow();
複製代碼

上述代碼在主進程中執行,建立了兩個窗口,窗口並沒加載HTML文件。可是窗口倒是真實存在的,帶有系統標準的控制欄,可拖動,是貨真價實的系統窗口!

兩個空白窗口

咱們能夠發現,前端資源和窗口是分離的。由主進程建立的的窗口(BrowserWindow),既是一個系統原生窗口,同時也是一個加載&渲染前端資源的容器

窗口一般會經過file協議和http(s)協議來加載前端資源,接下來咱們看看這兩種方式的區別。

經過file協議加載HTML

在Electron的官方入門例子中,就是經過file協議來加載HTML的

經過file協議加載HTML,不管有沒有網絡,均可以加載到HTML文件,這是file協議核心優點。缺點也比較明顯:

  1. 若是頁面資源要更新,那麼只能經過發版來解決(若是你用webview,那麼webview的內容就能夠自動更新,不過webview也須要有網絡才能加載)
  2. 在file協議下,沒法經過ajax來請求數據(協議不一樣),只能經過NodeJs的http(s)模塊來發起網絡請求

經過http協議加載HTML

經過http協議加載HTML,優勢是能夠隨時經過web頁面的部署,更新渲染進程的資源,而且在https協議下,你能夠在頁面中使用前端熟悉的ajax請求來獲取數據。

固然,缺點也比較明顯:

  1. 沒有網絡,而且在你沒有作HTML的緩存時,你的窗口內容沒法加載
  2. 必須經過https來加載,保證頁面內容的安全性

代碼示例

方便讀者更好理解上文的內容,寫了一個小demo,源代碼地址 github.com/littlecold2…,例子有如下特色:

  • 建立主窗口,阻止關閉主窗口關閉的默認事件,不銷燬窗口。(大部分客戶端的主窗口,關閉主窗口的時候,其實是隱藏了該窗口,例如QQ、微信)

  • 應用退出時,會嘗試關閉全部窗口,再退出應用。若是主窗口的關閉行爲默認事件被阻止,那麼會致使主窗口沒法關閉,整個應用沒法退出。所以使用forceQuit這個變量來控制。

  • 使用http或者file協議加載窗口前端資源(例子中,默認加載的是微信)

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

async function main () {
  await app.whenReady();

  let forceQuit = false;

  const majorWindow = new BrowserWindow({
    title: '主窗口',
    width: 1000,
    height: 750,
    minWidth: 1000,
    minHeight: 750,
    backgroundColor: '#f2f2f2',
  }); // 主窗口

  // 阻止標題更新
  majorWindow.on('page-title-updated', (e) => {
    e.preventDefault();
  });

  majorWindow.on('close', (e) => {
      // 用戶但願退出的時候,不做處理,默認會銷燬這個窗口
      if (forceQuit) return;
      e.preventDefault();

      // macOS全屏的處理
      if (majorWindow.isFullScreen()) {
        majorWindow.once('leave-full-screen', () => {
          majorWindow.hide();
        });
        majorWindow.setFullScreen(false);
      } else {
        majorWindow.hide();  // 隱藏窗口
      }
  });

  // 點擊dock打開主窗口
  app.on('activate', () => {
    majorWindow.show();
  });

  // 用戶使用cmd + Q、代碼中調用app.quit等狀況
  // 此時用戶但願可以退出應用,所以將forceQuit改成true
  app.on('before-quit', () => {
    forceQuit = true;
  });
  
  app.dock.setIcon('./img/icon.png'); // 在app打包後,這一句代碼實際上是不須要的

  majorWindow.loadURL('https://wx.qq.com'); // http協議加載前端資源,隨便加載一個微信試試
  // majorWindow.loadURL('file://index.html'); // file協議加載前端資源

}

main();

複製代碼

在本地跑一下這個例子

微信測試

最後

歡迎你們在評論區討論,技術交流 & 內推 -> zhongli@qunhemail.com

相關文章
相關標籤/搜索