[electron]終極奧義 五千字教程丟給你
本文包含 打包
、 自動更新
、 簡易API
、 除錯
、 程序通訊
等相關知識點,內容較多,可能會引起不適,請酌情檢視(手動滑稽)。
electron
簡介
electron是由Github開發,是一個用Html、css、JavaScript來構建桌面應用程式的開源庫,可以打包為Mac、Windows、Linux系統下的應用。
electron是一個執行時環境,包含Node和Chromium,可以理解成把web應用執行在node環境中
結構
electron主要分為主程序和渲染程序,關係如下圖

electron執行 package.json
中的 main
欄位標明指令碼的程序稱為 主程序
在主程序建立web頁面來展示使用者頁面, 一個electron有且只有一個主程序
electron使用Chromium來展示web頁面,每個頁面執行在自己的 渲染程序
中
快速開始
接下來,讓程式碼來發聲,雷打不動的 hello world
建立資料夾,並執行 npm init -y
,生成 package.json
檔案,下載 electron
模組並新增開發依賴
mkdir electron_hello && cd electron_hello && npm init -y && npm i electron -D 複製程式碼
下載速度過慢或失敗,請嘗試使用cnpm,安裝方式如下
# 下載cnpm npm install -g cnpm --registry=https://registry.npm.taobao.org # 下載electron cnpm i electron -D 複製程式碼
建立index.js,並寫入以下內容
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() } }) 複製程式碼
建立index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hello World!</title> </head> <body> <h1 id="h1">Hello World!</h1> We are using node <script> document.write(process.versions.node) </script> Chrome <script> document.write(process.versions.chrome) </script>, and Electron <script> document.write(process.versions.electron) </script> </body> </html> 複製程式碼
最後,修改 packge.json
中的main欄位,並新增start命令
{ ... main:'index.js', scripts:{ "start": "electron ." } } 複製程式碼
執行 npm run start
後,就會彈出我們的應用來。

除錯
我們知道electron有兩個程序,主程序和渲染程序,開發過程中我們需要怎麼去除錯它們呢?老太太吃柿子,咱們撿軟的來
渲染程序
BrowserWindow 用來建立和控制瀏覽器視窗,我們呼叫它的例項上的API即可
win = new BrowserWindow({width: 800, height: 600}) win.webContents.openDevTools() // 開啟除錯 複製程式碼
除錯起來是和Chrome是一樣的,要不要這麼酸爽

主程序
使用VSCode進行除錯
使用VSCode開啟專案,點選除錯按鈕

點選除錯後的下拉框

選擇新增配置,選擇node

此時會把預設的除錯配置開啟,大概長這樣

什麼?你的不是,不是的話,就直接把下面的複製並替換你的配置
差不多這麼辦,那就把 configurations
裡面第二項複製到你的 configurations
配置裡面,第一個配置是用來除錯node的
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "啟動程式", "program": "${workspaceFolder}/main.js" }, { "name": "Debug Main Process", "type": "node", "request": "launch", "cwd": "${workspaceFolder}", "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron", "windows": { "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd" }, "args" : ["."] } ] } 複製程式碼
可以看到 ${workspaceFolder}
,這是關於VSCode的變數,用來表示當前開啟的資料夾的路徑
修改完配置後,我們除錯面板,選擇我們剛才配置的

在程式碼中標記需要除錯的地方,然後點選綠色的小三角,就可以愉快的除錯了

程序通訊
在Electron中, GUI 相關的模組 (如 dialog、menu 等) 僅在主程序中可用, 在渲染程序中不可用。 為了在渲染程序中使用它們, ipc 模組是向主程序傳送程序間訊息所必需的,以下介紹幾種程序間通訊的方法。
哥倆好
ipcMain 和ipcRenderer是兩個好基友,通過這兩個模組可以實現程序的通訊。
-
ipcMain 在主程序中使用,用來處理渲染程序(網頁)傳送的同步和非同步的資訊
-
ipcRenderer 在渲染程序中使用,用來發送同步或非同步的資訊給主程序,也可以用來接收主程序的回覆資訊。
以上兩個模組的通訊,可以理解成 釋出訂閱模式
,接下來,我們看下它們具體的使用方法
主程序
const {ipcMain} = require('electron') // 監聽渲染程式發來的事件 ipcMain.on('something', (event, data) => { event.sender.send('something1', '我是主程序返回的值') }) 複製程式碼
渲染程序
const { ipcRenderer} = require('electron') // 傳送事件給主程序 ipcRenderer.send('something', '傳輸給主程序的值') // 監聽主程序發來的事件 ipcRenderer.on('something1', (event, data) => { console.log(data) // 我是主程序返回的值 }) 複製程式碼
以上程式碼使用的是非同步傳輸訊息,electron也提供了同步傳輸的API。
傳送同步訊息將會阻塞整個渲染程序,你應該避免使用這種方式 - 除非你知道你在做什麼。
切忌用 ipc 傳遞大量的資料,會有很大的效能問題,嚴重會讓你整個應用卡住。
remote模組
使用 remote 模組, 你可以呼叫 main 程序物件的方法, 而不必顯式傳送程序間訊息。
const { dialog } = require('electron').remote dialog.showMessageBox({type: 'info', message: '在渲染程序中直接使用主程序的模組'}) 複製程式碼
webContents
webContents負責渲染和控制網頁, 是 BrowserWindow 物件的一個屬性, 我們使用send方法向渲染器程序傳送非同步訊息。
主程序
const {app, BrowserWindow} = require('electron') let win app.on('ready', () => { win = new BrowserWindow({width: 800, height: 600}) // 載入頁面 win.loadURL('./index.html') // 導航完成時觸發,即選項卡的旋轉器將停止旋轉,並指派onload事件後。 win.webContents.on('did-finish-load', () => { // 傳送資料給渲染程式 win.webContents.send('something', '主程序傳送到渲染程序的資料') }) }) 複製程式碼
渲染程序
<!DOCTYPE html> <html> <head> </head> <body> <script> require('electron').ipcRenderer.on('something', (event, message) => { console.log(message) // 主程序傳送到渲染程序的資料 }) </script> </body> </html> 複製程式碼
渲染程序資料共享
更多情況下,我們使用HTML5 API實現,如localStorage、sessionStorage等,也可以使用electron的IPC機制實現
主程序
global.sharedObject = { someProperty: 'default value' } 複製程式碼
渲染程序
第一個頁面
require('electron').remote.getGlobal('sharedObject').someProperty = 'new value' 複製程式碼
第二個頁面
console.log(require('electron').remote.getGlobal('sharedObject').someProperty) // new value 複製程式碼
總結
以上四個方法均可實現主程序和渲染程序的通訊,可以發現使用 remote
模組是最簡單的,渲染程序程式碼中可以直接使用 electron
模組
常用模組
快捷鍵與選單
本地快捷鍵

只有在應用聚焦的時候才會觸發,主要程式碼如下
官方文件親測沒有效果
const { Menu, MenuItem } = require('electron') const menu = new Menu() menu.append(new MenuItem({ label: '自定義快捷鍵', submenu: [ { label: '測試', accelerator: 'CmdOrCtrl+P', click: () => { console.log('我是本地快捷鍵') } } ] })) Menu.setApplicationMenu(menu) 複製程式碼
全域性快捷鍵
註冊全域性,無論應用是否聚焦,都會觸發,使用 globalShortcut
來註冊, 主要程式碼如下,
const {globalShortcut, dialog} = require('electron') app.on('read', () => { globalShortcut.register('CmdOrCtrl+1', () => { dialog.showMessageBox({ type: 'info', message: '你按下了全域性註冊的快捷鍵' }) }) }) 複製程式碼
顯示如下,使用了 dialog
模組

上下文選單
上下文選單就是我們點選右鍵的時候顯示的選單, 設定在渲染程序裡面

主要程式碼如下
const remote = require('electron').remote;// 通過remote模組使用主程式才能使用的模組 const Menu = remote.Menu; const MenuItem = remote.MenuItem; var menu = new Menu(); menu.append(new MenuItem({ label: 'MenuItem1', click: function() { console.log('item 1 clicked'); } })); menu.append(new MenuItem({ type: 'separator' })); menu.append(new MenuItem({ label: 'MenuItem2', type: 'checkbox', checked: true })); window.addEventListener('contextmenu', (e) => { e.preventDefault(); menu.popup(remote.getCurrentWindow()); }, false); 複製程式碼
應用選單

上文中我們通過 Menu
模組建立了本地快捷鍵,應用選單也是用 Menu
模組,我們看到長得好像一樣,其實實現起來也差不多,我們這裡使用建立模版的方式來實現,主要程式碼如下:
const {app, Menu} = require('electron') const template = [ { label: '操作', submenu: [{ label: '複製', accelerator: 'CmdOrCtrl+C', role: 'copy' }, { label: '貼上', accelerator: 'CmdOrCtrl+V', role: 'paste' }, { label: '重新載入', accelerator: 'CmdOrCtrl+R', click: function (item, focusedWindow) { if (focusedWindow) { // on reload, start fresh and close any old // open secondary windows if (focusedWindow.id === 1) { BrowserWindow.getAllWindows().forEach(function (win) { if (win.id > 1) { win.close() } }) } focusedWindow.reload() } } }] }, { label: '載入網頁', submenu: [ { label: '優酷', accelerator: 'CmdOrCtrl+P', click: () => { console.log('time to print stuff') } }, { type: 'separator' }, { label: '百度', } ] } ] if (process.platform === 'darwin') { const name = electron.app.getName() template.unshift({ label: name, submenu: [{ label: `關於 ${name}`, role: 'about' }, { type: 'separator' }, { label: '服務', role: 'services', submenu: [] }, { type: 'separator' }, { label: `隱藏 ${name}`, accelerator: 'Command+H', role: 'hide' }, { label: '隱藏其它', accelerator: 'Command+Alt+H', role: 'hideothers' }, { label: '顯示全部', role: 'unhide' }, { type: 'separator' }, { label: '退出', accelerator: 'Command+Q', click: function () { app.quit() } }] }) } app.on('read', () => { const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); }) 複製程式碼
系統托盤

主要程式碼
const { app, Tray } = require('electron') /* 省略其他程式碼 */ let tray; app.on('ready', () => { tray = new Tray(__dirname + '/build/icon.png');//系統托盤圖示 const contextMenu = Menu.buildFromTemplate([ // 選單項 {label: '顯示', type: 'radio', click: () => {win.show()}}, {label: '隱藏', type: 'radio', click: () => {win.hide()}}, ]) // tray.on('click', () => { //滑鼠點選事件最好和菜單隻設定一種 //win.isVisible() ? win.hide() : win.show() // }) tray.setToolTip('This is my application.') // 滑鼠放上時候的提示 tray.setContextMenu(contextMenu) // 應用選單項 }) 複製程式碼
模組一覽
開啟頁面
shell
主程序開啟頁面,會呼叫預設的瀏覽器開啟頁面,使用 shell
模組
const { app,shell } = require('electron') app.on('ready', () => { shell.openExternal('github.com') }) 複製程式碼
應用啟動後就會使用預設瀏覽器開啟 github
頁面
window.open
渲染程序中使用,當前應用開啟頁面
window.open('github.com') 複製程式碼
webview
使用 webview
可以在一個獨立的 frame 和程序裡顯示外部 web 內容

直接在index.html中使用即可
<webview id="foo" src="https://www.github.com/" style="display:inline-flex; width:640px; height:480px"></webview> 複製程式碼
模組一覽
QA
其他應用模組和API請下載
模版
electron-forge
electron-forge 包含 vue
、 react
、 Angular
等開箱即用的模版。
npm i -g electron-forge electron-forge init my-app template=react cd my-app npm run start 複製程式碼
目錄結構如下

.compilerc
是 electron-compile
的配置檔案,和 .babelrc
類似
electron-compile 是一種轉換規則,支援在應用程式中編譯js和css
electron-react-boilerplate
如果你不希望任何工具,而想要簡單地從一個模板開始構建,react-electron模版可以瞭解下。
electron-react-boilerplate 是雙 package.json
配置專案, 目錄如下

electron-vue
electron-vue 充分利用 vue-cli 作為腳手架工具,加上擁有 vue-loader 的 webpack、electron-packager 或是 electron-builder,以及一些最常用的外掛,如vue-router、vuex 等等。
目錄結構如下

更多模版
更多模版 請訪問
打包
怎麼將我們開發好的應用打包成 .app
或 .exe
的執行檔案,這就涉及到了重要的打包環節, 這裡使用 electron-quick-start 專案進行打包
目前,主流的打包工具有兩個 electron-packager 和 electron-builder
Mac打包window安裝包需下載 wine
brew install wine
如果有丟失元件,按照報錯資訊進行下載即可
electron-packager
electron-packager 把你的electron打包成可執行檔案(.app, .exe, etc)
執行 npm i electron-packager -D
進行安裝
electron-packager .
快速打包
打包詳解
electron-packager <sourcedir> <appname> --platform=<platform> --arch=<arch> --out=out --icon=assets/app.ico --asar --overwrite --ignore=.git 複製程式碼
darwin, linux, mas, win32 all ia32,x64,armv7l,arm64
第一次打包需要下載二進位制的包耗時會久一些,以後走快取就快的多了。
打包後目錄為

其中 electron-quick-start
可以直接開啟執行
快取地址
- Mac
~/Library/Caches/electron/
- Windows
$LOCALAPPDATA/electron/Cache
or~/AppData/Local/electron/Cache/
- Linux
$XDG_CACHE_HOME
or~/.cache/electron/
electron-builder
electron-builder 是一個完整的解決方案,對於macos、windows、linux下的electron app,它可以提供打包及構建的相關功能。同時,它還提供開箱即用的“自動更新”功能支援
npm i electron-builder -D
下載
electron-builder
打包
出現如下

同樣是第一次打包時間會比較長,彆著急,靜候佳音。
會預設打包在 dist
目錄,包含如下

mac
檔案裡有 執行時檔案
,預設使用asar方式進行打包
electron-builder --dir
只會生成包檔案,對測試有用

坑坑坑
第一次打包的時候會比較慢,如果你和我手欠直接退出了,再次打包的時候,恭喜你,出錯了。報錯資訊如下
• electron-builder version=20.28.2 • loaded configuration file=package.json ("build" field) • description is missed in the package.json appPackageFile=/Users/shisan/Documents/self/you-app/package.json • writing effective config file=dist/builder-effective-config.yaml • no native production dependencies • packagingplatform=darwin arch=x64 electron=2.0.7 appOutDir=dist/mac • cannot unpack electron zip file, will be re-downloaded error=zip: not a valid zip file ⨯ zip: not a valid zip file Error: /Users/shisan/Documents/self/you-app/node_modules/app-builder-bin/mac/app-builder exited with code 1 at ChildProcess.childProcess.once.code (/Users/shisan/Documents/self/you-app/node_modules/builder-util/src/util.ts:254:14) at Object.onceWrapper (events.js:272:13) at ChildProcess.emit (events.js:180:13) at maybeClose (internal/child_process.js:936:16) at Process.ChildProcess._handle.onexit (internal/child_process.js:220:5) From previous event: at unpack (/Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/out/electron/ElectronFramework.js:191:18) at Object.prepareApplicationStageDirectory (/Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/electron/ElectronFramework.ts:148:50) at /Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/platformPackager.ts:179:21 at Generator.next (<anonymous>) From previous event: at MacPackager.doPack (/Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/platformPackager.ts:166:165) at /Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/macPackager.ts:88:63 at Generator.next (<anonymous>) From previous event: at MacPackager.pack (/Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/macPackager.ts:80:95) at /Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/packager.ts:376:24 at Generator.next (<anonymous>) at xfs.stat (/Users/shisan/Documents/self/you-app/node_modules/fs-extra-p/node_modules/fs-extra/lib/mkdirs/mkdirs.js:56:16) at /Users/shisan/Documents/self/you-app/node_modules/graceful-fs/polyfills.js:287:18 at FSReqWrap.oncomplete (fs.js:171:5) From previous event: at Packager.doBuild (/Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/packager.ts:344:39) at /Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/packager.ts:314:57 at Generator.next (<anonymous>) at /Users/shisan/Documents/self/you-app/node_modules/graceful-fs/graceful-fs.js:99:16 at /Users/shisan/Documents/self/you-app/node_modules/graceful-fs/graceful-fs.js:43:10 at FSReqWrap.oncomplete (fs.js:153:20) From previous event: at Packager._build (/Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/packager.ts:285:133) at /Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/packager.ts:281:23 at Generator.next (<anonymous>) From previous event: at Packager.build (/Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/packager.ts:238:14) at build (/Users/shisan/Documents/self/you-app/node_modules/app-builder-lib/src/index.ts:58:28) at build (/Users/shisan/Documents/self/you-app/node_modules/electron-builder/src/builder.ts:227:10) at then (/Users/shisan/Documents/self/you-app/node_modules/electron-builder/src/cli/cli.ts:42:48) at runCallback (timers.js:763:18) at tryOnImmediate (timers.js:734:5) at processImmediate (timers.js:716:5) From previous event: at Object.args [as handler] (/Users/shisan/Documents/self/you-app/node_modules/electron-builder/src/cli/cli.ts:42:48) at Object.runCommand (/Users/shisan/Documents/self/you-app/node_modules/yargs/lib/command.js:237:44) at Object.parseArgs [as _parseArgs] (/Users/shisan/Documents/self/you-app/node_modules/yargs/yargs.js:1085:24) at Object.get [as argv] (/Users/shisan/Documents/self/you-app/node_modules/yargs/yargs.js:1000:21) at Object.<anonymous> (/Users/shisan/Documents/self/you-app/node_modules/electron-builder/src/cli/cli.ts:25:28) at Module._compile (internal/modules/cjs/loader.js:654:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:665:10) at Module.load (internal/modules/cjs/loader.js:566:32) at tryModuleLoad (internal/modules/cjs/loader.js:506:12) at Function.Module._load (internal/modules/cjs/loader.js:498:3) at Function.Module.runMain (internal/modules/cjs/loader.js:695:10) at startup (internal/bootstrap/node.js:201:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:516:3) npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! [email protected] dist: `electron-builder --mac --x64` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the [email protected] dist script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR!/Users/shisan/.npm/_logs/2018-08-22T06_28_55_102Z-debug.log ✖Error building interactive interface 複製程式碼
問題是在下載.zip包的時候,中斷了操作,以後所有執行打包的時候,找不到那個檔案(或者是殘缺的檔案)就報錯了,需要手動清除下快取 快取路徑在 ~/Library/Caches/electron/
常用引數
electron-builder
配置檔案寫在 package.json
中的 build
欄位中
"build": { "appId": "com.example.app", // 應用程式id "productName": "測試", // 應用名稱 "directories": { "buildResources": "build", // 構建資源路徑預設為build "output": "dist" // 輸出目錄 預設為dist }, "mac": { "category": "public.app-category.developer-tools", // 應用程式類別 "target": ["dmg", "zip"],// 目標包型別 "icon": "build/icon.icns" // 圖示的路徑 }, "dmg": { "background": "build/background.tiff or build/background.png", // 背景影象的路徑 "title": "標題", "icon": "build/icon.icns" // 圖示路徑 }, "win": { "target": ["nsis","zip"] // 目標包型別 } } 複製程式碼
使用electron-package或者electron-builder進行打包,就不需要asar手動打包了,這兩個工具已經整合進去了,以下內容只做瞭解即可
asar
簡介
asar是一種將多個檔案合併成一個檔案的類 tar 風格的歸檔格式。 Electron 可以無需解壓整個檔案,即可從其中讀取任意檔案內容。
使用
上面我們看到使用 electron-package
進行打包的時候,可以新增 --asar
引數, 它的作用就是將我們的原始碼打包成asar格式, 不使用的話就是我們的原始碼。
依舊使用 electron-quick-start
模版, 使用 electron-package
進行打包
打包後目錄中選擇應用,右鍵 顯示包內容

依次開啟路徑 Contents -> Resources -> app
就可以看到我們的原檔案了

使用 electron-package . --asar
進行打包,再次檢視app中的原始檔已經打包成 asar
檔案了

常用命令
- 打包
asar pack|p <dir> <ouput> 複製程式碼
如 asar ./ app.asar
- 解壓
asar extract|e <archive> <output> 複製程式碼
如 asar e app.asar ./
事實上,就算使用了asar打包方式,我們的原始碼依舊是暴露的,使用asar解壓仍然能輕易解密,使用webpack將程式碼壓縮混淆才是王道。
自動更新
我們使用 electron-builder 和electron-updater來完成應用的自動更新, 並不是electron裡面的 autoUpdater
流程

建立證書
無論使用哪種方式進行自動更新,程式碼簽名是必不可少的,我們使用建立本地的證書來進行測試自動更新
在其他中找到鑰匙串訪問

依次選擇 鑰匙串訪問
-> 證書助理
-> 建立證書
,建立一個新的證書

起個響亮的名字,證書型別選擇 程式碼簽名
,然後點選建立

建立完成後在鑰匙串中找到它

右鍵,選擇更改信任設定,將信任設定成 始終信任

程式碼簽名
-
沒有程式碼簽名
-
程式碼簽名
設定環境變數
執行 sudo vi ~/.bash_profile
, 新增變數
export CSC_NAME="electron_update" 複製程式碼
執行 source ~/.bash_profile
過載變數檔案
執行 echo $CSC_NAME
檢視變數有沒有生效,沒有生效的話,執行上面命令過載檔案
多次過載,依舊獲取不到變數,直接退出終端,再次登入終端即可
打包程式碼
使用 electron-builder
進行打包,專案使用 electron-quick-start
我們先新增打包的 build
配置, 在 package.json
中新增
"build": { "appId": "org.electron.com", "publish": [ { "provider": "generic", "url": "http://172.28.86.36:8080/" } ], "productName": "我的", "directories": { "output": "dist" }, "mac": { "target": [ "dmg", "zip" ] }, "win": { "target": [ "nsis", "zip" ] }, "dmg": { "backgroundColor": "red", "title": "made", "contents": [ { "x": 400, "y": 128, "type": "link", "path": "/Applications" } ] } } 複製程式碼
前面,我們說過了 build
欄位中各個引數的含義,這裡就不贅述了,這裡添加了 publish
欄位,它是配置我們自動更新的資訊,url是伺服器地址。
伺服器我們可以使用http-server模組, 快速搭建一個靜態伺服器
執行打包後,輸出

主要看下 latest-mac.yml
檔案

可以看到 version
欄位,用來表明當前應用的版本,那這個欄位是從哪來的呢?
沒錯就是我們的 package.json
檔案中 version
欄位
自動更新
在主程序中寫入以下程式碼
const {app, BrowserWindow, ipcMain} = require('electron') const { autoUpdater } = require('electron-updater') // 本地伺服器地址 const feedURL = `http://172.28.82.40:8080/` let mainWindow function createWindow () { // 建立視窗 mainWindow = new BrowserWindow({width: 800, height: 600}) mainWindow.loadFile('index.html') mainWindow.on('closed', function () { mainWindow = null }) } app.on('ready', createWindow) app.on('window-all-closed', function () { if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', function () { if (mainWindow === null) { createWindow() } }) // 監聽自定義update事件 ipcMain.on('update', (e, arg) => { checkForUpdate() }) const checkForUpdate = () => { // 設定檢查更新的 url,並且初始化自動更新 autoUpdater.setFeedURL(feedURL) // 監聽錯誤 autoUpdater.on('error', message => { sendUpdateMessage('err', message) }) // 當開始檢查更新的時候觸發 autoUpdater.on('checking-for-update', message => { sendUpdateMessage('checking-for-update', message); }) // autoUpdater.on('download-progress', function(progressObj) { sendUpdateMessage('downloadProgress', progressObj); }); // 更新下載完成事件 autoUpdater.on('update-downloaded', function(event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) { ipcMain.on('updateNow', (e, arg) => { autoUpdater.quitAndInstall(); }); sendUpdateMessage('isUpdateNow'); }); // 向服務端查詢現在是否有可用的更新 autoUpdater.checkForUpdates(); } // 傳送訊息觸發message事件 function sendUpdateMessage(message, data) { mainWindow.webContents.send('message', { message, data }); } 複製程式碼
然後更改我們的渲染程序的程式碼
const {ipcRenderer} = require ('electron'); const button = document.querySelector('#button') const ul = document.querySelector('ul') button.onclick = () => { ipcRenderer.send('update') } ipcRenderer.on('message', (event, {message,data }) => { let li = document.createElement('li') li.innerHTML = message + " <br>data:" + JSON.stringify(data) +"<hr>"; ul.appendChild(li) if (message === 'isUpdateNow') { if (confirm('是否現在更新?')) { ipcRenderer.send('updateNow'); } } }) 複製程式碼
頁面程式碼
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hello World!</title> </head> <body> <h1>Hello World!</h1> <h1>當前版本是1.0.3</h1> <ul> <li>生命週期過程展示</li> </ul> <button id="button">點選我</button> <script> // You can also require other files to run in this process require('./renderer.js') </script> </body> </html> 複製程式碼
這裡使用按鈕點選事件觸發更新操作。
直接打包,安裝這個 1.0.3
的版本。
然後將 package.json
的檔案中的 version
欄位更改成 1.0.5
,將index.html的更改成 1.0.5
,進行打包。
隨便找個地方建立個資料夾,如 test
將打包的檔案移動到 test
中,執行 http-server ./
,搭建服務
當我們點選按鈕的時候,就可以進行更新應用了,如下圖

點選ok後,靜候片刻,更新完後就打開了最新版本的應用

以上就完成了electron的自動更新。