使用 Electron 開發桌面應用

image.png

介紹

Electron,官方簡介:使用 JavaScript,HTML 和 CSS 構建跨平臺的桌面應用程序。
出於我的愛好,接觸到了Electron,並開始嘗試開發一些本地小工具。
如下是對開發過程作的一個經驗總結,便於回顧和交流。
javascript

使用

下面來構建一個簡單的electron應用。
應用源碼地址:github.com/zhuxingmin/…html

1. 項目初始化

項目基於 create-react-app@3.3.0 搭建,執行命令生成項目java

// 全局安裝 create-react-app
npm install -g create-react-app

// 執行命令生成項目
create-react-app electronApp

// 安裝依賴並啓動項目
yarn && yarn start
複製代碼

此時啓動的只是一個react應用,下一步安裝 electron electron-updater electron-builder electron-is-dev等庫node

yarn add electron electron-updater electron-builder electron-is-dev
複製代碼
包名 版本 做用
electron ^9.2.1 electron庫
electron-builder ^22.8.0 打包庫
electron-updater ^4.1.2 更新庫
electron-is-dev 2.0.0 環境庫

2. 配置package.json

安裝完項目依賴後,在package.json中添加electron應用相關配置。react

"version": "0.0.1"              // 設置應用版本號 
"productName": "appName"        // 設置應用名稱
"main": "main.js"               // 設置應用入口文件
"homepage": "."                 // 設置應用根路徑
複製代碼

scripts中添加應用命令,啓動以及打包。git

"estart": "electron ."              // 啓動
"package-win": "electron-builder"   // 打包 (此處以windows平臺爲例,故命名爲package-win)
複製代碼

新增build配置項,添加打包相關配置。
主要有如下幾個配置:
github

屬性 說明
appId 應用id,通常默認爲應用安裝路徑
windows下能夠在 PowerShell中輸入Get-StartApps查看應用id
compression 應用打包壓縮類型
nsis 應用打包安裝與卸載的相關配置
files 應用打包所包含的文件範圍
directories 應用打包地址和輸出地址
publish 應用發佈配置,目前主要做爲應用更新地址來使用
protocols 自定義協議,可根據協議名喚醒app
win windows打包配置,生成exe文件
"build": {
    // 自定義appId 通常以安裝路徑做爲id windows下能夠在 PowerShell中輸入Get-StartApps查看應用id
    "appId": "org.develar.zhuxingmin",
    // 打包壓縮 "store" | "normal"| "maximum" 
    "compression": "store",
    // nsis安裝配置
    "nsis": {
        "oneClick": false, // 一鍵安裝
        "allowToChangeInstallationDirectory": true, // 容許修改安裝目錄
        // 下面這些配置不經常使用
        "guid": "haha",    // 註冊表名字
        "perMachine": true, // 是否開啓安裝時權限限制(此電腦或當前用戶)
        "allowElevation": true, // 容許請求提高。 若是爲false,則用戶必須使用提高的權限從新啓動安裝程序。
        "installerIcon": "xxx.ico", // 安裝圖標
        "uninstallerIcon": "xxx.ico", //卸載圖標
        "installerHeaderIcon": "xxx.ico", // 安裝時頭部圖標
        "createDesktopShortcut": true, // 建立桌面圖標
        "createStartMenuShortcut": true, // 建立開始菜單圖標
        "shortcutName": "lalala" // 圖標名稱
    },
    // 應用打包所包含文件
    "files": [
        "build/**/*",
        "main.js",
        "source/*",
        "service/*",
        "static/*",
        "commands/*"
    ],
    // 應用打包地址和輸出地址
    "directories": {
        "app": "./",
        "output": "dist"
    },
    // 發佈配置 用於配合自動更新
    "publish": [
        {
            // "generic" | "github"
            "provider": "generic", // 靜態資源服務器
            "url": "http://你的服務器目錄/latest.yml"
        }
    ],
    // 自定義協議 用於喚醒應用
    "protocols": [
        {
            "name": "myProtocol",
            "schemes": [
                "myProtocol"
            ]
        }
    ],
    // windows打包配置
    "win": {
        "icon": "build/fav.ico",
        // 運行權限 
        // "requireAdministrator" | "獲取管理員權"
        // "highestAvailable" | "最高可用權限"
        "requestedExecutionLevel": "highestAvailable",
        "target": [
            {
                "target": "nsis"
            }
        ]
    },
},
複製代碼

3. 編寫入口文件 main.js

衆所周知,基於react腳手架搭建的項目,入口文件爲index.js,所以在上面配置完成後,咱們想要啓動electron應用,須要修改項目入口爲main.jsweb

  1. 首先在目錄下新建main.js文件,並在package.json文件中,修改應用入口字段main的值爲main.js
  2. 經過electron提供的BrowserWindow,建立一個窗口實例mainWindow
  3. 經過mainWindow實例方法loadURL, 加載靜態資源
  4. 靜態資源分兩種加載方式:開發和生產;須要經過electron-is-dev判斷當前環境;如果開發環境,能夠開啓調試入口,經過http://localhost:3000/加載本地資源(react項目啓動默認地址);如果生產環境,則要關閉調試入口,並經過本地路徑找到項目入口文件index.html

大致代碼以下算法

const { BrowserWindow } = require("electron");
const url = require("url");
const isDev = require('electron-is-dev');
mainWindow = new BrowserWindow({
    width: 1200,              // 初始寬度
    height: 800,              // 初始高度
    minWidth: 1200,
    minHeight: 675,
    autoHideMenuBar: true,    // 隱藏應用自帶菜單欄
    titleBarStyle: false,     // 隱藏應用自帶標題欄
    resizable: true,          // 容許窗口拉伸
    frame: false,             // 隱藏邊框
    transparent: true,        // 背景透明
    backgroundColor: "none",  // 無背景色
    show: false,              // 默認不顯示
    hasShadow: false,         // 應用無陰影
    modal: true,              // 該窗口是否爲禁用父窗口的子窗口
    webPreferences: {
      devTools: isDev,     // 是否開啓調試功能
      nodeIntegration: true,  // 默認集成node環境
    },
});

const config = dev
    ? "http://localhost:3000/"
    : url.format({
        pathname: path.join(__dirname, "./build/index.html"),
        protocol: "file:",
        slashes: true,
      });

mainWindow.loadURL(config);
複製代碼

4. 項目啓動

項目前置操做完成,運行上面配置的命令來啓動electron應用npm

// 啓動react應用,此時應用運行在"http://localhost:3000/"
   yarn start 
   // 再啓動electron應用,electron應用會在入口文件`main.js`中經過 mainWindow.loadURL(config) 來加載react應用
   yarn estart 
複製代碼

文件目錄

至此,一個簡單的electron應用已經啓動,效果圖以下(這是示例項目的截圖)。

效果圖

做爲一個客戶端應用,它的更新與咱們的網頁開發相比要顯得稍微複雜一些,具體將會經過下面一個應用更新的例子來講明。

5. 應用更新

electron客戶端的更新與網頁不一樣,它須要先下載更新包到本地,而後經過覆蓋源文件來達到更新效果。

首先第一步,安裝依賴

yarn add electron-updater electron-builder
複製代碼

應用經過electron-updater提供的api,去上文配置的服務器地址尋找並對比latest.yml文件,若是版本號有更新,則開始下載資源,並返回下載進度相關信息。下載完成後能夠自動也能夠手動提示用戶,應用有更新,請重啓以完成更新 (更新是能夠作到無感的,下載完更新包以後,能夠不提示,下次啓動客戶端時會自動更新)

// 主進程
const { autoUpdater } = require("electron-updater");
const updateUrl = "應用所在的遠程服務器目錄"
const message = {
    error: "檢查更新出錯",
    checking: "正在檢查更新……",
    updateAva: "檢測到新版本,正在下載……",
    updateNotAva: "如今使用的就是最新版本,不用更新",
};
autoUpdater.setFeedURL(updateUrl);
autoUpdater.on("error", (error) => {
    sendUpdateMessage("error", message.error);
});
autoUpdater.on("checking-for-update", () => {
    sendUpdateMessage("checking-for-update", message.checking);
});
autoUpdater.on("update-available", (info) => {
    sendUpdateMessage("update-available", message.updateAva);
});
autoUpdater.on("update-not-available", (info) => {
    sendUpdateMessage("update-not-available", message.updateNotAva);
});
// 更新下載進度事件
autoUpdater.on("download-progress", (progressObj) => {
    mainWindow.webContents.send("downloadProgress", progressObj);
});
autoUpdater.on("update-downloaded", function ( event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate ) {
    ipcMain.on("isUpdateNow", (e, arg) => {
        // 接收渲染進程的確認消息 退出應用並更新
        autoUpdater.quitAndInstall();
    });
    //詢問是否當即更新
    mainWindow.webContents.send("isUpdateNow");
});
ipcMain.on("checkForUpdate", () => {
    //檢查是否有更新
    autoUpdater.checkForUpdates();
});

function sendUpdateMessage(type, text) {
  // 將更新的消息事件通知到渲染進程
  mainWindow.webContents.send("message", { text, type });
}

複製代碼
// 渲染進程
const { ipcRenderer } = window.require("electron");

// 發送檢查更新的請求
ipcRenderer.send("checkForUpdate");

// 設置檢查更新的監聽頻道

// 監聽檢查更新事件
ipcRenderer.on("message", (event, data) => {
  console.log(data)
});

// 監聽下載進度
ipcRenderer.on("downloadProgress", (event, data) => {
  console.log("downloadProgress: ", data);
});

// 監聽是否能夠開始更新
ipcRenderer.on("isUpdateNow", (event, data) => {
   // 用戶點擊肯定更新後,回傳給主進程
   ipcRenderer.send("isUpdateNow");
});
複製代碼

應用更新的主要步驟

  1. 在主進程中,經過api獲取遠程服務器上是否有更新包
  2. 對比更新包的版本號來肯定是否更新
  3. 對比結果如需更新,則開始下載更新包並返回當前下載進度
  4. 下載完成後,開發者可選擇自動提示仍是手動提示或者不提醒(應用在下次啓動時會自動更新)

上文演示了在頁面上(渲染進程),是如何與主進程進行通訊,讓主進程去檢查更新。
在實際使用中,若是咱們須要用到後臺的能力或者原生功能時,主進程與渲染進程的交互必不可少。
那麼他們有哪些交互方式呢?

在看下面的代碼片斷以前,能夠先了解一下electron主進程與渲染進程 簡單來講就是,經過main.js來執行的都屬於主進程,其他皆爲渲染進程。

6. 主進程與渲染進程間的經常使用交互方式

// 主進程中使用
const { ipcMain } = require("electron");

// 渲染進程中使用
const { ipcRenderer } = window.require("electron");
複製代碼

方式一

渲染進程 發送請求並監聽回調頻道

ipcRenderer.send(channel, someRequestParams);
ipcRenderer.on(`${channel}-reply`, (event, result)=>{
    // 接收到主進程返回的result
})
複製代碼

主進程 監聽請求並返回結果

ipcMain.on(channel, (event, someRequestParams) => {
    // 根據someRequestParams,通過操做後獲得result
    event.reply(`${channel}-reply`, result)
})
複製代碼

方式二

渲染進程

const result = await ipcRenderer.invoke(channel, someRequestParams);
複製代碼

主進程:

ipcMain.handle(channel, (event, someRequestParams) => {
    // 根據someRequestParams,通過操做後獲得result
    return result
});
複製代碼

方式三 以上兩種方式均爲渲染進程通知主進程, 第三種是主進程通知渲染進程

主進程

/* * 使用`BrowserWindow`初始化的實例`mainWindow` */ 
mainWindow.webContents.send(channel, something)
複製代碼

渲染進程

ipcRenderer.on(channel, (event, something) => {
    // do something
})
複製代碼

上文的應用更新用的就是方式一

還有其它通信方式postMessage, sendTo等,能夠根據具體場景決定使用何種方式。

7. 應用喚醒(與其餘應用聯動)

electron應用除了雙擊圖標運行以外,還能夠經過協議連接啓動(瀏覽器地址欄或者命令行)。這使得咱們能夠在網頁或者其餘應用中,以連接的形式喚醒該應用。連接能夠攜帶參數 例:zhuxingmin://?a=1&b=2&c=3 ‘自定義協議名:zhuxingmin’ ‘參數:a=1&b=2&c=3’。

咱們能夠經過參數,來使應用跳轉到某一頁或者讓應用作一些功能性動做等等。

const path = require('path');
const { app } = require('electron');

// 獲取單實例鎖
const gotTheLock = app.requestSingleInstanceLock();

// 若是獲取失敗,證實已有實例在運行,直接退出
if (!gotTheLock) {
  app.quit();
}

const args = [];
// 若是是開發環境,須要腳本的絕對路徑加入參數中
if (!app.isPackaged) {
  args.push(path.resolve(process.argv[1]));
}
// 加一個 `--` 以確保後面的參數不被 Electron 處理
args.push('--');
const PROTOCOL = 'zhuxingmin';
// 設置自定義協議
app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, args);

// 若是打開協議時,沒有其餘實例,則當前實例當作主實例,處理參數
handleArgv(process.argv);

// 其餘實例啓動時,主實例會經過 second-instance 事件接收其餘實例的啓動參數 `argv`
app.on('second-instance', (event, argv) => {
  if (process.platform === 'win32') {
    // Windows 下經過協議URL啓動時,URL會做爲參數,因此須要在這個事件裏處理
    handleArgv(argv);
  }
});

// macOS 下經過協議URL啓動時,主實例會經過 open-url 事件接收這個 URL
app.on('open-url', (event, urlStr) => {
  handleUrl(urlStr);
});

function handleArgv(argv) {
  const prefix = `${PROTOCOL}:`;
  const offset = app.isPackaged ? 1 : 2;
  const url = argv.find((arg, i) => i >= offset && arg.startsWith(prefix));
  if (url) handleUrl(url);
}

function handleUrl(urlStr) {
  // myapp://?a=1&b=2
  let paramArr = urlStr.split("?")[1].split("&");
  const params = {};
  paramArr.forEach((item) => {
      if (item) {
        const [key, value] = item.split("=");
        params[key] = value;
      }
  });
  /** { a: 1, b: 2 } */ 
}
複製代碼

8. 文檔參考

  1. 自動更新
  2. 自定義協議喚醒Electron應用
  3. electron主進程與渲染進程

9. 完整項目代碼

github.com/zhuxingmin/…


南京三百雲信息科技有限公司(車300)成立於2014年3月27日,是一家紮根於南京的移動互聯網企業,目前坐落於南京、北京。通過7年積累,累計估值次數已達52億次,得到了國內外多家優質投資機構青睞如紅杉資本、上汽產業基金等。
三百雲是國內優秀的以人工智能爲依託、以汽車交易訂價和汽車金融風控的標準化爲核心產品的獨立第三方的汽車交易與金融SaaS服務提供商。

歡迎加入三百雲,一塊兒見證汽車行業蓬勃發展,期待與您攜手同行!
Java開發、Java實習、PHP實習、測試、測開、產品經理、大數據、算法實習,熱招中...
官網:www.sanbaiyun.com/
投遞簡歷:hr@che300.com,請註明來自掘金😁

相關文章
相關標籤/搜索