# 應用程式打包 為了減少圍繞著 Windows 上長路徑名稱問題的 [issues](https://github.com/joyent/node/issues/6960) ,稍微地加速 `require` 和隱藏你的原始碼避免不小心被看到,你可以選擇把你的應用程式打包成一個 [asar][asar] 壓縮檔,只需要改變一點點你的原始碼就好。 ## 產生 `asar` 壓縮檔 一個 [asar][asar] 壓縮檔是一個簡單的類 tar 格式的檔案,它會把幾個檔案串接成一個檔案, Electron 可以不需要解壓縮整個檔案就從中讀取任意檔案。 把你的應用程式打包成 `asar` 壓縮檔的步驟: ### 1. 安裝 asar 工具包 ```bash $ npm install -g asar ``` ### 2. 用 `asar pack` 打包 ```bash $ asar pack your-app app.asar ``` ## 使用 `asar` 壓縮檔 在 Electron 中有兩組 API:Node.js 提供的 Node APIs 和 Chromium 提供的 Web APIs,兩組 API 都支援從 `asar` 壓縮檔中讀取檔案。 ### Node API 因為 Electron 中有一些特別的補釘,像是 `fs.readFile` 和 `require` 這樣的 Node API 會將 `asar` 壓縮檔視為許多虛擬目錄,將裡頭的檔案視為在檔案系統上的一般檔案。 例如,假設我們有一個 `example.asar` 壓縮檔在 `/path/to` 中: ```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') ``` 列出所有在壓縮檔根目錄下的檔案: ```javascript const fs = require('fs') fs.readdirSync('/path/to/example.asar') ``` 使用一個壓縮檔中的模組: ```javascript require('/path/to/example.asar/dir/module.js') ``` 你也可以利用 `BrowserWindow` 在 `asar` 壓縮檔中呈現一個網頁: ```javascript const BrowserWindow = require('electron').BrowserWindow var win = new BrowserWindow({width: 800, height: 600}) win.loadURL('file:///path/to/example.asar/static/index.html') ``` ### Web API 在一個網頁中,壓縮檔中的檔案都可以透過 `file:` 這個協定被存取,如同 Node API,`asar` 壓縮檔都被視為目錄。 例如,要透過 `$.get` 取得一個檔案: ```html ``` ### 把一個 `asar` 壓縮檔視為一般檔案 在一些像是驗證 `asar` 壓縮檔檢查碼(checksum)的例子中,我們需要以檔案的方式讀取 `asar` 壓縮檔中的內容,為了達到這個目的,你可以使用內建的 `original-fs` 模組,它提供了沒有 `asar` 支援的原生 `fs` API: ```javascript var originalFs = require('original-fs') originalFs.readFileSync('/path/to/example.asar') ``` 你也可以設定 `process.noAsar` 為 `true` 來關掉在 `fs` 模組中的 `asar` 支援: ```javascript process.noAsar = true fs.readFileSync('/path/to/example.asar') ``` ## Node API 上的限制 儘管我們盡可能的努力嘗試著使 Node API 中的 `asar` 壓縮檔像目錄一樣運作,還是有一些基於 Node API 低階本質的限制存在。 ### 壓縮檔都是唯讀的 所有壓縮檔都無法被修改,因此所有可以修改檔案的 Node API 都無法與 `asar ` 壓縮檔一起運作。 ### 使用中的目錄無法被設為壓縮檔中的目錄 儘管 `asar` 壓縮檔被視為目錄,卻並沒有真正的目錄在檔案系統中,所以你永遠無法將使用中的目錄設定成 `asar` 壓縮檔中的目錄,把他們以 `cwd` 選項的方式傳遞,對某些 API 也會造成錯誤。 ### 更多透過 API 拆封的方法 大部分 `fs` API 可以讀取一個檔案,或是不用拆封就從 `asar` 壓縮檔中取得一個檔案的資訊,但對一些需要傳遞真實檔案路徑給現行系統呼叫的 API ,Electron 將會解開需要的檔案到一個暫時的檔案,然後傳遞該暫時檔案的路徑給那些 API 以便使他們可以運作,這會增加這些 API 一點負擔。 需要額外拆封的 API : * `child_process.execFile` * `child_process.execFileSync` * `fs.open` * `fs.openSync` * `process.dlopen` - 在原生模組中被 `require` 使用 ### `fs.stat` 的不真實的狀態資訊 `fs.stat` 回傳的 `Stats` 物件和它在 `asar` 壓縮檔中的檔案朋友都是以猜測的方式產生的,因為那些檔案不存在檔案系統,所以你不應該信任 `Stats` 物件,除了取得檔案大小和確認檔案型態之外。 ### 執行 `asar` 壓縮檔中的二進位檔 有像是 `child_process.exec`、`child_process.spawn` 和 `child_process.execFile` 的 Node APIs 可以執行二進位檔,但只有 `execFile` 是 `asar` 壓縮檔中可以執行二進位檔的。 這是因為 `exec` 和 `spawn` 接受的輸入是 `command` 而不是 `file`,而 `command` 們都是在 shell 底下執行,我們找不到可靠的方法來決定是否一個命令使用一個在 `asar` 壓縮檔中的檔案,而儘管我們找得到,我們也無法確定是否我們可以在沒有外部影響(side effect)的情況下替換掉命令中的路徑。 ## 加入拆封檔案到 `asar` 壓縮檔中 如前述,一些 Node API 再被呼叫時會拆封檔案到檔案系統,除了效能議題外,它也會導致掃毒軟體發出 false alerts。 要繞過這個問題,你可以透過使用 `--unpack` 選向來拆封一些建立壓縮檔的檔案,以下是一個不包含共享原生模組的函式庫的例子: ```bash $ asar pack app app.asar --unpack *.node ``` 執行這個命令以後,除了 `app.asar` 以外,還有一個帶有拆封檔案的 `app.asar.unpacked` 資料夾被產生出來,當你要發布給使用者時,你應該把它和 `app.asar` 一起複。 [asar]: https://github.com/atom/asar