使用pkg打包Node.js应用的方法步骤
作者:jingsam 发布时间:2024-05-13 09:58:30
Node.js应用不需要经过编译过程,可以直接把源代码拷贝到部署机上执行,确实比C++、Java这类编译型应用部署方便。然而,Node.js应用执行需要有运行环境,意味着你需要先在部署机器上安装Node.js。虽说没有麻烦到哪里去,但毕竟多了一个步骤,特别是对于离线环境下的部署机,麻烦程度还要上升一级。假设你用Node.js写一些小的桌面级工具软件,部署到客户机上还要先安装Node.js,有点“大炮打蚊子”的感觉。更严重的是,如果部署机器上游多个Node.js应用,而且这些应用要依赖于不同的Node.js版本,那就更难部署了。
理想的情况是将Node.js打包为一个单独的可执行文件,部署的时候直接拷贝过去就行了。除了部署方便外,因为不需要再拷贝源代码了,还有利于保护知识产权。
将Node.js打包为可执行文件的工具有pkg、nexe、node-packer、enclose等,从打包速度、使用便捷程度、功能完整性来说,pkg是最优秀的。这篇文章就来讲一讲半年来我使用pkg打包Node.js应用的一些经验。
pkg的打包原理简单来说,就是将js代码以及相关的资源文件打包到可执行文件中,然后劫持fs里面的一些函数,使它能够读到可执行文件中的代码和资源文件。例如,原来的require('./a.js')会被劫持到一个虚拟目录require('/snapshot/a.js')。
安装
pkg既可以全局安装也可以局部安装,推荐采用局部安装:
npm install pkg --save-dev
用法
pkg使用比较简单,执行下pkg -h就可以基本了解用法,基本语法是:
pkg [options] <input>
<input>可以通过三种方式指定:
1.一个脚本文件,例如pkg index.js;
2.package.json,例如pkg package.json,这时会使用package.json中的bin字段作为入口文件;
3.一个目录,例如pkg .,这时会寻找指定目录下的package.json文件,然后在找bin字段作为入口文件。
[options]中可以指定打包的参数:
1.-t指定打包的目标平台和Node版本,如-t node6-win-x64,node6-linux-x64,node6-macos-x64可以同时打包3个平台的可执行程序;
2.-o指定输出可执行文件的名称,但如果用-t指定了多个目标,那么就要用--out-path指定输出的目录;
3.-c指定一个JSON配置文件,用来指定需要额外打包脚本和资源文件,通常使用package.json配置。
使用pkg的最佳实践是:在package.json中的pkg字段中指定打包参数,使用npm scripts来执行打包过程,例如:
{
...
"bin": "./bin/www",
"scripts": {
"pkg": "pkg . --out-path=dist/"
},
"pkg": {
"scripts": [...]
"assets": [...],
"targets": [...]
},
...
}
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)
这些路径都不是常量,pkg没办法帮你自动识别要打包哪个文件,所以文件就丢失了,所以这时候就使用scripts和assets来告诉pkg,这些文件要打包进去。那么我们怎么知道哪些文件没有被打包呢?难倒要一行行地去翻源代码吗?其实很简单,只需要把打包好的文件运行下,报错信息一般就会告诉你缺失哪些文件,并且pkg在打包过程中也会提示一些它不能自动打包的文件。
注意事项
如果说pkg还有哪儿还可以改进的地方,那就是无法自动打包二进制模块*.node文件。如果你的项目中引用了二进制模块,如sqlite3,那么你需要手动地将*.node文件复制到可执行文件同一目录,我通常使用命令cp node_modules/**/*.node .一键完成。但是,如果你要跨平台打包,例如在windows上打包linux版本,相应的二进制模块也要换成linux版本,通常需要你手动的下载或者编译。
那为什么pkg不能将二进制模块打包进去呢?我猜想是require载入一个js文件和node文件,它们的机制是不一样的。另外从设计来说,不自动打包二进制模块也是合理的,因为二进制模块都是平台相关的。如果我在windows上生成一个linux文件,那么就需要拉取linux版本的.node文件,这是比较困难的。并且有些二进制模块不提供预编译版本,需要安装的时候编译,pkg再牛也不可能模拟一个其他平台的编译环境吧。nexe可以自动打包二进制模块,但是只能打包当前平台和当前版本的可执行文件。这意味着如果Node.js应用引用了二进制包,那么这个应用就不能跨平台打包了,所以我认为这方面,nexe不能算是一个好的设计。
还有一点就是关于项目中的配置文件处理,比如数据库连接参数、环境变量等。因为这些配置文件会跟着不同的部署环境进行更改,所以为了方便更改,一般不希望把配置文件打包到exe。为了避免pkg自动地将配置文件打包到exe中,代码中不要采用以下方式读取配置文件:
fs.readFile(path.join(__dirname, './config.json')), callback)
而是采用相对于process.CWD()的方法读取:
fs.readFile(path.join(process.CWD(), './config.json'), callback)
// 或者
fs.readFile('./config.json', callback)
如果配置文件是js格式的,那么不要直接require('./config'),而是采用动态require:
const config = require(process.CWD() + './config')
另外要提及的是pkg打包之后动态载入js文件会有安全性问题,即用户可以在js文件写任何处理逻辑,注入到打包后的exe中。例如,可以读取exe里面的虚拟文件系统,把源代码导出来。所以,尽量不要采用JS作为配置文件,也不要动态载入js模块。
来源:https://jingsam.github.io/2018/03/02/pkg.html


猜你喜欢
- 注:我指一个网站被第三方网站以iframe的形式调用时,被调用网站的禁止策略 和 调用网站的突破禁止策略,跟XSS麽关系,但跟clickja
- 不多说,直接上代码from hdfs import Clientimport pandas as pdHDFSHOST = "ht
- 目录1.1 题目1.2 思路1.2.1 发送请求1.2.2 解析网页1.2.3 获取结点1.2.4 数据保存 (单线程)1.2.4 数据保存
- 前言:之前,我写笔记的工具一直都是 notion,而且没有写博客的习惯。但是一是由于 notion 的服务器在
- 准备图片选择一张shape为(500,500,3)的梵高的《星月夜》以便示例。1. 缩放 cv2.resize()方法cv2.resize(
- 目录构建消息对象发送邮件要点在很多时候,使用 Python 发送邮件可能没有办法使用邮件服务器提供的 API,因为不是所有的邮件服务商都会提
- 因此,常常出现这样的错误select * from [IND] where INDID>10unionselect * from [I
- 注:本篇文章主要介绍如何在 Go 语言中定义和使用自定义类型,涉及到一定的编程基础知识和语法。如有不熟悉的地方,建议先去学习相关的基础知识。
- 可以用JS来监控播放器的事件,如果播放完毕了,就用Js跳转到下一集的播放页面, 这样就可以间接实现连续播放了 PlayStateChange
- 本文实例讲述了python生成器用法。分享给大家供大家参考,具体如下:1. 生成器利用迭代器,我们可以在每次迭代获取数据(通过next()方
- Zeroc Ice简介 Zeroc ICE(Internet Communications Engine ,互联网通信引擎)是目前功能比较
- 主要记录一下:图片验证码1.获取登录界面的图片2.获取验证码位置3.在登录页面截取验证码保存4.调用百度api识别(目前准确率较高的识别图片
- 一、验证计算机服务列表中是否有MySQL服务(1)右击桌面上的计算机图标出现菜单列表选项,选中管理(有多种方式可以打开计算机本地服务列表,这
- 起步Python 提供的多线程模型中并没有提供读写锁,读写锁相对于单纯的互斥锁,适用性更高,可以多个线程同时占用读模式的读写锁,但是只能一个
- 1、首先模拟python类似shell命令行操作的接口:python安装subprocess(本地)、paramiko(SSH远程)#-*-
- 原来在robotframework中使用press key方法进行键盘的操作,但是该方法需要写 * 作对象的locator,不是很方便,现在找
- 这篇文章主要介绍了Python PyPDF2模块安装使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 1.多边形的绘制案例# 多边形的绘制案例import turtledef main():turtle.color("green&q
- 初学python,抓取搜狗微信公众号文章存入mysqlmysql表:代码:import requestsimport jsonimport
- 如何在寸土寸金的首页上使页面的价值最大化,是每个网站设计者最关心的话题。用户关注的页面长度、宽度都是有限的。宽度自不必说,一般网站都会根据自