如何使用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 相关文章推荐
js 多种变量定义(对象直接量,数组直接量和函数直接量)
May 24 Javascript
javascript动态加载三
Aug 22 Javascript
事件委托与阻止冒泡阻止其父元素事件触发
Sep 02 Javascript
JavaScript中判断变量是数组、函数或是对象类型的方法
Feb 25 Javascript
遍历json 对象的属性并且动态添加属性的实现
Dec 02 Javascript
js实现自动轮换选项卡
Jan 13 Javascript
JS中定位 position 的使用实例代码
Aug 06 Javascript
详细分析jsonp的原理和实现方式
Nov 20 Javascript
详解Vue组件之作用域插槽
Nov 22 Javascript
如何为vue的项目添加单元测试
Dec 19 Javascript
微信小程序实现订单倒计时
Nov 01 Javascript
layui内置模块layim发送图片添加加载动画的方法
Sep 23 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
如何用phpmyadmin设置mysql数据库用户的权限
2012/01/09 PHP
PHP函数preg_match_all正则表达式的基本使用详细解析
2013/08/31 PHP
php模拟ping命令(php exec函数的使用方法)
2013/10/25 PHP
PHP遍历目录并返回统计目录大小
2014/06/09 PHP
PHP获取时间排除周六、周日的两个方法
2014/06/30 PHP
ThinkPHP单字母函数(快捷方法)使用总结
2014/07/23 PHP
微信公众号点击菜单即可打开并登录微站的实现方法
2014/11/14 PHP
jQuery制作的别致导航有阴影背景高亮模式窗口
2014/04/15 Javascript
JavaScript日期时间与时间戳的转换函数分享
2015/01/31 Javascript
jQuery Easyui 验证两次密码输入是否相等
2016/05/13 Javascript
BootStrap智能表单实战系列(四)表单布局介绍
2016/06/13 Javascript
d3.js实现简单的网络拓扑图实例代码
2016/11/06 Javascript
Bootstrap按钮组实例详解
2017/07/03 Javascript
cropper js基于vue的图片裁剪上传功能的实现代码
2018/03/01 Javascript
解决vue-cli项目webpack打包后iconfont文件路径的问题
2018/09/01 Javascript
Angular项目如何升级至Angular6步骤全纪录
2018/09/03 Javascript
Vuejs开发环境搭建及热更新【推荐】
2018/09/07 Javascript
vue实现鼠标移入移出事件代码实例
2019/03/27 Javascript
vue-cli3 配置开发与测试环境详解
2019/05/17 Javascript
浅谈TypeScript 用 Webpack/ts-node 运行的配置记录
2019/10/11 Javascript
python中的__slots__使用示例
2015/02/26 Python
python通过函数属性实现全局变量的方法
2015/05/16 Python
详解Python如何获取列表(List)的中位数
2016/08/12 Python
Python 删除连续出现的指定字符的实例
2018/06/29 Python
python修改txt文件中的某一项方法
2018/12/29 Python
Python实现计算长方形面积(带参数函数demo)
2020/01/18 Python
Django与pyecharts结合的实例代码
2020/05/13 Python
解决django 向mysql中写入中文字符出错的问题
2020/05/18 Python
HTML5 Canvas+JS控制电脑或手机上的摄像头实例
2014/05/03 HTML / CSS
房地产销售大学生自我评价分享
2013/11/11 职场文书
企划专员岗位职责
2013/12/09 职场文书
故意伤害人身损害赔偿协议书
2014/11/19 职场文书
2016年高校自主招生自荐信范文
2015/03/24 职场文书
Python还能这么玩之用Python做个小游戏的外挂
2021/06/04 Python
Redis分布式锁的7种实现
2022/04/01 Redis
Ubuntu Server 安装Tomcat并配置systemctl
2022/04/28 Servers