3.2 原理介绍
虽然我们介绍的是electron-builder的原理,但大多数Electron的打包工具都是基于这个原理实现的,甚至NW.js的打包方式也如出一辙。希望读者学完本节的内容后能触类旁通。
使用electron-builder为一个Electron应用生成安装包,需要执行以下几步工作。
(1)构建前端项目
如果使用现代前端开发框架(比如webpack、Rollup或Vite)开发应用,那么electron-builder是不会帮我们完成webpack、Rollup或Vite的打包工作的,需要开发者自己完成。好在这些框架都提供了完善的开发接口,开发者只要写一些简单的脚本即能完成打包工作(后面介绍了几个常见的前端开发框架的打包脚本),注意这里打包完成后产出的HTML、js、css要存放到一个指定的输出目录内,后面还会在这个输出目录内执行一系列的操作。
(2)准备package.json
有别于纯前端工程,当使用webpack或者Vite打包完前端工程后,还需要在输出目录下创建一个package.json文件,开发者需要在这个文件中指定主进程的入口程序。这样当你的应用被分发给用户后,用户启动应用时,Electron才知道要首先加载哪个脚本。这个package.json文件只在生产环境中起效,所以里面的配置信息非常简单,这一点后面还会有详述。
另外,package.json文件的devDependencies配置节应包含一个electron项,并明确当前项目所使用的Electron版本,这个信息将被提供给electron-builder,在electron-builder制成安装包时使用正确的Electron版本。
(3)收集配置信息
electron-builder启动后做的第一件事情就是收集开发者为electron-builder提供的配置信息,比如应用图标、应用名称、应用id、附加资源等。有些配置信息可能开发者并没有提供,这时electron-builder会使用默认值。总之,这一步工作完成后,会生成一个全量的配置信息对象用于接下来的打包工作。
(4)安装依赖
electron-builder接着会检查我们在输出目录下准备的package.json文件,查看其内部是否存在dependencies依赖,如果存在,electron-builder会帮我们在输出目录下安装这些依赖,(原生模块则重新编译,以使其适配Electron环境)。这里需要注意的是,我们使用Vue或者React开发前端工程,往往会依赖很多外部模块,但这些模块已经被Vite或者webpack打包到业务脚本中了,输出目录下的package.json文件中不应该存在这些依赖包。
主进程的业务代码同理,也应该被webpack、Rollup或Vite等工具处理后再写入安装包,因为这些打包工具会编译、压缩你所依赖的第三方模块。如果开发者不这么做,electron-builder会帮你安装这些模块,还会帮你安装这些模块依赖的其他模块,electron-builder是不会帮你做编译、压缩工作的。
也就是说业务脚本目录下的package.json中大部分情况下是不应该包含dependencies配置节的(原生模块或难以编译的npm模块除外)。
(5)生成asar
electron-builder会根据用户配置信息(asar的值为true或false)来判断是否需要把输出目录下的文件合并成一个asar文件,如果开发者没有设置这个信息,那么electron-builder默认是会为应用程序生成asar文件的。
(6)准备二进制文件
electron-builder会从配置信息中获得安装包生成目录的路径,然后把存储在缓存目录下的Electron可执行程序及其依赖的资源拷贝到安装包生成目录下的win-ia32-unpacked子目录内。如果开发者生成的是64位的应用程序,那么这个子目录的名字为win-x64-unpacked;如果electron-builder没有在缓存目录下找到Electron可执行程序及其依赖的资源,则会去服务器上下载,并缓存好,然后再为开发者执行拷贝工作。
electron-builder除了准备Electron相关的文件外,还会把前面提到的app.asar文件也拷贝到\win-ia32-unpacked\resources\目录下。
(7)准备附加资源
除了Electron相关的文件和app.asar文件外,electron-builder还会检查用户是否在配置信息中指定了extraResources配置项,如果有,则把相应的文件按照配置的规则拷贝到对应的目录中。
(8)修改可执行程序
文件准备好后,electron-builder会根据配置信息使用一个工具(后文我们会有介绍)修改electron.exe的文件名和文件信息,这些信息除了文件名、版本号、版权信息外,还包括应用程序的图标。这样做了之后当用户查看可执行程序的属性时,就可以得到开发者配置的信息了,如图3-1所示。
图3-1 可执行文件的详细信息
(9)应用签名
如果开发者在配置信息中指定了签名信息,那么接下来electron-builder会使用一个名为winCodeSign的工具来为可执行文件签名。为应用程序签名是防止应用在分发过程中被篡改的有效手段。签名后的可执行程序如图3-2所示。
图3-2 可执行文件的数字签名
上述所有工作做完之后,子目录win-ia32-unpacked内存放的就是你应用程序的绿色版,开发者完全可以在这个目录下启动应用程序,检查其是否正常运行。如果在这里无法正常运行,那么生成安装程序后,大概率也无法正常运行。
(10)压缩资源
electron-builder会使用7z压缩工具把子目录win-ia32-unpacked下的内容压缩成一个名为yourProductName-1.3.6-ia32.nsis.7z的压缩包,7z压缩工具的lzma压缩算法的压缩比非常高,这可以极大地降低安装包的体积,便于产品分发。
(11)生成卸载程序
electron-builder会使用一个名为NSIS的工具生成卸载程序的可执行文件,这个卸载程序记录了win-ia32-unpacked目录下所有文件的相对路径。当用户卸载我们的应用时,卸载程序会根据这些相对路径删除我们的文件,同时它也会记录一些安装时使用的注册表信息,在卸载时清除这些注册表信息。
如果开发者配置了签名逻辑,则electron-builder也会为卸载程序的可执行文件进行签名。
(12)生成安装程序
electron-builder会使用NSIS工具生成安装程序的可执行文件,然后把压缩包和卸载程序当做资源写入这个安装程序的可执行文件中。当用户执行安装程序时,这个可执行文件会读取自身的资源,并把这些资源释放到用户指定的安装目录下。
如果开发者配置了签名逻辑,则electron-builder也会为安装程序的可执行文件进行签名。
至此,一个应用程序的安装包就制作完成了。一般情况下开发者会把安装包放置在不同的环境下进行测试,测试通过后再分发给用户。
如你所见,electron-builder使用了好几个外部工具来完成打包工作,这些工具有些是Go语言开发的,有些是C/C++语言开发的,甚至里面还能看到Delphi的身影。
electron-builder的作者并不是不想使用Node.js完成这些工作,只是Node.js非常不适合做这些工作。由此可见,虽然我们都是Electron开发者,但更是一个桌面应用开发者,并不能把眼光局限在Electron领域,只有这样才能做出更好的产品。