electron應用自動更新

項目現狀:每次有新版本發佈時,須要用戶本身去手動點擊下載,而後手動一步步操做安裝,每次都要去從新選擇安裝目錄等,過程較爲耗時,也須要用戶去手動操做,用戶體驗不佳。javascript

方案: 採用electron-builder配合electron-updater實現自動更新

electron 的打包能夠採用electron-packager及electron-builder兩種方式實現,通過對比以後發現electron-builder有比electron-packager有更豐富的的功能,支持更多的平臺,同時也支持了自動更新。除了這幾點以外,由electron-builder打出的包更爲輕量,而且能夠打包出不暴露源碼的setup安裝程序,因此將方案選爲後者。java

步驟:

A. 安裝 electron-updater 包模塊linux

npm install electron-updater --save
複製代碼

B. 配置package.json文件裏的buildweb

"build": {
    "productName": "uWorker",   //項目名 這也是生成的exe文件的前綴名
    "appId": "xxxx",   //項目的appid
    "copyright": "xxx", //版權 信息
    "directories": {
      "output": "dist"  // 輸出的文件夾
    },
    "publish": [
      {
        "provider": "generic",  //服務器提供商 也能夠是GitHub等等 
        "url": "http://127.0.0.1/",//更新服務器地址,用本地服務作測試
      }
    ],
    "dmg": {
      "background": "../build/body-background.jpg",
      "window": {
        "x": 400,
        "y": 100,
        "width": 1100,
        "height": 709
      }
    }
    "mac": {
      "icon": "build/icons/icon.icns", // mac的圖標路徑
      "artifactName": "${productName}_setup_${version}.${ext}" // 打出來的包所帶的版本信息名
    },
    "win": {
      "icon": "build/icons/icon.ico", // windows的圖標路徑,
      // 打包出來以後的結果
      "target": [
        "nsis",
        "zip"
      ],
      "artifactName": "${productName}_setup_${version}.${ext}" // 打出來的包所帶的版本信息名
    },
    "nsis": {
        "oneClick": false, // 一鍵安裝
        "perMachine": true, // 是否開啓安裝時權限限制(此電腦或當前用戶)
        "allowElevation": true, // 容許請求提高。 若是爲false,則用戶必須使用提高的權限從新啓動安裝程序。
        "allowToChangeInstallationDirectory": true, // 容許修改安裝目錄
        "installerIcon": "../build/icons/icon.icns", // 安裝圖標
        "uninstallerIcon": "../build/icons/icon.icns", //卸載圖標
        "installerHeaderIcon": "../build/icons/icon.icns", // 安裝時頭部圖標
        "createDesktopShortcut": true, // 建立桌面圖標
        "createStartMenuShortcut": true, // 建立開始菜單圖標
    }
    "linux": {
      "icon": "build/icons",
      "artifactName": "${productName}_setup_${version}.${ext}"
    }
  }
  "scripts": {
    "builder": "electron-builder"
  }
複製代碼
  • 要打包成安裝程序的話咱們有兩種方式,a.使用NSIS工具對咱們的文件夾再進行一次打包,打包成exe; b.經過electron-builder的nsis直接打包成exe.這個nsis的配置指的是安裝過程的重要配置,若是不配置nsis那麼應用程序就會自動的安裝在C盤。沒有用戶選擇的餘地,這樣是不友好的(這裏的nsis是一些基礎的的配置,支持引入自定義的nsis腳本,來完成一些定製化的需求)。

注意: 配置了publish會生成latest.yml文件,用於自動更新的配置信息;不要本身輕易去修改該文件。若是文件有誤,須要重打包獲取新的latest.yml文件。npm

C. 打包操做json

npm run builder
複製代碼

配置了publish以後在mac上會生成.dmg的包以及latest-mac.yml文件; 在windows上會生成.exe的包以及latest.yaml, 這是實現自動更新的關鍵。根據latest.yaml裏的版本信息去判斷是否有新版本,須要進行下載安裝。windows

// windows 下的latest.yml
version: 2.8.0

files:

        - url: 2.8.0.exe

        sha512:  FmSi6nU1PJ1LRQIBjuvaw0TG32KHPM76FlGMRcWrSNOs7XGeaUALspgOKknTFYzuqmjEJk6JiHGNOm/UH+wDLw==

size: 42463133

// 當target選擇nsis和zip等時候,此時的files裏會有多項,因此path是指定下載時候對於服務器上的下載文件地址,若是是mac的話指的的.zip. sha512參數是在下載時候作校驗的,以確保下載的是正確的新版本
path: 2.8.0.exe

sha512: FmSi6nU1PJ1LRQIBjuvaw0TG32KHPM76FlGMRcWrSNOs7XGeaUALspgOKknTFYzuqmjEJk6JiHGNOm/UH+wDLw==

releaseDate: '2020-01-21T02:24:38.716Z'
複製代碼

D. 配置主進程main.js文件,引入 electron-updater 文件,添加自動更新檢測和事件監聽(必定要是主進程main.js文件(或主進程main中的index.js文件),不然會報錯)api

const {app, BrowserWindow,Menu,ipcMain} = require('electron')
const path = require('path')
const package = {
    version:"2.8.1",
    productEnv:"development",// development products
    feedUrl:"http://127.0.0.1/"
};

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;

// ===============================================================更新區
// ===============================================================更新區
// ===============================================================更新區
const autoUpdater = require('electron-updater').autoUpdater;
// 檢測更新,在你想要檢查更新的時候執行,renderer事件觸發後的操做自行編寫
function updateHandle() {
    let message = {
        error: 'Check update is error.',
        checking: 'checking update now.',
        updateAva: 'has new packer is downing...',
        updateNotAva: 'is the lasted packer. do not to update !',
    };
    const os = require('os');

    autoUpdater.setFeedURL(package.feedUrl);
    autoUpdater.on('error', function (error) {
        console.log('出錯了', error)
        sendUpdateMessage({cmd:'error',message:error})
    });
    autoUpdater.on('checking-for-update', function (message) {
        sendUpdateMessage({cmd:'checking-for-update',message:message})
    });
    autoUpdater.on('update-available', function (message) {
        sendUpdateMessage({cmd:'update-available',message:message})
    });
    autoUpdater.on('update-not-available', function (message) {
        sendUpdateMessage({cmd:'update-not-available',message:message})
    });

    // 更新下載進度事件
    autoUpdater.on('download-progress', function (progressObj) {
        sendUpdateMessage({cmd:'download-progress',message:progressObj})
    })
    autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl) {
        sendUpdateMessage({cmd:'update-downloaded',message:{
            releaseNotes, releaseName, releaseDate, updateUrl
        }})
    });

    ipcMain.on('isUpdateNow', (e, arg)=>{
        // sendUpdateMessage({cmd:'isUpdateNow',message:arg})
        //some code here to handle event
        autoUpdater.quitAndInstall();
    });

    ipcMain.on("checkForUpdate",(e, arg)=>{
        //執行自動更新檢查
        // sendUpdateMessage({cmd:'checkForUpdate',message:arg})
        autoUpdater.checkForUpdates();
    })

}

// 經過main進程發送事件給renderer進程,提示更新信息
function sendUpdateMessage(data) {
    mainWindow.webContents.send('message', data)
}
複製代碼
此處是在主進程中調用自動更新的api(事件觸發能夠在渲染進程中自定義而且經過ipcRenderer知會主進程),主進程中能夠在對應的事件鉤子裏拿到對應的狀態參數,如是否須要更新,下載包的進度,下載是否成功等。而後將這些通信給渲染進程,能夠將其反饋在界面上顯示。
const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on("staticData",function(event,data){
    // 獲取配置項數據
    console.log('獲取配置項數據', data)
    if(data.version) {
        Upd_version.innerText = data.version;
        if(typeof window.updateVerson == 'function') _app.innerText = data.version
    }
})

ipcRenderer.on('message',(event,data) => {
    // 初始化參數
    data = data || {};
    var message = data.message || {};
    console.log(data)

    // 需更新
    if(data.cmd == 'update-available'){
        Upd_app.style.display = "block";
        if(message.version) {
            console.log("正在更新到最新版本:v "+message.version);
            Upd_version.innerText = message.version;
        }
    }
    // 更新異常
    if(data.cmd == 'error'){
        console.log('error', data)
        Upd_lock = false;
        Upd_btn.innerText = "當即更新";

        if(message.errno == -4058){
            Upd_app.style.display = "none";
        }
    }
    // 下載中
    if(data.cmd == 'download-progress'){
        Upd_progress.value = Number(message.percent);
        Upd_percent = Number(message.percent);
    }
    // 下載完成
    if(data.cmd == 'update-downloaded'){
        // 下載完成 開始安裝
        if(Upd_percent == 100){
            Upd_btn.innerText = "安裝重啓中...";
            ipcRenderer.send('isUpdateNow');
        }
    }
});
setTimeout(function(){
    autoUpdate()
},500);
function autoUpdate(){
    if(Upd_lock) return false;
    Upd_lock = true;
    Upd_btn.innerText = "版本更新中.";
    ipcRenderer.send('checkForUpdate');
}
複製代碼
渲染進程接受這些狀態而後進行反饋,當下載成功時進詢問用戶。

E. 如今能夠去模擬測試了,以windows的爲例,安裝剛剛打包出來的2.8.0的應用,而後再將版本號改成2.8.1,而後進行打包,而且部署到publish裏設置的url服務目錄下(用了IIS開了本地服務,不過剛開始一直失敗,原來是要爲.yml添加MIME的擴展類型~)服務器

windows安裝示例markdown

效果如上視頻所示,autoUpdater會檢測到有新版本而且進行下載,在下載完成後能夠本身選擇彈框詢問用戶是否當即更新,用戶選擇肯定以後,會去調用autoUpdater.quitAndInstall(),會退出當前的應用而且安裝剛剛下載好的新版本

相關文章
相關標籤/搜索