Electron桌面應用程式從建立專案、啟動專案到打包程式的詳細過程

king0964發表於2020-11-02

開發環境

本文使用環境node12.14.1+electron10.1.5+electron-builder22.9.1+electron-updater4.3.5;

操作過程

一、新建和啟動專案

1. 直接使用官網示例,先克隆示例專案的倉庫,然後進入該倉庫;

# 克隆示例專案的倉庫
$ git clone https://github.com/electron/electron-quick-start

# 進入這個倉庫
$ cd electron-quick-start

2. 安裝專案依賴,安裝時候會比較慢,經常安裝未成功,建議先執行“npm config set registry https://registry.npm.taobao.org/”再安裝依賴;

# 安裝依賴
$ npm install

3. 啟動專案,可正常執行顯示頁面,即表示新建成功。

# 執行
$ npm start

二、打包和升級配置

1. 本文使用electron-builder方式打包和electron-updater自動升級,先安裝依賴;

# 安裝electron-builder
npm install electron-builder --save-dev

# 安裝electron-updater
npm install electron-updater --save

2. 在package.json做如下配置;

1)“publish”用於配置升級的引數,URL為升級包地址;

2)“output”為打包的路徑;

3)“nsis”為安裝過程的引數,增加安裝體驗感;注意這裡引入了icon.ico圖片,記得新建資料夾build並放入相應的圖片;

3. 修改主程式“main.js”程式碼:

1)feedUrl是升級包放置的路徑地址;

2)updateHandle是更新函式,放置了更新過程的方法(官方都有詳細解釋)和傳送資訊;

3)使用template來使選單中文化,便於使用;

// Modules to control application life and create native browser window
const { app, BrowserWindow, Menu, ipcMain } = require('electron')
const electron = require('electron')
const { autoUpdater } = require('electron-updater')
const path = require('path')

function createWindow() {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: true
    }
  })

  // and load the index.html of the app.
  mainWindow.loadFile('index.html')

  // Open the DevTools.
  mainWindow.webContents.openDevTools()

  let feedUrl = "http://192.168.0.107/electron-update/eqs";
  //檢測版本更新
  updateHandle(mainWindow, feedUrl);
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
  const menu = Menu.buildFromTemplate(template)
  Menu.setApplicationMenu(menu) // 設定選單部分

  createWindow()

  app.on('activate', function() {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function() {
  if (process.platform !== 'darwin') app.quit()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.


// 更新函式
function updateHandle(window, feedUrl) {
  mainWindow = window;
  let message = {
    error: '檢查更新出錯',
    checking: '正在檢查更新……',
    updateAva: '檢測到新版本,正在下載……',
    updateNotAva: '現在使用的就是最新版本,不用更新',
  };
  //設定更新包的地址
  autoUpdater.setFeedURL(feedUrl);
  //監聽升級失敗事件
  autoUpdater.on('error', function(error) {
    sendUpdateMessage(mainWindow, {
      cmd: 'error',
      message: error
    })
  });
  //監聽開始檢測更新事件
  autoUpdater.on('checking-for-update', function(message) {
    sendUpdateMessage(mainWindow, {
      cmd: 'checking-for-update',
      message: message
    })
  });
  //監聽發現可用更新事件
  autoUpdater.on('update-available', function(message) {
    sendUpdateMessage(mainWindow, {
      cmd: 'update-available',
      message: message
    })
  });
  //監聽沒有可用更新事件
  autoUpdater.on('update-not-available', function(message) {
    sendUpdateMessage(mainWindow, {
      cmd: 'update-not-available',
      message: message
    })
  });

  // 更新下載進度事件
  autoUpdater.on('download-progress', function(progressObj) {
    sendUpdateMessage(mainWindow, {
      cmd: 'download-progress',
      message: progressObj
    })
  });
  //監聽下載完成事件
  autoUpdater.on('update-downloaded', function(event, releaseNotes, releaseName, releaseDate, updateUrl) {
    sendUpdateMessage(mainWindow, {
      cmd: 'update-downloaded',
      message: {
        releaseNotes,
        releaseName,
        releaseDate,
        updateUrl
      }
    })
    //退出並安裝更新包
    autoUpdater.quitAndInstall();
  });

  //接收渲染程式訊息,開始檢查更新
  ipcMain.on("checkForUpdate", (e, arg) => {
    //執行自動更新檢查
    // sendUpdateMessage(mainWindow, {cmd:'checkForUpdate',message:arg})
    autoUpdater.checkForUpdates();
  })
}
//給渲染程式傳送訊息
function sendUpdateMessage(mainWindow, text) {
  mainWindow.webContents.send('message', text)
}
/**
 * 註冊鍵盤快捷鍵
 * 其中:label: '切換開發者工具',這個可以在釋出時註釋掉
 */
let template = [{
    label: 'Edit ( 操作 )',
    submenu: [{
      label: 'Copy ( 複製 )',
      accelerator: 'CmdOrCtrl+C',
      role: 'copy'
    }, {
      label: 'Paste ( 貼上 )',
      accelerator: 'CmdOrCtrl+V',
      role: 'paste'
    }, {
      label: 'Reload ( 重新載入 )',
      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: 'Window ( 視窗 )',
    role: 'window',
    submenu: [{
      label: 'Minimize ( 最小化 )',
      accelerator: 'CmdOrCtrl+M',
      role: 'minimize'
    }, {
      label: 'Close ( 關閉 )',
      accelerator: 'CmdOrCtrl+W',
      role: 'close'
    }, {
      label: '切換開發者工具',
      accelerator: (function() {
        if (process.platform === 'darwin') {
          return 'Alt+Command+I'
        } else {
          return 'Ctrl+Shift+I'
        }
      })(),
      click: function(item, focusedWindow) {
        if (focusedWindow) {
          focusedWindow.toggleDevTools()
        }
      }
    }, {
      type: 'separator'
    }]
  },
  {
    label: 'Help ( 幫助 ) ',
    role: 'help',
    submenu: [{
      label: 'FeedBack ( 意見反饋 )',
      click: function() {
        electron.shell.openExternal('https://forum.iptchain.net')
      }
    }]
  }
]

4. 修改首頁“index.html” ,增加傳送檢測升級功能:

1)增加“sendMessage.js”,用來檢測更新;該例項是開啟或重新載入就檢測更新,可自行根據實際情況修改檢測的方式;

window.onload = function() {
  let ipcRenderer = require("electron").ipcRenderer;
  //接收主程式版本更新訊息
  ipcRenderer.on("message", (event, arg) => {
    console.log(arg);
    if ("update-available" == arg.cmd) {
      //顯示升級對話方塊
      console.log('update-available');
    } else if ("download-progress" == arg.cmd) {
      //更新升級進度
      console.log(arg.message.percent);
    } else if ("error" == arg.cmd) {
      console.log('更新失敗');
    }
    // }
  });
  //20秒後開始檢測新版本
  let timeOut = window.setTimeout(() => {
    ipcRenderer.send("checkForUpdate");
  }, 20000);
  clearTimeout;
}

2)“index.html”作如下修改,加入引入上面js的程式碼:

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  <title>Hello World!</title>
</head>

<body>
  <h1>Hello World2!</h1>
  We are using Node.js <span id="node-version"></span>,
  Chromium <span id="chrome-version"></span>,
  and Electron <span id="electron-version"></span>.
  <!-- You can also require other files to run in this process -->
  <script src="./renderer.js"></script>
  <script src="utils/sendMessage.js"></script>
</body>

</html>

 

三、打包程式並演示升級過程

1. 執行命令“npm run build”,第一次在build的時候會因為下載檔案而超時,可根據提示的網址下載相應的檔案並進行手工安裝;

1)下載“electron-v10.1.3-win32-x64”時(後面升級到了10.1.5,操作一樣),可根據顯示的(圖1)url直接下載(並下載相應的SHASUMS256.txt),然後將“SHASUMS256.txt”修改為“SHASUMS256.txt-10.1.3”,並把這兩個檔案拷入圖2位置;

2)下載“winCodeSign-2.6.0”時,可根據顯示的(圖1)url直接下載,然後把檔案拷入圖2位置;

3)下載“nsis-3.0.4.1”時,可根據顯示的(圖1)url直接下載,然後把檔案拷入圖2位置;

2. 再次執行命令“npm run build”,等待執行完畢,在release檔案下生成exe檔案,然後安裝“test-demo Setup 1.0.0.exe”版本;

3. 修改“packgage.json”的屬性“version”為2.0.0,然後執行命令“npm run build”,生成2.0安裝版本;

4.  把升級檔案“latest.yml”和“test-demo Setup 2.0.0.exe”放到設定的升級路徑下;

5. 每次開啟程式或者點選選單的“Reload”,過20S後都會檢測是否有升級包,並根據實際情況返回資訊;

6. 如果升級成功會退出程式並跳到安裝新程式的視窗,一步步確定即可。

 

相關文章