概要:如果将node项目打包为一个可执行的文件,那么对于部署、安装来说,以及源码的保护性都有很多的好处
1.全局安装pkg库,也可以不局部安装,全局安装为了方便(npm install pkg -g)
以下为了演示,用express搭建一个默认的web项目,如下,结构如下:
2. 编辑package.json文件,如下:
{
"name": "test",
"version": "0.0.0",
"private": true,
"bin":"./bin/www",
"scripts": {
"start": "node ./bin/www",
"pkg": "pkg . -t node10-win-x64 -o app"
},
"pkg":{
"assets":["public/**/*","views/*"]
},
"dependencies": {
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"http-errors": "~1.6.2",
"jade": "~1.11.0",
"morgan": "~1.9.0"
},
"bundledDependencies": ["express"]
}
解释:1)指定bin,为项目启动文件
pkg .,会
寻找指定目录下的package.json
文件,然后在找bin
字段作为入口文件。
-t
指定打包的目标平台和Node版本,如-t node6-win-x64,node6-linux-x64,node6-macos-x64
可以同时打包3个平台的可执行程序
-o
指定输出可执行文件的名称,但如果用-t
指定了多个目标,那么就要用--out-path
指定输出的目录
scripts
和assets
用来配置未打包进可执行文件的脚本和资源文件,文件路径可以使用glob通配符。这里就浮现出一个问题:为什么有的脚本和资源文件打包不进去呢?
要回答这个问题,就涉及到pkg打包文件的机制。按照pkg文档的说法,pkg只会自动地打包相对于__dirname
、__filename
的文件,例如path.join(__dirname, '../path/to/asset')
。至于require()
,因为require本身就是相对于__dirname
的,所以能够自动打包。假设文件中有以下代码:
require('./build/' + cmd + '.js')
path.join(__dirname, 'views/' + viewName)
cmd和viewname这些路径都不是常量,pkg没办法帮你自动识别要打包哪个文件,所以文件就丢失了,所以这时候就使用scripts
和assets
来告诉pkg,这些文件要打包进去
3. 运行npm run pkg打包 出来你想要的可执行文件
附:我配置pkg中的assets和我app.js对应代码
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
4. pkg打包后的路径和node有些区别,详情对应如下
比如:__dirname 对应 proccess.cwd()
exe文件路径:process.execPath
build失败异常解决
1. pkg需要特殊的node环境,默认去cache目录找,没有从github下载(比较慢,可能会造成打包失败)
解决办法:直接下对应的文件放在pkg的cache目录下面
1. 对应的fetch下载地方: https://github.com/vercel/pkg-fetch/releases
2. 一般全局安装后pkg后,pkg的缓存目录是在 C:\Users\用户名\.pkg-cache\
3. 将下载好的文件放在目录下重新打包就不会下载,打包成功率会高很多!