如何使用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 select操作的日期联动实现代码
Dec 06 Javascript
js继承的实现代码
Aug 05 Javascript
jQuery+css实现百度百科的页面导航效果
Dec 16 Javascript
跟我学习javascript的作用域与作用域链
Nov 19 Javascript
又拍云 Node.js 实现文件上传、删除功能
Oct 28 Javascript
在 Vue 应用中使用 Netlify 表单功能的方法详解
Jun 03 Javascript
Layui表格监听行单双击事件讲解
Nov 14 Javascript
优化Vue中date format的性能详解
Jan 13 Javascript
JavaScript中的惰性载入函数及优势
Feb 18 Javascript
基于javascript处理二进制图片流过程详解
Jun 08 Javascript
详解node.js 事件循环
Jul 22 Javascript
Nuxt 嵌套路由nuxt-child组件用法(父子页面组件的传值)
Nov 05 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
第十节 抽象方法和抽象类 [10]
2006/10/09 PHP
PHP中ini_set与ini_get用法实例
2014/11/04 PHP
PHP+apc+ajax实现的ajax_upload上传进度条代码
2016/01/25 PHP
Thinkphp3.2.3分页使用实例解析
2016/07/28 PHP
jquery数据验证插件(自制,简单,练手)实例代码
2013/10/24 Javascript
jQuery实现单击按钮遮罩弹出对话框(仿天猫的删除对话框)
2014/04/10 Javascript
JavaScript中的replace()方法使用详解
2015/06/06 Javascript
深入浅析react native es6语法
2015/12/09 Javascript
jquery动态切换背景图片的简单实现方法
2016/05/14 Javascript
微信小程序tabBar底部导航中文注解api详解
2017/08/16 Javascript
JSON在Javascript中的使用(eval和JSON.parse的区别)详细解析
2017/09/05 Javascript
浅谈React高阶组件
2018/03/28 Javascript
Vue使用高德地图搭建实时公交应用功能(地图 + 附近站点+线路详情 + 输入提示+换乘详情)
2018/05/16 Javascript
js核心基础之闭包的应用实例分析
2019/05/11 Javascript
layui table单元格事件修改值的方法
2019/09/24 Javascript
[03:17]2016完美“圣”典风云人物:冷冷专访
2016/12/08 DOTA
Python中的异常处理相关语句基础学习笔记
2016/07/11 Python
django从请求到响应的过程深入讲解
2018/08/01 Python
python使用xlrd和xlwt读写Excel文件的实例代码
2018/09/05 Python
python 保存float类型的小数的位数方法
2018/10/17 Python
Python模块future用法原理详解
2020/01/20 Python
Python利用for循环打印星号三角形的案例
2020/04/12 Python
Keras—embedding嵌入层的用法详解
2020/06/10 Python
基于SQLAlchemy实现操作MySQL并执行原生sql语句
2020/06/10 Python
HTML5 CSS3打造相册效果附源码下载
2014/06/16 HTML / CSS
TobyDeals美国:在电子产品上获得最好的优惠和折扣
2019/08/11 全球购物
面向对象编程是如何提高软件开发水平的
2014/05/06 面试题
农救科工作职责
2013/11/27 职场文书
施工材料员岗位职责
2014/02/12 职场文书
清明节扫墓活动方案
2014/03/02 职场文书
运动会方阵口号
2014/06/07 职场文书
商业门面租房协议书
2014/11/25 职场文书
公司宣传语大全
2015/07/13 职场文书
婚庆答谢词大全
2015/09/29 职场文书
goland 恢复已更改文件的操作
2021/04/28 Golang
python微信智能AI机器人实现多种支付方式
2022/04/12 Python