企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 第7章.应用打包 要缓和 Windows 上长路径名的[问题](https://github.com/joyent/node/issues/6960),显著加快 `require` 速度,和从一个粗略的检查来隐藏你的源代码,可以选择只需要对源代码做一些小的改变打包 app 到一个 `[asar](https://github.com/electron/asar)` 档案。 ## 生成 asar 档案 一个 `asar` 档案是一个类似 `tar` 的、连接文件到一个单独文件的格式。Electron 可以从中读取任意文件而不用解压整个文件。 打包 app 到一个 `asar` 档案的步骤 ### 1. 安装 asar 工具 ```bash $ npm install -g asar ``` ### 2. 使用 `asar pack` 命令打包 ```bash $ asar pack your-app app.asar ``` ## 使用 asar 档案 在 Electron 中有两组 APIs:Node.js 提供的 Node APIs,和 Chromium 提供的 Web APIs。所有 APIs 都支持从 `asar` 档案中读取文件。 ### Node API 由于 Electron 特别中打了补丁, Node API 中如 `fs.readFile` 或者 `require` 之类 的方法可以将 `asar` 视之为虚拟文件夹,读取 `asar` 里面的文件就和从真实的文件系统中读取普通文件一样。 例如,假设我们在 `/path/to` 文件夹下有个 `example.asar` 档案: ```bash $ asar list /path/to/example.asar /app.js /file.txt /dir/module.js /static/index.html /static/main.css /static/jquery.min.js ``` 从 `asar` 档案中读取一个文件: ```javascript const fs = require('fs') fs.readFileSync('/path/to/example.asar/file.txt') ``` 列出 `asar` 档案中根目录下的所有文件: ```javascript const fs = require('fs') fs.readdirSync('/path/to/example.asar') ``` 使用 `asar` 档案中的一个模块: ```javascript require('/path/to/example.asar/dir/module.js') ``` 你还可以使用 `BrowserWindow` 来显示一个 `asar` 档案里的 web 页面: ```javascript const {BrowserWindow} = require('electron') let win = new BrowserWindow({width: 800, height: 600}) win.loadURL('file:///path/to/example.asar/static/index.html') ``` ### Web API 在 Web 页面里,用 `file:` 协议可以请求 `asar` 档案中的文件。和 Node API 一样,`asar` 档案被视为虚拟目录。 例如,用 `$.get` 获取文件: ```html <script> let $ = require('./jquery.min.js') $.get('file:///path/to/example.asar/file.txt', (data) => { console.log(data) }) </script> ``` ### 把 `asar` 档案当作一般文件 有些场景,如验证 `asar` 档案的校验和,我们需要像读取一个文件那样读取 `asar` 档案的内容(而不是当成虚拟文件夹)。 对于这个目的,你可以使用内置的提供了和 `fs` 一样的 API 的 `original-fs` 模块,而不提供读取 `asar` 档案内某个文件的支持 。 ```javascript const originalFs = require('original-fs') originalFs.readFileSync('/path/to/example.asar') ``` 还可以通过设置 `process.noAsar` 为 `true` 来禁用 `fs` 模块对 `asar` 档案的支持: ```javascript const fs = require('fs') process.noAsar = true fs.readFileSync('/path/to/example.asar') ``` ## Node API 限制 尽管我们已经尽了最大努力使得 `asar` 包在 Node API 下的应用尽可能的趋向于真实的目录,但由于一些底层 Node API 特性,仍然有一些限制。 ### `asar` 档案是只读的 `asar` 档案中的内容不可修改,所以 Node APIs 里那些可以用来修改文件的方法不能操作 `asar` 档案。 ### Working Directory 不能设置为 `asar` 中的目录 尽管 `asar` 档案被当作虚拟文件夹,但其实在文件系统中并没有真实的目录,所以你不可能将 working Directory 设置成 `asar` 档案里的一个目录。将 `asar` 档案中的文件夹作为 `cwd` 选项传入一些 API 会引发错误。 ### 一些 API 中额外的拆包 大部分 `fs` API 可以无需解压即从 `asar` 档案中读取文件或者获取文件信息,但是对于一些依赖传递真实文件路径到底层系统调用 APIs 时,Electron 会将所需文件解压到临时目录下的临时文件,并传递临时文件的路径给这些 APIs 以使其正常工作。 对于这些API,增加了一些开销。 以下是一些需要拆包的 APIs: * `child_process.execFile` * `child_process.execFileSync` * `fs.open` * `fs.openSync` * `process.dlopen` —— 在原生模块中被 `require` 使用 ### `fs.stat` 得到假的 Stat 信息 对 `asar` 档案中的文件使用 `fs.stat`和它的伙伴,返回的 `Stats` 对象通过猜测产生,因为这些文件不是存在于文件系统里。所以除了获得文件大小和检查文件类型以外,你不应该信任 `Stats` 对象。 ### 执行 asar 档案中的二进制文件 Node 中有一些可以执行程序的 API,如 `child_process.exec`,`child_process.spawn` 和 `child_process.execFile`, 但只有 `execFile` 支持执行 `asar` 档案中的程序。 这是因为 `exec` 和 `spawn` 允许 `command` 而不是 `file` 作为输入,而 `command` 在 shell 下执行的。目前没有可靠的方法来判断一个 command 是否使用一个 `asar` 包中的文件,而且即便可以判断,我们依旧无法确定可以在无任何副作用的情况下替换 command 中的文件路径。 ## 打包时排除 `asar` 档案中的文件 如上所述,一些 Node API 会在调用时将文件解压到文件系统中,除了性能问题外,也有可能引起杀毒软件的注意! 为了绕开这个问题,你可以在生成 `asar` 档案时使用 `--unpack` 选项来排除一些文件,一个排除共享的原生模块的库的例子是: ```bash $ asar pack app app.asar --unpack *.node ``` 运行上述命令后,除了生成的 `app.asar` 档案以外,还生成了一个包含排除文件的 `app.asar.unpacked` 文件夹, 你需要将这个文件夹和 `app.asar`一起,拷贝提供给用户。 [asar](https://github.com/electron/asar):https://github.com/electron/asar