作者:钟离,酷家乐PC客户端负责人

原文地址:webfe.kujiale.com/electron-au…

酷家乐客户端:下载地址 www.kujiale.com/activity/13…

文章背景:在酷家乐客户端在V12改版成功后,我们积累了许多的宝贵的经验和最佳实践。前端社区里关于Electron知识相对较少,因此希望将这些内容以系列文章的形式分享出来。

系列文章:

  • 【Electron】酷家乐客户端开发实践分享 — 入坑篇
  • 【Electron】酷家乐客户端开发实践分享 — 软件自动更新
  • 【Electron】酷家乐客户端开发实践分享 — 浏览器启动客户端
  • 不定期更新...

更新原理

在讲客户端更新方案之前,我们先了解一下web和客户端更新的原理

web应用

在web应用的世界里,我们通常会更新web服务器上的前端代码(模板、HTML,也可能是js、css),来发布新的功能。在此之后用户再访问我们的web服务器,拿到的已经是更新过后的前端代码了。

web应用更新如此方便,得益于它中心化存储的方式:

  1. web应用的前端代码,一般集中储存在服务器或云服务上
  2. 浏览器每次都会都会去服务器拉取最新的资源,用户本机实际上没有持久化储存web应用的代码

浏览器缓存也算是在用户本机存储了前端代码,但是在web应用需要更新的时候,肯定是会禁用缓存的,否则这次发布对有缓存的用户无效。

客户端

和web应用的中心化储存不同,客户端的代码实际上是一种分布式存储,每个用户电脑上都有一份完整的代码文件,有点像git

用户在电脑上安装客户端,实际上会将客户端代码文件持久储存到本机。例如在MacOS上,代码文件存放在/Applications目录下。

客户端内嵌web页面的更新方式,和上面讲到的web应用更新是一样的,不再赘述(参考移动APP内嵌的H5页面更新)

结论

web应用的更新,实际上是更新服务端代码文件

客户端的更新,实际上是更新用户电脑上代码文件

具体实现

Electron官网有关于更新的教程 Updating Applications,但是都不能满足业务需求:

  1. update.electron.org,代码必须托管在github上,pass
  2. electron-builder,windows下只支持NSIS,而且需要搭建HTTP服务。更新程序UI和交互定制也不是很友好
  3. Deploying an Update Server,这个方案需要部署一个update server,也比较麻烦

因此,我们使用的是自己实现的一套更新流程。

1、检查更新

检查更新是整体流程的第一个步骤。如果有更新,后续的更新逻辑才会执行。通常我们会在软件启动时检查更新。

检查更新的策略,实际上是将本地客户端的版本与远程版本进行一次对比,然后根据版本对比的结果来给出不同的更新展示。

远程版本

相比于自己搭建一个update server,维护一个远程的JSON数据成本是很低的。这个远程数据可以是一个后端接口或者cdn上的json文件,并且可以在需要更新的时候,及时更新远程数据的内容

这个远程JSON数据里面一般会存放版本号、更新内容介绍以及发布时间:

const updateData = axios.get('https://some-update.json');
console.log(updateData);
/*
{version: '1.0.0',changeLogs: ['来个开发祭天','新增了??的功能'],time: '2019-06-06',
}
*/
复制代码
本地版本

在Electron中获取本地版本是非常简单的 app.getVersion

const localVersion = app.getVersion(); // 0.0.1
复制代码
版本对比

通常,远程版本号大于本地版本时,即认定为有更新。在有更新的情况下,我们还可以根据版本号里的major、minor、patch版本变动,来制定不同的更新策略。

// 远程版本 > 本地版本
const shouldUpdate = semver.gt(removeVersion, localVersion);// 例子:major版本号变化时,给出强的更新提示。否则给出正常更新提示const isMajorUpdate = semver.diff(removeVersion, localVersion) === 'major';if (!shouldUpdate) return; // 无更新,不走后续if (isMajorUpdate) {console.log('给出强势更新')
} else {console.log('给出普通的更新提示')
}
复制代码

对于版本号的操作使用 semver

2、更新提示

检查到软件更新之后,需要给出更新提示来提醒到用户。此时,我们会使用一个窗口承载更新提示的内容,后面统称为更新窗口。

更新窗口内部代码示例:

const updateData = axios.get('some-update.json')
// 检查更新的逻辑,省略if (!shouldUpdate) return; // 无更新// 执行到这里,肯定有更新了。拿到更新数据,渲染窗口内容
ReactDom.render(<AppupdateData={updateData}onUpdate={() => console.log('用户点击了更新')}
/>, '#app');// 有更新,主动展示窗口(更新窗口默认是隐藏的)
currentWindow.show();
复制代码

3、更新本机文件

当用户点击了更新按钮之后,那么意味着我们可以开始进行最后一步了。

最后的这一步骤,我们分两步进行:

  1. 获取到最新的安装程序(.dmg or .exe),因为最新的代码文件就在安装程序中
  2. 替换掉用户本机上的代码文件

这一步骤,可以交给用户来做,也可以由我们帮用户来做。我们来看看这两种情况下,分别是如何实现的。

交给用户来更新

首先,我们需要更新网站客户端下载页上的安装程序资源至最新。然后,用户点击更新按钮之后,直接用本机默认浏览器打开下载页,让用户自己下载、安装,安装程序正常执行完毕之后,本身就可以覆盖本机代码文件。

  1. 用户通过浏览器,在下载页获取到了最新的安装程序。
  2. 用户手动打开了安装程序,并执行完毕安装程序。

此法用户体验不是很好,但是优点也很明显:节省了很多开发成本,直接复用了web页面来做更新。

如果采用这种策略,那么代码会非常简单:

// 点击更新按钮
function handleUpdate() {shell.openExternal('https://www.kujiale.com/activity/136'); // 打开一个下载页,剩下的交给用户}
复制代码
我们帮用户更新

当然,为了追求更好的用户体验,直接在更新窗口的代码中实现功能是更好的。

第一步,下载最新的安装程序,并且给出下载进度展示。下载进度功能推荐使用request-progress来做。当然,也可以使用NodeJs原生的httpstream模块来实现下载进度展示,这里不详细讲解。

下载到的安装程序,可以暂时存放到用户电脑的临时文件夹中

const fs = require('fs');
const request = require('request');
const progress = require('request-progress');// 点击更新按钮
function handleUpdate(){// 根据版本号拼接安装程序地址const downloadUrl = `https://someupdate/${updateData.version}/installer.dmg`;// 用request下载progress(request(downloadUrl)).on('progress', (state) => {// 进度console.log(state)})// 写入到临时文件夹.pipe(fs.createWriteStream(path.join(app.getPath('temp'), 'installer.dmg')))
}复制代码

进度展示示例图:

第二步,将安装程序中的代码文件更新到用户本机上,此时有两种方案:

  1. 直接打开安装程序,用户跟随安装程序指引稍作点击即可完成安装。
  2. 解压安装程序中的内容,并将内容更新到用户本机

在windows下,安装程序里面是有业务逻辑的:操作注册表、卸载程序、快捷方式等等,因此我们选择第一种方案。

const { shell, app } = require('electron');
shell.openItem('your installer exe path'); // 打开下载好的安装程序
app.quit(); // 退出当前客户端
复制代码

在MacOS下,我们所发行的dmg文件其实没有业务逻辑,因此可以使用方案二,直接把.app目录解压出来,然后拷贝到/Applications目录即可。在MacOS下,解压dmg文件可以使用hdiutil。

const cp = require('child_progress');
const path = require('path');
const fs = require('fs-extra');// 下载完毕之后的dmg文件,文件内的.app目录名为Test
const installerPath = '/your_installer.dmg'; // 使用hdiutil来解压dmg文件内部资源,解压后的资源目录为/Volumes/your_installercproc.execSync(`hdiutil attach ${installerPath} -nobrowse`, {stdio: ['ignore', 'ignore', 'ignore']});// 删掉原有的.app目录fs.removeSync('/Applications/Test.app'); // 把Volumes目录下的.app目录拷贝到/Applications中,更新完毕fs.copySync('/Volums/your_installer/Test.app', '/Applications');// 重启应用app.relaunch();app.quit();
复制代码

最后

欢迎大家在评论区讨论,技术交流 & 内推 -> zhongli@qunhemail.com

转载于:https://juejin.im/post/5cfb6c8ce51d45108126d206

【Electron】酷家乐客户端开发实践分享 — 软件自动更新相关推荐

  1. 【Electron】酷家乐客户端开发实践分享 — 下载管理器

    作者:钟离,酷家乐PC客户端负责人 原文地址:webfe.kujiale.com/electron-ku- 酷家乐客户端:下载地址 www.kujiale.com/activity/13- 文章背景: ...

  2. 【Electron】酷家乐客户端开发实践分享 — 入坑篇

    作者:钟离,酷家乐PC客户端负责人 原文地址:webfe.kujiale.com/electron-ku- 酷家乐客户端:下载地址 www.kujiale.com/activity/13- 文章背景: ...

  3. 【Electron】酷家乐客户端开发实践分享 — 进程通信

    作者:钟离,酷家乐PC客户端负责人 原文地址:webfe.kujiale.com/electron-ku- 酷家乐客户端:下载地址 www.kujiale.com/activity/13- 文章背景: ...

  4. 【Electron】酷家乐客户端开发实践分享 — 浏览器启动客户端

    作者:钟离,酷家乐PC客户端负责人 原文地址:webfe.kujiale.com/browser-to-- 酷家乐客户端:下载地址 www.kujiale.com/activity/13- 文章背景: ...

  5. 酷家乐java开发,【Electron】酷家乐客户端开发实践分享 — 入坑篇

    本文的初衷 Electron所使用的技术栈(JavaScript.NodeJs.HTML.CSS)和web前端工程师完美契合.于是,越来越多的前端工程师,用Electron来开发桌面客户端的开发,我也 ...

  6. 酷家乐 java面经_酷家乐内部教练案例分享

    素材中台主营两部分业务:模型.材质,模型材质数据是酷家乐的关键资源,对内承载着对业务团队的数据支持,对外负责了酷家乐渲染图的表现效果. 所以团队成员承接的工作除了中台能力建设之外,还有很多日常很多前台 ...

  7. 酷家乐前端开发工程师一面

    写在前面 周五面试酷家乐 面试官上来直接五道算法题把我给整懵了 记录一下吧 算法题还是要多刷多刷! 解法一 排序 不赘述了 解法二 利用map集合 var majorrityElement=funct ...

  8. 酷家乐如何使用 Istio 解决新服务治理系统(Serverless)接入已有成熟自研 Java 服务治理体系...

    本文来自酷家乐先进技术工程团队,作者罗宁,酷家乐资深开发工程师. 公司背景 酷家乐 [1] 公司以分布式并行计算和多媒体数据挖掘为技术核心,推出的家居云设计平台,致力于云渲染.云设计.BIM.VR.A ...

  9. 酷家乐怎样把两个方案合并_两个地产科技的理工男联姻:他们都想做一家云端的Autodesk...

    文 | Ellie 在2015年那波轰轰烈烈的互联网公司并购潮后,垂直细分赛道的收并购在疫情期间兴起. 两家独立创业的公司会因哪些契机走到一起?合并注定会有主次,两家公司的创始人会如何妥协和让步?两家 ...

最新文章

  1. javascript与浏览器学习(一)
  2. 由浅入深CIL系列:6.For和Foreach的CIL结构组成以及运行效率
  3. C#图片处理示例(裁剪,缩放,清晰度,水印)
  4. Python基本语法_函数属性 参数类型 偏函数的应用
  5. macOS Monterey新问题:“内存泄漏”,应用后台运行消耗上百 GB 内存
  6. ASP截取字符:instr,instrrev,left,right,mid
  7. 八 关于电机驱动芯片L298N使用心得
  8. 华为交换机链路聚合使用ENSP模拟器进行实验
  9. HTML5期末大作业:关于旅游主题网站设计——开心网旅游网页源码(15页) HTML+CSS+JavaScript
  10. 计算机桌面图标出现蓝色问号,win7系统桌面图标有蓝色问号的解决方法
  11. 转自猎豹移动CEO傅盛:深度学习是什么?
  12. iOS崩溃日志符号化及NLP聚类实现
  13. 幅度响应怎么计算_黄河三角洲地区植被变化及其对气温的响应特征
  14. matlab dlnode,Matlab 绘制三维立体图(以地质异常体为例)
  15. 数据库事务的概念和ACID特性!你懂了吗,小老弟?!
  16. 家庭NAS服务器(1)服务器的配置与选择
  17. Linux驱动学习-平台设备驱动probe函数-20220410
  18. 6行代码实现对TF卡的读写功能
  19. 黑客学习-中断攻击:UDP Flood拒绝服务攻击与防范
  20. leetcode-买卖股票/背包问题

热门文章

  1. Cydia崩溃错误修复
  2. Git小技巧——修改commit的注释内容——一看就会
  3. 帮我用Java写一个生成流水号的方法
  4. 轮岗为何会逼走人才?轮岗机制常见的6个问题是什么?
  5. 算法竞赛进阶指南 骑士放置
  6. 软件测试的风险主要体现在哪里
  7. 【备注】【C42】《编写高质量代码:改善Python程序的91个建议》PDF
  8. 协同过滤(英语:Collaborative Filtering)
  9. hadoop实现求共同好友
  10. 全月平均法业务存货核算系统应如何操作