关于
HexoHub是款个人开发的项目(桌面应用程序),旨在提供一个一体化的hexo集成可视化面板,优化hexo使用体验。
截至撰文,HexoHub已更新到v2.2.0。近几个版本的更新主要集中在界面的调整和性能的优化,包括但不限于:
- 加入背景自定义,面板透明度可调
- 优化顶部窗口栏,使程序界面看起来无边框化
- 优化日志记录,日志记录更加全面清晰
- 优化总体界面布局
- 优化程序性能,解决了当程序关闭后,hexohub进程依然存在的问题
- (下文将提到)优化应用大小,原本
850MB缩小至290MB
Electron应用构建思考
我的package.json是这样的:
1 | { |
完成了界面的设计和功能的搭建后,执行cnpm run electron在开发环境下预览,执行cnpm run build生成静态发布文件,执行cnpm run make构建exe可执行文件(发行版)。
构建之后,发现安装所需大小为851MB,这么大!尽管我不是专业开发者,但能感觉到这个大小并不正常。当时是第一次构建成功(在此之前,有个全局css样式引入问题我一直解决不了,困扰了相当久——两个星期左右,导致一直构建失败),兴奋着来没管那么多,直接发布了第一个版本,后继的开发也主要在功能和界面的改进上,把这个大小问题暂时放在一边。
后来功能完善的差不多,碰巧R1cky也在这时候提了个与占用空间有关的issue:
于是重新研究起这个问题。
首先查看安装后的目录:
可以发现,应用本身、chromium v8浏览器内核、nodejs这些基本的electron应用组件占294MB,在850MB中占比并不大,真正大的是里面的asar文件,体积高达500MB:
什么是ASAR文件?
.asar 文件是 Electron 应用特有的一个存档格式,它把应用的源代码(HTML, CSS, JavaScript, 资源文件等)打包成一个单一文件,目的是为了组织和保护代码,防止用户轻易查看和修改,是 Electron 用来封装和分发应用程序源代码的标准容器文件。可以使用以下命令来查看、提取asar文件内容:
1 | # 安装 asar 工具 |
查看asar文件发现,里面有相当多的npm包,绝大部分都是package.json里dependencies的。当时认为,这是因为我在build字段填入了”node_modules/**/*”导致的:
1 | "build": { |
那接下来的工作就是要优化dependencies,问了问AI,将几个体积比较大的依赖移动到了下面的devDependencies中(特别是next,这个玩意相当大)。这些包只在我开发的时候有用,在生产环境并无作用,执行构建时,构建器不会把开发依赖进行打包。下面是我优化后的package.json:
1 | { |
相比较初版,将以下这些依赖移动到了开发环境中:
1 | "next": "^15.3.5", // Electron 不需要 Next.js 服务端 |
然后重新进行构建,构建后的安装总体积下降到了500MB,感觉还是不理想。继续问AI,也是一直让我优化依赖,但是后面无论我如何修改生产依赖,如何在”files”字段中对打包文件加以限制(例如使用”!**/node_modules/*”来明确声明排除某些包),体积始终在500MB浮动。
转折
折腾了很久没进展,突然间想到,electron的本质不就是把网页封装成可执行文件吗,所有功能是否已经在run build出的静态文件里实现了?如果是这样的话,那为什么还需要这些依赖呢?下面是执行build之后产生的静态文件,我认为electron已经将所有功能都封装进那些js文件里了。
1 | E:\BLOGTEST\14\HEXOHUB\OUT |
真的是豁然开朗的感觉,于是直接去package.json里,把dependencies的内容全部删了重新构建,得到了开头提到的294MB的安装体积:
这下舒服了,果然在某些方面,AI的全力以赴,不如人类的灵机一动。后来,找DS问了具体的原理:
这主要得益于现代前端构建工具(如 Vite、Webpack)和 electron-builder 的高度智能化。前端代码已被“打包内联” (Bundling):在你运行 npm run build(或类似的构建命令)时,你的构建工具(Vite/Webpack)已经完成了最关键的工作:它将你所有的前端依赖(React, Radix, 各种组件库)和你自己写的代码,全部打包、压缩、Tree-shaken 后,生成了一些静态文件(通常是 index.html, assets/index-xxxxxx.js, assets/index-xxxxxx.css)。这些生成的 .js 和 .css 文件已经包含了所有运行时代码。原始的 node_modules 文件夹在运行时不再被需要。electron-builder 打包的是“构建结果”,而非源代码:在你的 electron-builder 配置中,files 字段很可能指向的是构建输出目录(例如 dist/ 或 build/)。electron-builder 的工作只是:把这个已经包含所有依赖的 dist/ 文件夹、你的主进程代码(main.js)和 Electron 运行时捆绑在一起。它并不需要原始的 node_modules 来执行你的渲染进程代码,因为它只需要最终的构建产物。依赖已被“烘焙”进最终文件:
你可以做一个实验:用文本编辑器打开 dist/assets/index-xxxxxx.js 这个文件。你会看到一大片被压缩的代码,在里面搜索 react、zustand 等关键词,你一定能找到它们。它们已经不再是独立的包,而是被“打碎”后融合进了一个或几个大的 JavaScript 文件中。
这也证实了我一开始的猜想。当然了,这样的“临时修改”肯定是非正规手段,也是被DS狠狠教育了一番:
请立即停止“删除-恢复”的想法。
你通过实验证明了一件事:你的前端构建流程是正确的,最终的安装包体积与 dependencies 字段的内容无关。
你现在需要做的唯一工作就是:
坐下来。
仔细审核你的 package.json。
使用 npm install –save-dev 命令将所有开发工具(如 electron, electron-builder, vite, typescript)正确地移动到 devDependencies 中。
删除真正无用的依赖(如单机应用中的 socket.io)
行吧,过几天再来研究怎么“正确的”删除依赖。前天游泳的时候,跟一哥们在水里泡着聊天聊久了,没运动开,身子也没热,给我冻感冒了,这几天发烧头晕的不行
Comments