生成图标

在打包应用之前,要为应用准备一个图标,作为安装包图标。不同的操作系统所需图标的格式不同,Mac对应的格式为icns,Windows对应的格式为ico

图标的生成可以借助 electron-icon-builder

  • 首先,准备一张1024*1024的png图片,将图片放在项目文件夹中,我们这里选择放在tasky/public文件夹中。
  • 安装 electron-icon-builder:

    npm i electron-icon-builder --D

  • package.jsonscripts添加指令:

    "build-icon": "electron-icon-builder --input=./public/icon.png --output=build --flatten"

  • 运行npm run build-icon,就会在build文件夹中生成一系列打包所需的图标文件。

打包应用

Electron生态下常用的打包工具有两个:electron-builderelectron-packager

electron-builder配置更灵活,使用也更广泛。下面,我们使用electron-builder来进行打包。

安装

 1npm i electron-builder --D

配置

使用electron-builder打包主要是各种配置,它支持两种配置方式:

  1. package.json中添加build字段:
 1"build": {
 2  "appId": "your.app.id"
 3}
  1. 指定配置文件,在其中写入配置项。默认是项目根目录下的electron-builder.yml。
 1appId"your.app.id"

在日常开发中,package.json这种配置方式比较常用,我们也以这种方式为主。

基础配置

 1"build": {
 2    "appId": "this.is.tasky", 
 3    "productName": "Tasky",
 4    "copyright": "Copyright © 2021 Alaso",
 5    "directories": {
 6      "buildResources": "build",   //指定打包需要的静态资源,默认是build
 7      "output": "dist",  //打包生成的目录,默认是dist
 8    }
 9 },

build文件夹放置的是,electron-builder默认的在打包过程中需要的静态文件,比如我们上面生成的图标文件;dist文件夹放置的是打包生成的各种文件。

  1. package.jsonscripts添加指令:"pack": "electron-builder"
  2. 运行npm run pack

基于以上的配置,electron-builder会根据当前的操作系统打包出默认的文件。比如,在windows平台下,打包结果如下:

image.png

平台相关的配置

electron-builder会自动识别当前的操作系统,打出系统对应的安装包。 这也意味着,如果要生成exe\msi,需要在Windows操作系统,如果是dmg,则需要在Mac操作系统。

electron-builder的配置选项中,有很多跟操作系统相关的配置,可以对不同平台的打包做一些定制效果。下面以Windows和Mac为例,介绍一些常用的平台相关的配置。

  1. Windows
 1"build": {
 2  ...
 3  "win": {
 4    "target": ["msi","nsis"],        //安装包的格式,默认是"nsis"
 5    "icon": "build/icons/icon.ico"   //安装包的图标
 6  },
 7  
 8  //"target"值"nsis"打包出来的就是exe文件
 9  //nsis是windows系统安装包的制作程序,它提供了安装、卸载、系统设置等功能
10  //关于"nsis"的一些配置
11  "nsis": {                          
12    "oneClick": false,               //是否一键安装,默认为true
13    "language": "2052",              //安装语言,2052对应中文
14    "perMachine": true,              //为当前系统的所有用户安装该应用程序
15    "allowToChangeInstallationDirectory": true   //允许用户选择安装目录
16  }
17}

2. Mac

 1...
 2"build": {
 3  "mac": {
 4     "target": ["dmg", "zip"],       //安装包的格式,默认是"dmg"和"zip"
 5     "category": "public.app-category.utilities"  //应用程序安装到哪个分类下,具体有哪些分类可以在苹果官网上找
 6  },
 7  "dmg": {
 8     "background": "build/background.jfif",   //安装窗口背景图
 9     "icon": "build/icons/icon.icns",         //安装图标
10     "iconSize": 100,                         //图标的尺寸
11     "contents": [                            //安装图标在安装窗口中的坐标信息
12        {
13          "x": 380,
14          "y": 180,
15          "type": "link",
16          "path": "/Applications"
17        },
18        {
19          "x": 130,
20          "y": 180,
21          "type": "file"
22        }
23     ],
24     "window": {                             //安装窗口的大小
25        "width": 540,
26        "height": 380
27     }
28   }
29}

{B92F643C-E8F2-183F-C7DE-E0C24E347CFC}.jpg

会将哪些文件pack到安装包

在打包生成的文件夹中,会有一个app.asar,它是Electron应用程序的主业务文件压缩包,要知道项目中哪些文件被pack到安装包,可以通过解压app.asar进行查看。

解压app.asar需要借助asar工具,首先来安装:npm i asar -g

然后切换到app.asar所在目录,执行:asar extract app.asar ./app-folder

以windows为例,app.asar位于tasky\dist\win-unpacked\resources目录中,解压后,可以看到app-folder中的内容如下:

image.png

可以看到,基本上就是项目所有文件了(除了package-lock.json\ .gitignore \ build文件夹),并且还有node_modules

对于node_modules并不是所有node_modules中的内容都会被打包进安装包,只有package.jsondependencies字段中的依赖会被打包,devDependencies字段中的依赖则不会。 这是唯一规则,跟项目实际是否使用依赖没有关系。

所以,为了减小安装包体积,建议在渲染进程中使用的外部包,都安装在devDependencies中,然后使用webpack将外部包的代码和业务代码打包到一起,在后面的文章中会详细介绍。

当然,可以通过配置files字段,来指定将哪些内容进行打包image.png

比如,我们只打包src文件夹index.jspackage.json,可以这样配置:

 1"build": {
 2  "files": [
 3    "package.json",
 4    "index.js",
 5    "src/**/*"
 6  ]
 7}

自动更新

要自动更新,应用程序的安装包应该存放在互联网的某台服务器上,每次打开应用的时候,进行自动检测,根据当前应用程序的version和线上版本进行匹配,当发现有新的version的时候,就自动下载,下载完成后,询问用户是否安装新版本。

打包不同版本

package.json中,有个"version"字段,用于决定当前版本。

  • step1: 设置"version": "1.0.0",运行npm run pack
  • step2: 设置"version": "1.0.1", 运行npm run pack

虽然,我们没有改变应用程序的内容,但是会被识别成”1.0.0”和”1.0.1”两个版本。

搭建一个服务器放安装包

我们在本地启动一个服务器,放最新版本的安装包资源。

  • 1、初始化
 1mkdir tasky-server
 2cd tasky-server
 3npm init -y
 4npm install koa koa-static --save
  • 2、在tasky-server目录下新建index.js,内容如下:
 1const Koa = require('koa')
 2const app = new Koa()
 3
 4const static = require('koa-static')
 5const path = require('path')
 6
 7app.use(static(path.join(__dirname,'./static')));
 8
 9app.listen(9005)
  • 3、在创建一个static文件夹,放入最新版本的安装包set。具体包含哪些文件呢?假如最新版本是”1.0.1”。

Mac平台: latest-mac.ymlTasky-1.0.1-mac.zipTasky-1.0.1.dmgTasky-1.0.1.dmg.blockmap

Windows平台: latest.ymlTasky 1.0.1.msiTasky Setup 1.0.1.exeTasky Setup 1.0.1.exe.blockmap

  • 4、启动服务器。node index.js

检测更新

检测更新可以借助electron-updater来实现。它结合electron-builder,实现起来非常简单。直接上代码。

  • 第一步、在build中配置"publish"字段:
 1"build": {
 2    ...
 3    "publish": [
 4      {
 5         "provider": "generic",
 6         "url": "http://127.0.0.1:9005/" 
 7      }
 8    ]
 9}

第二步、在应用程序主进程中调用electron-updater模块检测更新。

 1const { autoUpdater } = require('electron-updater')
 2function checkUpdate(
 3
 4){
 5  if(process.platform == 'darwin'){  
 6  
 7    //我们使用koa-static将静态目录设置成了static文件夹,
 8    //所以访问http://127.0.0.1:9005/darwin,就相当于访问了static/darwin文件夹,win32同理
 9    autoUpdater.setFeedURL('http://127.0.0.1:9005/darwin')  //设置要检测更新的路径
10    
11  }else{
12    autoUpdater.setFeedURL('http://127.0.0.1:9005/win32')
13  }
14  
15  //检测更新
16  autoUpdater.checkForUpdates()
17  
18  //监听'error'事件
19  autoUpdater.on('error', (err) => {
20    console.log(err)
21  })
22  
23  //监听'update-available'事件,发现有新版本时触发
24  autoUpdater.on('update-available', () => {
25    console.log('found new version')
26  })
27  
28  //默认会自动下载新版本,如果不想自动下载,设置autoUpdater.autoDownload = false
29  
30  //监听'update-downloaded'事件,新版本下载完成时触发
31  autoUpdater.on('update-downloaded', () => {
32    dialog.showMessageBox({
33      type: 'info',
34      title: '应用更新',
35      message: '发现新版本,是否更新?',
36      buttons: ['是', '否']
37    }).then((buttonIndex) => {
38      if(buttonIndex.response == 0) {  //选择是,则退出程序,安装新版本
39        autoUpdater.quitAndInstall() 
40        app.quit()
41      }
42    })
43  })
44}
45
46app.on('ready', () => {
47  //每次启动程序,就检查更新
48  checkUpdate()
49}

是否需要更新是根据什么判断的呢?

electron-updater会根据上面setFeedURL指定路径下的latest.yml中的version来判断是否需要更新,大于当前版本的version则需要更新,否则不更新。.yml也是一种配置文件,有点类似于我们常用的.json配置文件,两者写法不一样。

基于github的方案

如果你不想搭建自己的服务器,也可以借助github。使用github自动发布,不用每次手动上传最新安装包资源。

自动发布

第一步,依然是配置"publish"字段。

 1"build": {
 2    ...
 3    "publish": ['github']
 4}

第二步、在"scripts"中配置新的指令,由于github权限控制,需要GH_TOKEN,可以在 github.com/settings/to… 中生成GH_TOKEN。

 1"scripts": {
 2    ...
 3    "release": "cross-env GH_TOKEN=ghp_KmVD3.......W2k3Pd4vV electron-builder"
 4}

第三步、npm run release,就会在打包后,将资源上传到github,生成release draft,你在github项目中,找到这个draft,publish release就可以了。

检测更新

和上面类似,以Windows为例,代码如下。

 1const { autoUpdater } = require('electron-updater')
 2function checkUpdate(
 3
 4){
 5  //检测更新
 6  autoUpdater.checkForUpdates()
 7  
 8  //监听'error'事件
 9  autoUpdater.on('error', (err) => {
10    console.log(err)
11  })
12  
13  //监听'update-available'事件,发现有新版本时触发
14  autoUpdater.on('update-available', () => {
15    console.log('found new version')
16  })
17  
18  //默认会自动下载新版本,如果不想自动下载,设置autoUpdater.autoDownload = false
19  
20  //监听'update-downloaded'事件,新版本下载完成时触发
21  autoUpdater.on('update-downloaded', () => {
22    dialog.showMessageBox({
23      type: 'info',
24      title: '应用更新',
25      message: '发现新版本,是否更新?',
26      buttons: ['是', '否']
27    }).then((buttonIndex) => {
28      if(buttonIndex.response == 0) {  //选择是,则退出程序,安装新版本
29        autoUpdater.quitAndInstall() 
30        app.quit()
31      }
32    })
33  })
34}
35
36app.on('ready', () => {
37  //每次启动程序,就检查更新
38  checkUpdate()
39}

结语

我们上面的例子中,是将页面的web资源都打包到了安装包,还有一种情况就是,web资源和“app壳子”分离,web资源放在服务器,每次都通过网络动态加载,像下面这样:

 1mainWindow.loadURL('https://juejin.cn')

在业务需要频繁更新的场景中,可以使用这种方式,快速无障碍地实现更新。在这种情况下,我们可以按照上述方式打包和更新“壳子”,也就是主进程相关;而页面资源的打包和普通的前端项目打包无异,这里不再赘述。

个人笔记记录 2021 ~ 2025