使用pkg打包Node.js应用的方法步骤


Posted in Javascript onOctober 19, 2018

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模块。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScipt基本教程之前言
Jan 16 Javascript
JavaScript 继承详解(四)
Jul 13 Javascript
FileUpload 控件 禁止手动输入或粘贴的实现代码
Apr 07 Javascript
原生JS实现表单checkbook获取已选择的值
Jul 21 Javascript
jquery五角星评分插件示例分享
Feb 21 Javascript
javascript实现在指定元素中垂直水平居中
Sep 13 Javascript
jQuery 操作input中radio的技巧
Jul 18 Javascript
AngularJS中的promise用法分析
May 19 Javascript
深究AngularJS如何获取input的焦点(自定义指令)
Jun 12 Javascript
鼠标拖动改变DIV等网页元素的大小的实现方法
Jul 06 Javascript
jquery写出PC端轮播图实例
Jan 26 jQuery
Typescript3.9 常用新特性一览(推荐)
May 14 Javascript
简化版的vue-router实现思路详解
Oct 19 #Javascript
vue中el-upload上传图片到七牛的示例代码
Oct 19 #Javascript
浅析vue-router原理
Oct 19 #Javascript
vue-cli3.0 脚手架搭建项目的过程详解
Oct 19 #Javascript
vue-quill-editor+plupload富文本编辑器实例详解
Oct 19 #Javascript
vue+VeeValidate 校验范围实例详解(部分校验,全部校验)
Oct 19 #Javascript
浅析JS中什么是自定义react数据验证组件
Oct 19 #Javascript
You might like
Oracle Faq(Oracle的版本)
2006/10/09 PHP
用PHP制作的意见反馈表源码
2007/03/11 PHP
基于PHP字符串的比较函数strcmp()与strcasecmp()的使用详解
2013/05/15 PHP
浅析PHP中的UNICODE 编码与解码
2013/06/29 PHP
PHP+jQuery+Ajax实现分页效果 jPaginate插件的应用
2015/10/09 PHP
CodeIgniter框架基本增删改查操作示例
2017/03/23 PHP
PHP如何防止XSS攻击与XSS攻击原理的讲解
2019/03/22 PHP
php7 参数、整形及字符串处理机制修改实例分析
2020/05/25 PHP
IE/FireFox具备兼容性的拖动代码
2007/08/13 Javascript
DWR Ext 加载数据
2009/03/22 Javascript
jQuery 第二课 操作包装集元素代码
2010/03/14 Javascript
始终在屏幕中间显示Div的代码(css+js)
2011/03/10 Javascript
手机端页面rem宽度自适应脚本
2015/05/20 Javascript
浅谈window对象的scrollBy()方法
2015/07/15 Javascript
JS实现的左侧竖向滑动菜单效果代码
2015/10/19 Javascript
Ajax使用原生态JS验证用户名是否存在
2020/05/26 Javascript
利用JS实现scroll自定义滚动效果详解
2017/10/17 Javascript
Angular.js实现获取验证码倒计时60秒按钮的简单方法
2017/10/18 Javascript
JavaScript实现一个带AI的井字棋游戏源码
2018/05/21 Javascript
vue2使用keep-alive缓存多层列表页的方法
2018/09/21 Javascript
JavaScript数据结构与算法之检索算法示例【二分查找法、计算重复次数】
2019/02/22 Javascript
利用PHP实现递归删除链表元素的方法示例
2020/10/23 Javascript
[01:58]最残酷竞争 2016国际邀请赛中国区预选赛积分循环赛回顾
2016/06/28 DOTA
用pandas中的DataFrame时选取行或列的方法
2018/07/11 Python
Python基于滑动平均思想实现缺失数据填充的方法
2019/02/21 Python
Python Flask上下文管理机制实例解析
2020/03/16 Python
python如何删除文件、目录
2020/06/23 Python
最新Python idle下载、安装与使用教程图文详解
2020/11/28 Python
欧洲有机婴儿食品最大的市场:Organic Baby Food(供美国和加拿大)
2018/03/28 全球购物
土木工程毕业生自荐信
2013/11/12 职场文书
计算机通信工程专业毕业生推荐信
2013/12/24 职场文书
员工入职担保书范文
2014/04/01 职场文书
程序员求职信
2014/04/16 职场文书
医学求职自荐信
2014/06/21 职场文书
科长个人四风问题整改措施思想汇报
2014/10/13 职场文书
幼儿园托班开学寄语(2016春季)
2015/12/03 职场文书