NodeJS打包可執行文件工具:Pkg使用心得。

NodeJS打包可執行文件工具:Pkg使用心得。

這個項目很神奇,直接將node.js項目打包成windows可以直接執行的exe文件(也支持FreeBSD、linux、macos、arm系統),甚至不需要安裝Node.js,且無須修改你項目中的任何代碼!

首先安裝pkg

<code>npm install -g pkg/<code>

然後在項目目錄下執行

<code>pkg entrance.js/<code>

即可打包linux,macos,win3個平臺的可執行文件。entrance.js為你node項目的入口文件。

如果只想打包windows下的exe,則加上-t參數。win即為打包成windows平臺下的exe文件,具體可選參數參見項目目錄

<code>pkg -t win entrance.js/<code>

稍等片刻後項目目錄下就會生成打包好的entrance.exe文件。

pkg會自動從入口文件開始查找依賴的文件並全數打包進去,無須修改項目裡的任何代碼。

例:

NodeJS打包可執行文件工具:Pkg使用心得。

其他

pkg可以根據package.json下的配置進行打包,默認入口文件為bin指向的文件。

執行

<code>pkg ./<code>

或是

<code>pkg package.json/<code>

即可自動按照package.json的配置打包。

<code>//package.json
{
//其他配置項
"bin": "service.js",//入口文件
"pkg": {
"scripts": [
"build/**/*.js"//需要打包進來的其他js文件,可添加多個
],
"assets": [
"dist/**/*"//靜態文件的目錄,可添加多個
]
}
} /<code>

注意:靜態文件需要在項目中將文件的引用換成

<code>path.join(__dirname, 'dist')/<code>

的形式,才可以正常打包,否則可能會讀取不到。

示例

使用vue-cli建立項目,並使用npm run build將你的項目編譯生成靜態文件到dist目錄下。這些都是vue-cli自帶的內容,不再贅述。

在項目目錄下新建一個service.js文件,並添加以下代碼,在本地起一個express靜態服務器,使你能夠在本地訪問你的網站(部署到線上也是類似)

NodeJS打包可執行文件工具:Pkg使用心得。

<code>//service.js
const express = require('express');

const app = express();
const path = require('path');

app.use(express.static(path.join(__dirname, 'dist')));//注意這裡使用path.join(__dirname, 'dist')而不是'dist',雖然在命令行中執行起來效果是一樣的,不過pkg打包會無法識別到dist目錄

var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log(`AIbuy agents server start successfully on http://${host}:${port}`)
})/<code>

此時你可以在控制檯執行

<code>node service.js/<code>

來啟動你的服務器了,啟動完成後,瀏覽器訪問http://localhost:8081/即可查看你的網站。

接下來我們使用將service.js和dist目錄打包成一個exe文件,方便他人使用

首先安裝pkg

<code>npm install -g pkg/<code>

然後修改package.json,添加bin(如果不是service.js的話)和pkg項

<code>{
//其他配置項
"bin": "service.js",//指定入口文件
"pkg": {
"assets": [
"dist/**/*"//指定要打包的靜態文件目錄
]
}
}/<code>

然後在項目目錄下執行

<code>pkg -t win package.json/<code>

完成後即生成一個exe文件,雙擊啟動即相當於執行node service.js,然後你瀏覽器裡就能訪問打包好的項目了!跟之前用node啟動項目再訪問一樣,但這時不需要提前安裝node了。

但說實話,如果是複雜的項目、大型的項目、有文件讀寫的項目等,打包這個操作還是有些複雜的,需要考慮不少打包帶來的影響,也可能需要修改某些功能代碼。如果是用於代碼保護目的,防止發佈後別人看到自己的程序代碼,也可以用JShaman之類的JS代碼保護平臺對源碼進行加密,也是可以保護nodeJS代碼的。

下面是一個具體的示例,演示使用pkg打包egg(一個web框架)項目:

1、在egg配置文件中把涉及到寫文件的路徑都移到包外(pkg的虛擬文件系統只是用來應對讀的行為,所有寫相關都得移出包外)

<code>// 通過process.cwd()獲取當前執行文件執行的路徑
config.rundir = process.cwd() + '/run';// 配置執行時臨時文件的路徑
config.logger = {
dir: path.join(process.cwd(), 'logs', logDir),//配置普通日誌文件地址

};
config.customLogger = {
scheduleLogger: {
file: path.join(process.cwd(), 'logs', logDir, 'egg-schedule.log'),//配置定時任務日誌的地址
},
};
config.static = { // 必須把public移出項目,否則在pkg的包中egg的static中間件會有對public操作(確保文件夾),會有拋錯
prefix: '/',
dir: process.cwd() + '/public',//配置靜態文件的地址
};/<code>

2、修改package.json文件

<code>{
...


"bin": "pkg-entry.js", // 執行包的入口文件,可執行包啟動的時候默認會調用該文件

"pkg": {// 以下主要是聲明那些文件需要被打包(pkg會解析require中的靜態路徑,但在egg.js中很多文件都是通過框架引用的,無法依賴解析)
"scripts": [ // 這裡是聲明需要打包的js文件,這裡的聲明的js文件都會被編譯為v8字節碼(建議主動聲明,不要依賴pkg自動引入)
"./app/**/*.js",
"./config/**/*.js",
"./normalJs/**/*.js", // 不只是egg的文件,只要要用到就要聲明打包
"./app.js",
"./agent.js"
],
"assets": [ // 這裡是聲明需要打包的靜態文件(即使有js文件也不進行編譯)。

"./lib/**/*",// lib中是我打算開放的一些egg組件,所有不需要編譯
"./app/public/**/*",// 如果要把前端靜態文件打包進來,就直接聲明(但是在egg中static中間件會有拋錯,需要hack egg或者 hack pkg)
"./node_modules/**/*"// npm安裝的所有依賴包全部打包進來,不要依賴自動引入,很容易導致部分文件沒打包,出現各種意料外的錯誤
]
},
}/<code>

3、增加入口文件pkg-entry.js(名字保持和package.json中一致)

<code>const fs = require('fs');

// 如果是egg的ts項目,由於egg-script會給ts項目通過-r引入sourcemap的注入文件,但是pkg的spawn不支持,所以把項目標識為飛ts
// 如果不是ts項目忽略一下兩行
const pkgInfo = require('./package');
pkgInfo.egg.typescript = false; // 防止egg-script識別為 typescript 自動添加soucemap支持(--require 在pkg的spawn中不支持)

//我自己的工具包的配置文件是直接打包到安裝包裡面的,這樣就不方便修改配置了。於是把提供給運維配置的配置都適用dotenv來配置,以下引入dotenv的預執行腳本
//也可以考慮把配置文件放到包外,不過因為包內執行包外js,會增加被攻擊的風險
require('dotenv/config');


// 由於egg-script是默認以當前執行proccess.cwd() 路徑為默認項目的,打包後需要每次輸入 /snapshot/${項目文件夾名} 作為指定目錄
// 所以,以下為修改參數,自動嵌入“/snapshot/${項目文件夾名}”
const baseDir = '/snapshot/' + fs.readdirSync('/snapshot')[0];
console.log('baseDir:', baseDir);
// 當 start 的時候,自動嵌入bashDir為 /snapshot/${項目文件夾名}
const startIndex = process.argv.indexOf('start');
if (startIndex > -1) {
process.argv = [].concat(
process.argv.slice(0, startIndex + 1),
baseDir,
process.argv.slice(startIndex + 1),
);
}

// 然後直接調起egg-scripts執行
require('./node_modules/egg-scripts/bin/egg-scripts.js');/<code>

正如前面所說,用pkg打包,可能會有這些修改過程,所以說如果是單純的防代碼洩露是可以用JShaman來完成的。

4、 執行打包

<code>## --targets 用於制定平臺和node版本,不指定時默認為3個平臺以package.json中的node版本配置為準
## --out-path 指定執行包輸出文件夾,默認為當前文件夾
## --debug 用於調試,可瞭解哪些文件被打包
pkg . --targets node8-linux-x64 --out-path /usr/dist --debug/<code>

5、運行

<code>> chmod a+x ./appName #給可執行包增加執行權限 

> ./appName start # 啟動項目,除項目路徑其它參數都和egg-scripts一致 可以用--title指定egg服務名
>./appName stop # 關閉項目(會關閉當前服務器的所有egg服務,如果有多個,最好用--title來指定要關閉的項目)/<code>


分享到:


相關文章: