# 第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
- 索引
- 前言.关于Electron
- 第一部分 开发指南
- 第1章.平台支持
- 第2章.安全、原生功能和你的责任
- 第3章.版本说明
- 第4章.发行应用
- 第5章.Mac App商店提交指南
- 第6章.Windows商店指南
- 第7章.应用打包
- 第8章.使用Node原生模块
- 第9章.调试主进程
- 9.1.在VSCode中调试
- 9.2.在node-inspector中调试
- 第10章.使用Selenium和WebDriver
- 第11章.DevTools扩展
- 第12章.使用Pepper Flash插件
- 第13章.使用Widevine CDM插件
- 第14章.通过自动化持续集成系统进行测试
- 第15章.离屏渲染
- 第二部分 使用教程
- 第16章.快速入门
- 第17章.桌面环境集成
- 第18章.在线/离线事件探测
- 第19章.应答式编译器(REPL)
- 第三部分 API参考
- 第20章.API简介
- 第21章.进程对象
- 第22章.Chrome的命令行开关
- 第23章.环境变量
- 第24章.定制的DOM元素
- 24.1.File 对象
- 24.2.webview 标签
- 第25章.主进程模块
- 25.1.app
- 25.2.BrowserWindow
- 25.3.无框架窗口
- 第26章.渲染进程模块
- 第27章.两种进程可用的模块
- 第四部分 高级主题
- 附 FAQ
- 附 文档规范
- 附 示例用例
- 1.无边框窗口