使用pkg打包ThinkJS項(xiàng)目的方法步驟
在 ThinkJS 的用戶群里,經(jīng)常有開發(fā)者提出需要對(duì)源碼進(jìn)行加密保護(hù)的需求。我們知道 JavaScript 是一門動(dòng)態(tài)語(yǔ)言,不像其他靜態(tài)語(yǔ)言可以編譯成二進(jìn)制包防止源碼泄露。所以就出現(xiàn)了 pkg、nexe 之類的工具,支持將 JS 代碼連同 Node 一塊打包成一個(gè)可執(zhí)行文件,一來(lái)解決了環(huán)境依賴的問題,二來(lái)解決了大家關(guān)心的源碼保護(hù)的問題。
在pkg 模塊的 README 中,羅列了它的幾大用處,如果你有下面的幾個(gè)需求的話建議不妨試試。
- 為應(yīng)用提供商業(yè)發(fā)行版而不用暴露源碼
- 為應(yīng)用提供 demo 而不用暴露源碼
- 一鍵打包所有平臺(tái)可執(zhí)行文件而不需要對(duì)應(yīng)平臺(tái)環(huán)境依賴
- 提供自解壓或自安裝的解決方案
- 運(yùn)行應(yīng)用不需要安裝 Node.js 和 npm
- 部署僅需要一份單文件,不需要通過(guò) npm 安裝大量的依賴
- 資源打包后讓應(yīng)用遷移起來(lái)更加方便
- 在指定 Node.js 版本下對(duì)應(yīng)用進(jìn)行測(cè)試而不需要安裝對(duì)應(yīng)的版本
如何使用
關(guān)于 pkg 模塊的基礎(chǔ)使用,大家可以看 《把你的NodeJS程序給沒有NodeJS的人運(yùn)行》 這篇文章。通過(guò) npm install -g pkg 在全局安裝上模塊后就可以在命令行中使用 pkg 命令了。pkg 除了支持在命令行中指定參數(shù)之外,還支持在 package.json 中進(jìn)行配置。
{ ... "bin": "production.js", "scripts": { "pkg": "pkg . --out-path=dist/" }, "pkg": { "scripts": [...] "assets": [...], "targets": [...] }, ... }
以上就是一個(gè)簡(jiǎn)單的配置。bin 用來(lái)指定最終打包的入口文件,pkg.scripts 和 pkg.assets 用來(lái)指定除了入口文件之外需要打包進(jìn)可執(zhí)行文件中的內(nèi)容,其中前者用來(lái)指定其他 .js 文件,后者用來(lái)指定非.js的資源。pkg.targets 則是用來(lái)指定需要打包的平臺(tái),平臺(tái)名稱結(jié)構(gòu)如下,node${version}-${platform}-${arch}。version 用來(lái)指定具體 Node 的版本,platform 用來(lái)指定編譯的平臺(tái),可以是 freebsd, linux, alpine, macos 或者 win,最后 arch 用來(lái)指定編譯平臺(tái)的架構(gòu),可以是 x64, x86, armv6 或者 armv7。例如 node10-macos-x64 表示的就是基于 Node 10 打包在 MacOS 平臺(tái)上執(zhí)行的可執(zhí)行程序。scripts, assets 和 targets 都支持?jǐn)?shù)組配置多個(gè)。
將入口文件、依賴的腳本和資源、需要編譯的平臺(tái)配置好之后,執(zhí)行 npm run pkg 即可完成編譯。
如何打包 ThinkJS
pkg 的原理大概是提供一個(gè)虛擬的文件系統(tǒng),將 __filename, __dirname 等變量以及官方 API 中的 IO 操作方法指向本地文件系統(tǒng)的變量修改成指向虛擬系統(tǒng)。通過(guò)該虛擬文件系統(tǒng)讀取壓縮打包后的程序源碼,提供腳本執(zhí)行的環(huán)境。需要注意的是該虛擬文件系統(tǒng)是只讀的,所以如果程序中有基于 __dirname 進(jìn)行讀寫操作的方法,需要規(guī)避規(guī)避掉。
代碼預(yù)處理
在 ThinkJS 項(xiàng)目中會(huì)有以下兩個(gè)地方有文件寫入操作:
- 項(xiàng)目啟動(dòng)后會(huì)在 runtime/config/${env}.json 下寫入最終的配置文件
- 生產(chǎn)環(huán)境下默認(rèn)會(huì)在 logs/ 目錄中寫入線上日志
這些目錄默認(rèn)都是基于當(dāng)前項(xiàng)目文件夾的,所以基于之前的理論都需要規(guī)避。pkg 的 README 中告訴我們 process.cwd() 還是會(huì)指向到真實(shí)的環(huán)境中,所以我們可以修改以上目錄的位置到 process.cwd() 來(lái)解決這個(gè)問題。
//pkg.js const path = require('path'); const Application = require('thinkjs'); const instance = new Application({ //在啟動(dòng)文件中可以自定義配置 runtime 目錄 RUNTIME_PATH: path.join(process.cwd(), 'runtime'), ROOT_PATH: __dirname, proxy: true, env: 'pkg', }); instance.run();
基于 production.js 我們新建一個(gè) pkg.js 啟動(dòng)文件,定義項(xiàng)目啟動(dòng)后的 RUNTIME_PATH 路徑,并將 env 賦值為 pkg,方便后續(xù)的配置中通過(guò) think.env === 'pkg' 來(lái)切換配置。
//src/config/adapter.js const {Console, DateFile} = require('think-logger3'); const isDev = think.env === 'development'; const isPkg = think.env === 'pkg'; exports.logger = { type: isDev ? 'console' : 'dateFile', console: { handle: Console }, dateFile: { handle: DateFile, level: 'ALL', absolute: true, pattern: '-yyyy-MM-dd', alwaysIncludePattern: true, filename: path.join(isPkg ? process.cwd() : think.ROOT_PATH, 'logs/app.log') } };
在 adapter 配置中我們將原來(lái)基于 think.ROOT_PATH 的路徑修改成基于 process.cwd()。除了日志服務(wù)之外,如果業(yè)務(wù)中有使用到 cache 和 session 等服務(wù),它們?nèi)绻彩腔谖募鎯?chǔ)的話,也需要修改對(duì)應(yīng)的文件存儲(chǔ)配置。當(dāng)然這些都是 ThinkJS 自帶的一些服務(wù),如果項(xiàng)目中有用到其它的一些服務(wù),或者說(shuō)本身的業(yè)務(wù)邏輯中有涉及到文件寫入的也都需要修改配置。
打包配置
項(xiàng)目的寫入操作規(guī)避掉之后我們就可以正常的配置 pkg 然后進(jìn)行打包處理了。一份簡(jiǎn)單的 pkg 模塊的配置大概是這樣的:
//package.json { "bin": "pkg.js", "pkg": { "assets": [ "src/**/*", "view/**/*", "www/**/*" ], "targets": [ "node10-linux-x64", "node10-macos-x64", "node10-win-x64" ] } }
這里我們指定了 pkg.js 為打包的入口文件,指定了需要編譯出 linux, macos, win 三個(gè)平臺(tái)的可執(zhí)行腳本,同時(shí)指定了需要將 src/, view/, www/ 三個(gè)目錄作為資源一塊打包進(jìn)去。這是因?yàn)?ThinkJS 是動(dòng)態(tài) require 的項(xiàng)目,具體的業(yè)務(wù)邏輯都是在執(zhí)行的時(shí)候通過(guò)遍歷文件目錄讀取文件的形式載入的,對(duì)于 pkg 模塊打包來(lái)說(shuō)無(wú)法在編譯的時(shí)候知道這些依賴關(guān)系,所以需要作為啟動(dòng)依賴的“資源”一塊打包進(jìn)去。
配置好后直接在項(xiàng)目目錄下執(zhí)行 pkg .,如果一切 OK 的話應(yīng)該能在當(dāng)前目錄中看到三個(gè)可執(zhí)行文件,直接執(zhí)行對(duì)應(yīng)平臺(tái)的二進(jìn)制文件即可啟動(dòng)服務(wù)了。
➜ www.thinkjs.org git:(master) npm run pkg-build > thinkjs-official@1.2.0 pkg-build /Users/lizheming/workspace/thinkjs/www.thinkjs.org > pkg ./ --out-path=dist > pkg@4.4.0 ➜ www.thinkjs.org git:(master) ✗ ls -alh dist total 577096 drwxr-xr-x 5 lizheming staff 160B 12 28 17:35 . drwxr-xr-x@ 30 lizheming staff 960B 12 28 17:34 .. -rwxr-xr-x 1 lizheming staff 87M 12 28 17:34 thinkjs-official-linux -rwxr-xr-x 1 lizheming staff 87M 12 28 17:35 thinkjs-official-macos -rw-r--r-- 1 lizheming staff 82M 12 28 17:35 thinkjs-official-win.exe ➜ www.thinkjs.org git:(master) ✗
后記
項(xiàng)目打包后有一個(gè)問題是配置沒辦法修改了,如果有動(dòng)態(tài)配置的需求的話就不是很方便了。這里提供兩個(gè)思路解決該問題:
- 將動(dòng)態(tài)的配置配置到環(huán)境變量中,程序通過(guò)讀取環(huán)境變量覆蓋默認(rèn)的配置。
- 利用 ThinkJS 提供的 beforeStartServer() 鉤子在啟動(dòng)前讀取真實(shí)目錄下的配置文件進(jìn)行配置覆蓋。
//pkg.js const path = require('path'); think.beforeStartServer(() => { const configFile = path.join(process.cwd(), 'config.js'); const config = require(configFile); think.config(config); });
另外隨著項(xiàng)目的復(fù)雜度提高,業(yè)務(wù)內(nèi)可能會(huì)引入大量的第三方模塊。前文只是解決了 ThinkJS 項(xiàng)目本身的動(dòng)態(tài)引入問題,如果引入的第三方模塊也有動(dòng)態(tài)引入的話也需要在 pkg.assets 配置中顯示指定出來(lái)。還有就是針對(duì) C++ 模塊,pkg 目前還沒有辦法做到自動(dòng)引入,同樣需要在 pkg.assets 中指定依賴資源。
//package.json { "pkg": { "assets": [ //以 node-sqlite3 模塊為例 "node_modules/sqlite3/lib/binding/node-v64-darwin-x64/node_sqlite3.node" ] } }
其中 node-v64-darwin-x64 可能會(huì)根據(jù)平臺(tái)不一樣導(dǎo)致名字不太一樣。無(wú)法引入 .node 模塊的原因是因?yàn)?C++ 模塊安裝的時(shí)候會(huì)通過(guò) node-gyp 進(jìn)行動(dòng)態(tài)編譯,該操作是和平臺(tái)相關(guān)的。也就是說(shuō)該特性和 pkg 模塊在一個(gè)平臺(tái)上能打包所有平臺(tái)的二進(jìn)制包特性是沖突的,畢竟 pkg 模塊也沒辦法在 Mac 平臺(tái)上編譯 Windows 平臺(tái)的模塊。所以在這種情況下除了需要手動(dòng)引入編譯后的 .node 模塊之外,還需要注意引入的該 .node 模塊和 pkg.targets 指定的編譯平臺(tái)的一致性。
獲取 .node 模塊除了在對(duì)應(yīng)平臺(tái)模塊安裝之外,也可以選擇下載其它同學(xué)提供編譯好的模塊。淘寶源上提供了很多二進(jìn)制模塊的編譯后結(jié)果,以 node-sqlite3 為例,它的所有編譯模塊可以在 https://npm.taobao.org/mirrors/sqlite3這里下載,自行選擇對(duì)應(yīng)的版本和平臺(tái)即可。
本文說(shuō)的打包配置都已在 ThinkJS 官網(wǎng) 項(xiàng)目中實(shí)現(xiàn),想要嘗試的同學(xué)可以直接克隆官網(wǎng)項(xiàng)目,安裝完依賴后執(zhí)行 npm run pkg-build 即可在 dist/ 目錄中獲得二進(jìn)制可執(zhí)行文件。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:小程序?qū)崿F(xiàn)長(zhǎng)按保存圖片的方法
欄 目:JavaScript
下一篇:使用webpack搭建vue環(huán)境的教程詳解
本文標(biāo)題:使用pkg打包ThinkJS項(xiàng)目的方法步驟
本文地址:http://mengdiqiu.com.cn/a1/JavaScript/9343.html
您可能感興趣的文章
- 04-02包含javascript舍的詞條
- 01-10使用webpack/gulp構(gòu)建TypeScript項(xiàng)目的方法示例
- 01-10使用JS來(lái)動(dòng)態(tài)操作css的幾種方法
- 01-10在Vue項(xiàng)目中使用Typescript的實(shí)現(xiàn)
- 01-10Vue中使用Lodop插件實(shí)現(xiàn)打印功能的簡(jiǎn)單方法
- 01-10echarts實(shí)現(xiàn)折線圖的拖拽效果
- 01-10JS數(shù)據(jù)類型STRING使用實(shí)例解析
- 01-10node使用request請(qǐng)求的方法
- 01-10angularjs模態(tài)框的使用代碼實(shí)例
- 01-10使用JavaScript計(jì)算前一天和后一天的思路詳解


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02javascript點(diǎn)線,點(diǎn)線的代碼
- 04-02javascript潛力,javascript強(qiáng)大嗎
- 04-02javascript替換字符串,js字符串的替換
- 04-02javascript移出,js 移入移出
- 04-02包含javascript舍的詞條
- 04-02javascript并行,深入理解并行編程 豆瓣
- 04-02javascript匿名,js匿名方法
- 04-02javascript警報(bào),JavaScript警告
- 04-02javascript遮蓋,JavaScript遮蓋PC端頁(yè)面
- 04-02javascript前身,javascript的前身
隨機(jī)閱讀
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10C#中split用法實(shí)例總結(jié)