Электрон реализует онлайн-обновление (в полном объеме) и наступает на ямы

Electron

предисловие

Недавно помогал компании разрабатывать десктопные приложения.Выбирал из NW.js и Electron,и в итоге остановился на Electron.Ничего особо сложного.Vue scaffolding+vue-cli-plugin-electron-builder это челнок,запишите онлайн обновление и шаг на некоторые из ям, я сделаю резюме для себя кстати, и простите меня за любые недостатки.

основное введение

Что такое электрон?

Фреймворк для разработки кроссплатформенных настольных приложений. Виндовс | Мак | Линукс

Зачем его использовать, какие плюсы и минусы?

преимущество

  • Быстро упаковывайте приложения vue/react
  • Кроссплатформенность

недостаток

  • Большой размер, занимает много памяти

Как общаться

Технические предпосылки для сетевых обновлений

Техническая предпосылка схемы, обсуждаемой в этой статье, заключается в следующем: Приложение Electron, созданное с помощью скаффолдинга vue/cli + плагин vue-cli-plugin-electron-builder (Использование фреймворка Electron, прямой импорт проекта vue или использование vue-electron, принцип аналогичен и выходит за рамки этой статьи)

Программа онлайн-обновлений

Обновление с помощью плагина electronic-updater

Установить плагин

npm install electron-updater --save

настроить публикацию

В файле vue.config.js добавьте публикацию в конфигурацию сборщика следующим образом:

Элемент публикации настроен на создание файла last.yml после упаковки, который используется для оценки обновления версии. Генерируется в процессе упаковки.Во избежание ошибок автоматического обновления,запрещено изменять содержимое файла после генерации.Если файл неверный,его нужно переупаковать и сгенерировать.Обратите внимание, что здесь есть яма.Если адрес обновления сервера часто меняется, рекомендуется не заполнять здесь url.После того, как основной процесс получит url, сбросьте его через апи. В файле last.yml записывается номер версии, путь к пакету обновления, размер пакета, дата и т. д.

код основного процесса

Код плагина electronic-updater должен выполняться в основном процессе

import { autoUpdater } from 'electron-updater'
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
function updateHandle (updateConfig) {
  let message = {
    error: 'update error',
    checking: 'updating...',
    updateAva: 'fetch new version and downloading...',
    updateNotAva: 'do not to update'
  }
  // 设置服务器更新地址
  autoUpdater.setFeedURL({
    provider: 'generic',
    url: updateConfig.download
  })
  autoUpdater.on('error', function () {
    sendUpdateMessage(message.error)
  })
  autoUpdater.on('checking-for-update', function () {
    sendUpdateMessage(message.checking)
  })
  // 版本检测结束,准备更新
  autoUpdater.on('update-available', function (info) {
    sendUpdateMessage(message.updateAva)
  })
  autoUpdater.on('update-not-available', function (info) {
    sendUpdateMessage(message.updateNotAva)
  })
  // 更新下载进度事件
  autoUpdater.on('download-progress', function (progressObj) {
    console.log('下载进度百分比>>>', progressObj.percent)
  })
  // 下载完成
  autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
    // 退出且重新安装  
    autoUpdater.quitAndInstall()
  })
  ipcMain.on('checkForUpdate', () => {
    // 执行自动更新检查
    autoUpdater.checkForUpdates()
  })
  // 通过main进程发送事件给renderer进程,提示更新信息
  function sendUpdateMessage (text) {
mainWindow.webContents.send('message', text)
  }
}
export default updateHandle

код процесса рендеринга

// ####### 请保证updateHandle方法在主进程已经调用过一遍,事件监听都存在
// 检测更新
ipcRenderer.send('checkForUpdate')

Чтение компонента индикатора выполнения

Основной процесс отправляет данные о ходе выполнения на страницу процесса рендеринга для отображения текущего хода обновления.

<template>
  <div class="update">
      <div class="con">
        <el-progress :percentage="percentage"></el-progress>
        <span>正在检测版本,请稍后</span>
      </div>
  </div>
</template>
<script>
const { ipcRenderer } = require('electron')
export default {
  name: 'update-loading',
  data () {
    return {
      percentage: 0
    }
  },
  created () {
    let vm = this
    ipcRenderer.on('download-progress', (e, data) => {
      vm.percentage = Number(data)
    })
  }
}

Яма в практике онлайн-обновлений

Правильная ссылка на autoUpdater

// 不要引用electron自带的autoUpdater
// const { autoUpdater } = require('electron')
import { autoUpdater } from 'electron-updater'

Серверный файл last-mac.yml не найден

решение: Сервер добавляет сопоставление типов MIME формата файла yml

Неверный номер локальной версии

Сам баг электрон-апдейтера пойдет на получение версии Электрона; решение: Измените код функции isUpdateAvailable в appUpdater.js в электронном обновлении.

const pkg=require('../../../package.json');
// const isLatestVersionNewer = (0, _semver().gt)(latestVersion, currentVersion);
const isLatestVersionNewer = (0, _semver().gt)(latestVersion, pkg.version);

Среда разработки сообщает, что файл dev-app-update.yml не существует.

решение: В методе updateHandle добавьте следующий код, адрес — это путь к локально упакованному файлу app-update.yml.

if (process.env.NODE_ENV === 'development' && !isMac) {
  autoUpdater.updateConfigPath = path.join(__dirname, 'win-unpacked/resources/app-update.yml')
  // mac的地址是'Contents/Resources/app-update.yml'
}

Ошибка обновления из-за загрузки кеша пакетов

решение: Значение updaterCacheDirName совпадает со значением updaterCacheDirName в src/main/app-update.yml В Windows файл, аналогичный //C:\Users\Administrator\AppData\Local\electron-updater1\pending, будет создан для хранения обновленных скачанных файлов.Файлы "*.exe" и "update-info.json" Перед каждым обновлением удаляйте локальный инсталляционный пакет В методе updateHandle добавьте следующий код

// 更新前,删除本地安装包
let updaterCacheDirName = 'electron-admin-updater'
const updatePendingPath = path.join(autoUpdater.app.baseCachePath, updaterCacheDirName, 'pending')
fs.emptyDir(updatePendingPath)

Обновления системы Mac требуют подписи кода

Я не буду здесь углубляться, если вам интересно, вы можете узнать большесегмент fault.com/ah/119000001…

Обновите полный код приложения

import { ipcMain } from 'electron'
import { autoUpdater } from 'electron-updater'
// win是所有窗口的引用
import { createWindow, win } from '../background'
const path = require('path') // 引入path模块
const _Store = require('electron-store')
const fs = require('fs-extra')
const isMac = process.platform === 'darwin'
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
function updateHandle (updateConfig = undefined) {
  // electron缓存
  let localStore = new _Store()
  // 更新配置
  updateConfig = updateConfig !== undefined ? updateConfig : localStore.get('updateConfig')
  // 更新前,删除本地安装包 ↓
  let updaterCacheDirName = 'electron-admin-updater'
  const updatePendingPath = path.join(autoUpdater.app.baseCachePath, updaterCacheDirName, 'pending')
  fs.emptyDir(updatePendingPath)
   // 更新前,删除本地安装包 ↑
  let message = {
    error: 'update error',
    checking: 'updating...',
    updateAva: 'fetch new version and downloading...',
    updateNotAva: 'do not to update'
  }
  // 本地开发环境,改变app-update.yml地址
  if (process.env.NODE_ENV === 'development' && !isMac) {
    autoUpdater.updateConfigPath = path.join(__dirname, 'win-unpacked/resources/app-update.yml')
  }
  // 设置服务器更新地址
  autoUpdater.setFeedURL({
    provider: 'generic',
    url: updateConfig.download
  })
  autoUpdater.on('error', function () {
    sendUpdateMessage(message.error)
  })
  autoUpdater.on('checking-for-update', function () {
    sendUpdateMessage(message.checking)
  })
  // 准备更新,打开进度条读取页面,关闭其他页面
  autoUpdater.on('update-available', function (info) {
    sendUpdateMessage(message.updateAva)
    createWindow('update-loading', {
      width: 500,
      height: 300,
      minWidth: 720,
      resizable: false,
      fullscreenable: false,
      frame: false
    })
    for (let key in win) {
      if (key !== 'update-loading') {
        win[key] && win[key].close()
      }
    }
  })
  autoUpdater.on('update-not-available', function (info) {
    sendUpdateMessage(message.updateNotAva)
  })
  // 更新下载进度
  autoUpdater.on('download-progress', function (progressObj) {
    win['update-loading'] && win['update-loading'].webContents.send('download-progress', parseInt(progressObj.percent))
  })
  // 更新完成,重启应用
  autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
    ipcMain.on('isUpdateNow', (e, arg) => {
      // some code here to handle event
      autoUpdater.quitAndInstall()
    })
    win['update-loading'] && win['update-loading'].webContents.send('isUpdateNow')
  })
  ipcMain.on('checkForUpdate', () => {
    // 执行自动更新检查
    autoUpdater.checkForUpdates()
  })
  // 通过main进程发送事件给renderer进程,提示更新信息
  function sendUpdateMessage (text) {
    win['update-loading'] && win['update-loading'].webContents.send('message', message.error)
  }
}
export default updateHandle