# 第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.無邊框窗口