如何使用electron-builder及electron-updater给项目配置自动更新


Posted in Javascript onDecember 24, 2018

说明:

本文的自动更新功能使用的项目为 electron-vue 脚手架搭建一个默认项目。

参考的文章如下:

  • electron-vue 中文文档
  • electron-builder 文档
  • Windows 下支持自动更新的 Electron 应用脚手架
  • Electron 文档 Docs / API / autoUpdater

开始:新建一个 electron 项目

首先你得有一个需要配置自动更新功能的 electron 项目。这里我为了测试自动更新功能是否成功搭建使用的是 electron-vue 脚手架搭建的项目。

搭建过程如下:

# 安装 vue-cli 和 脚手架样板代码
npm install -g vue-cli
vue init simulatedgreg/electron-vue autoUpdataTest

# 安装依赖并运行你的程序
cd autoUpdataTest
npm install
npm run dev

程序运行后的界面如下:

如何使用electron-builder及electron-updater给项目配置自动更新

脚手架生成的文件结构:

|- autoUpdateTest
 |- .electron-vue # 压缩及运行环境的配置文件
 |- build # 
 |- icons # 图标文件
 |- ... # 打包生成的文件在此处 
 |- dist # 用 webpack 压缩项目后生成的压缩文件在此处
 |- node_modules
 |- src # 资源文件
 |- main # 主进程
 |- renderer # 渲染进程
 |- index.ejx # 入口文件
 |- static # 静态资源
 |- .babelrc
 |- .gitignore
 |- .travis
 |- appveyor.yml
 |- package-lock.json # npm 自动生成的文件
 |- package.json
 |- README.md

使用 electron-builder 最关键的配置在 package.json 里:(为了观察我们所需要的地方,把此篇文章里不需要关注的代码给删掉了。)

{
 "name": "autoupdatetest",
 "version": "0.0.0",
 "author": "wonder <xxxxxxxxx@qq.com>",
 "description": "An electron-vue project",
 "main": "./dist/electron/main.js", 
 "scripts": {
 "build": "node .electron-vue/build.js && electron-builder",
 "dev": "node .electron-vue/dev-runner.js",
 },
 "build": {
 "productName": "autoupdateteset",
 "appId": "org.simulatedgreg.electron-vue",
 "directories": {
  "output": "build"
 },
 "files": "dist/electron/**/*",
 "win": {
  "icon": "build/icons/icon.ico"
 }
 },
 "dependencies": {
 },
 "devDependencies": {
 }
}

解析:

前四行是一般的 package.json 会有的:

  • name — 项目名
  • version — 版本号
  • author — 开发人员及邮箱号
  • description — 项目描述 。

下面重点看后面的内容:electron-builder详细配置文档

  • "main": "./dist/electron/main.js" — 这里的 main 入口文件指的是用 electron-builder 打包主程序的入口文件,这里的路径是使用 webpack 压缩项目后文件输出的位置。
  • scripts — 脚本
    • "build": "node .electron-vue/build.js && electron-builder" — 生产环境,压缩打包项目。先运行 .electron-vue 文件夹下的 build.js 脚本对项目进行压缩,输出的位置在 dist 文件夹下,然后再使用配置好的 electron-builder 对 dist 文件夹下的文件进行打包生成应用的安装包。
    • "dev": "node .electron-vue/dev-runner.js" — 开发环境,可以运行我们的项目并测试。这里使用了热更新,改动代码不需要刷新即可看到应用的改变。
  • build — electron-builder 配置项
    • "productName": "autoupdateteset", — 工程项目名
    • "appId": "org.simulatedgreg.electron-vue" — 应用程序 ID。强烈建议设置显式ID。
    • directories
    • "output": "build" — 生成的安装包输出目录。
    • "files": "dist/electron/**/*" — 安装包源文件目录,支持多路径(数组)
    • "win": { "icon": "build/icons/icon.ico"} — 打包成 Windows 系统下安装包应用程序图标路径,还有别的配置项可以在详细文档中查看。

有关 electron-vue 的使用的更详细的说明请看 中文文档。

自动更新

安装依赖

自动更新功能的实现依赖 electron-builderelectron-updater

因为我们是用的electron-builder脚手架生成的项目,已经有 electron-builder 依赖了,所以只需要安装 electron-updater

# 目录 E:\GitHub\autoupdateteset
npm i electron-updater --save # 必须安装为运行依赖,否则运行会出错

配置 package.json

为了配合打包 package.json 需要给 build 新增配置项:

"build": {
 "publish": [
  {
   "provider": "generic",
   "url": "http://127.0.0.1:5500/" #这里是我本地开的服务器的地址
  }
 ],
 ...
}

主进程(参考:electron 中文文档)

主进程的入口文件是 src/main/index.js

import { 
app,   // app 模块是为了控制整个应用的生命周期设计的。
BrowserWindow, // BrowserWindow 类让你有创建一个浏览器窗口的权力。
ipcMain 
} from 'electron';

// 引入自动更新模块
const { autoUpdater } = require('electron-updater');
// 不支持 ES6 则用如下方式引入
// const autoUpdater = require("electron-updater").autoUpdater

const feedUrl = `http://127.0.0.1:5500/win32`; // 更新包位置


/**
 * Set `__static` path to static files in production
 * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
 */
if (process.env.NODE_ENV !== 'development') {
 global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\');
}

let mainWindow, webContents;
const winURL = process.env.NODE_ENV === 'development' ?
 `http://localhost:9080` :
 `file://${__dirname}/index.html`;

function createWindow() {
 /**
  * Initial window options
  */
 mainWindow = new BrowserWindow({
  height: 563,
  useContentSize: true,
  width: 1000
 });

 mainWindow.loadURL(winURL);

 webContents = mainWindow.webContents;

 mainWindow.on('closed', () => {
  mainWindow = null;
 });
}

// 主进程监听渲染进程传来的信息
ipcMain.on('update', (e, arg) => {
 console.log("update");
 checkForUpdates();
});

let checkForUpdates = () => {
 // 配置安装包远端服务器
 autoUpdater.setFeedURL(feedUrl);

 // 下面是自动更新的整个生命周期所发生的事件
 autoUpdater.on('error', function(message) {
  sendUpdateMessage('error', message);
 });
 autoUpdater.on('checking-for-update', function(message) {
  sendUpdateMessage('checking-for-update', message);
 });
 autoUpdater.on('update-available', function(message) {
  sendUpdateMessage('update-available', message);
 });
 autoUpdater.on('update-not-available', function(message) {
  sendUpdateMessage('update-not-available', message);
 });

 // 更新下载进度事件
 autoUpdater.on('download-progress', function(progressObj) {
  sendUpdateMessage('downloadProgress', progressObj);
 });
 // 更新下载完成事件
 autoUpdater.on('update-downloaded', function(event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
  sendUpdateMessage('isUpdateNow');
  ipcMain.on('updateNow', (e, arg) => {
   autoUpdater.quitAndInstall();
  });
 });

 //执行自动更新检查
 autoUpdater.checkForUpdates();
};

// 主进程主动发送消息给渲染进程函数
function sendUpdateMessage(message, data) {
 console.log({ message, data });
 webContents.send('message', { message, data });
}

app.on('ready', () => {
 createWindow();
});

app.on('window-all-closed', () => {
 if (process.platform !== 'darwin') {
  app.quit();
 }
});

app.on('activate', () => {
 if (mainWindow === null) {
  createWindow();
 }
});

渲染进程

渲染进程的入口文件是 src/renderer/index.js

这里我们主要修改 App.vue,将原来的内容全删掉并使更新的整个周期在界面上打印出来。

<template>
 <div id="app">
  <!-- <router-view></router-view> -->
  <button @click="autoUpdate()">获取更新</button>
  <ol id="content">
   <li>生命周期过程展示</li>
  </ol>
 </div>
</template>

<script>
// import { ipcRenderer } from 'electron';
const { ipcRenderer } = require('electron');
export default {
 name: 'my-project1',
 mounted() {
  var _ol = document.getElementById("content");
  ipcRenderer.on('message',(event,{message,data}) => {
   let _li = document.createElement("li");
   _li.innerHTML = message + " <br>data:" + JSON.stringify(data) +"<hr>";
   _ol.appendChild(_li);
   if (message === 'isUpdateNow') {
    if (confirm('是否现在更新?')) {
     ipcRenderer.send('updateNow');
    }
   }
  });
 },
 methods: {
 autoUpdate() {
  ipcRenderer.send('update');
 }
 }
};
</script>

<style>
/* CSS */
</style>

显示的界面如下:

如何使用electron-builder及electron-updater给项目配置自动更新

自动更新过程简单介绍

1.将 webpack.json 里的版本号先改为 0.0.1,然后npm run build生成一个版本为0.0.1的安装包。

如何使用electron-builder及electron-updater给项目配置自动更新 

如何使用electron-builder及electron-updater给项目配置自动更新

注意上面一步会生成一个latest.yml文件,autoUpdate 实际上通过检查该文件中安装包版本号与当前应用版本号对比来进行更新判断的。

latest.yml文件内容如下:

如何使用electron-builder及electron-updater给项目配置自动更新

2.然后将上一步生成的安装包放在本地开启的服务器文件夹下,对应你在主程序入口文件中配置的服务器位置。

如何使用electron-builder及electron-updater给项目配置自动更新

3.将 package.json 中的版本号改回0.0.0,再npm run build一遍,运行 build 文件夹下的 exe 安装包,就将软件安装在你电脑里面了。点击安装完成后桌面上的快捷方式,再次点击上面的获取更新的按钮就可以看到显示在界面的自动更新生命周期了。(但这里因为会给你直接自动更新,所以会一闪而过,你可以在 autoUpdate 的各个生命周期事件里设置主进程与渲染进程通信,则可以一步一步观察到整个自动更新的生命周期了。)

通过测试总结 autoUpdate 生命周期图

如何使用electron-builder及electron-updater给项目配置自动更新

更新过程展示

1、无版本更新

如何使用electron-builder及electron-updater给项目配置自动更新 

如何使用electron-builder及electron-updater给项目配置自动更新

2、有版本更新

如何使用electron-builder及electron-updater给项目配置自动更新 

点击取消后会先不更新,在应用关闭后更新:

如何使用electron-builder及electron-updater给项目配置自动更新 

点击确认后则会直接更新:

如何使用electron-builder及electron-updater给项目配置自动更新

踩过的坑

1、主进程与渲染进程通信

最开始我是直接在主进程直接运行更新

如何使用electron-builder及electron-updater给项目配置自动更新 

然后想在渲染进程中打印主进程传过来的消息,但是发现只有 isUpdateNow 事件运行时才有日志显示。

结果发现原来主进程与渲染进程之间通信必须在渲染进程已经运行的时候(即那个界面完全显示出来)才能够进行。所以我将自动更新改为界面按钮触发,这样才能检测到自动更新的整个流程。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JQuery验证工具类搜集整理
Jan 16 Javascript
jQuery中(function(){})()执行顺序的理解
Mar 05 Javascript
JavaScript设计模式之观察者模式(发布者-订阅者模式)
Sep 24 Javascript
JS+CSS实现可以凹陷显示选中单元格的方法
Mar 02 Javascript
jQuery插件slides实现无缝轮播图特效
Apr 17 Javascript
jQuery ajax的功能实现方法详解
Jan 06 Javascript
js 调用百度分享功能
Feb 27 Javascript
JS实现颜色动态淡化效果
Mar 06 Javascript
使用ionic(选项卡栏tab) icon(图标) ionic上拉菜单(ActionSheet) 实现通讯录界面切换实例代码
Oct 20 Javascript
JS 音频可视化插件Wavesurfer.js的使用教程
Oct 31 Javascript
layui加载表格,绑定新增,编辑删除,查看按钮事件的例子
Sep 06 Javascript
VUE项目axios请求头更改Content-Type操作
Jul 24 Javascript
Vue.js组件高级特性实例详解
Dec 24 #Javascript
JavaScript模板引擎原理与用法详解
Dec 24 #Javascript
jQuery实现的简单日历组件定义与用法示例
Dec 24 #jQuery
原生js实现Flappy Bird小游戏
Dec 24 #Javascript
node错误处理与日志记录的实现
Dec 24 #Javascript
详解如何在vscode里面调试js和node.js的方法步骤
Dec 24 #Javascript
@angular前端项目代码优化之构建Api Tree的方法
Dec 24 #Javascript
You might like
用Simple Excel导出xls实现方法
2012/12/06 PHP
详解PHP中的PDO类
2015/07/06 PHP
PHP使用file_get_content设置头信息的方法
2016/02/14 PHP
Laravel框架中自定义模板指令总结
2017/12/17 PHP
php中上传文件的的解决方案
2018/09/25 PHP
yii框架结合charjs统计上一年与当前年数据的方法示例
2020/04/04 PHP
Thinkphp 框架扩展之驱动扩展实例分析
2020/04/27 PHP
推荐17个优美新鲜的jQuery的工具提示插件
2012/09/14 Javascript
js使下拉列表框可编辑不止是选择
2013/12/12 Javascript
dreamweaver 8实现Jquery自动提示
2014/12/04 Javascript
javascript表单处理具体实现代码(表单、链接、按钮)
2016/05/07 Javascript
JavaScript ES6中CLASS的使用详解
2016/11/22 Javascript
jQuery实现鼠标滑过图片移动特效
2016/12/08 Javascript
微信小程序  http请求封装详解及实例代码
2017/02/15 Javascript
react native带索引的城市列表组件的实例代码
2017/08/08 Javascript
解决jquery appaend元素中id绑定事件失效的问题
2017/09/12 jQuery
JS表单传值和URL编码转换
2018/03/03 Javascript
vue history 模式打包部署在域名的二级目录的配置指南
2019/07/02 Javascript
javascript二维数组和对象的深拷贝与浅拷贝实例分析
2019/10/26 Javascript
js实现浏览器打印功能的示例代码
2020/07/15 Javascript
[01:01:13]2018DOTA2亚洲邀请赛 4.5 淘汰赛 Mineski vs VG 第三场
2018/04/06 DOTA
[43:36]Liquid vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python创建线程示例
2014/05/06 Python
Python实现向QQ群成员自动发邮件的方法
2014/11/19 Python
编写Python爬虫抓取豆瓣电影TOP100及用户头像的方法
2016/01/20 Python
小小聊天室Python代码实现
2016/08/17 Python
利用python生成一个导出数据库的bat脚本文件的方法
2016/12/30 Python
Python 爬虫之Beautiful Soup模块使用指南
2018/07/05 Python
selenium2.0中常用的python函数汇总
2019/08/05 Python
解决html5中video标签无法播放mp4问题的办法
2017/05/07 HTML / CSS
微信小程序之html5 canvas绘图并保存到系统相册
2019/06/20 HTML / CSS
电子信息专业学生自荐信
2013/11/09 职场文书
投资意向协议书
2015/01/29 职场文书
党性教育心得体会(共6篇)
2016/01/21 职场文书
小学体育跳绳课教学反思
2016/02/16 职场文书
初中物理教学反思
2016/02/19 职场文书