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>


分享到:


相關文章: