实际上,nodejs 项目本来不需要做什么打包编译动作的,因为 js 本来也就不是编译型语言,只是个解释型语言,所以只要有 nodejs 运行环境,代码拷过去就能运行。

但是难免有些机器可能并没有安装 nodejs 运行环境,或者项目代码指定了 nodejs 的运行环境版本与实际不符合,就会多一个步骤去规整化运行环境的问题。

这本来不是个问题,那么简单打包后,变成一个可执行文件,就可以不用担心运行环境的问题了。

本文目的:将 Node 项目打包为可执行文件,可以在没有安装 Node.js 运行环境的设备上运行。

主要介绍两种将 nodejs 项目打包的工具,pkg 和 nexe。

使用 pkg 打包 nodejs 项目(网上推荐较多)

注意 pkg 支持的 nodejs 版本问题

注意:pkg 各个版本所支持的 Node 版本有所不同(主要集中在长期支持版本)。
例如最新版本之 pkg4.4 不支持 Node9 版本。

如何查看 pkg 支持的 nodejs 版本?

访问 pkg 源码 package.json,在”dependencies”属性中,查看”pkg-fetch”的版本。这个 pkg-fetch 可以获取的 nodejs 版本,就是 pkg 工具包可以打包的版本。

例如现在的 pkg 默认是 4.4.2,对应的 pkg-fetch 是 2.6.4,找到 pkg-fetch 对应版本的源文件中的 patches.json 文件,地址[https://github.com/zeit/pkg-fetch/blob/master/patches/patches.json],得到以下数据:

查看pkg支持的nodejs版本

如果安装的 pkg 版本,和实际需要打包的 nodejs 项目的版本不同,则可能会出现类似pkg error no available node version satisfies 'node 9'的报错信息。

为了节约大家的时间,秉承用新不用旧,我简单列举以下(2019/12/17 记):

nodejs 版本为 v12、v10、v8、v6、v4、v0.12,用 pkg4.4 版本;
nodejs 版本为 v9,用 pkg4.3 版本;
nodejs 版本为 v7,用 pkg4.2 版本;

我好像没找到 pkg-fetch 中的 patches.json 有支持 nodejs v11、v5 版本的,所有就不列示了。

一般习惯,还是使用长期支持版本的 nodejs 开发为好,这样也可以直接使用最新版本的 pkg 而忽略版本问题了。

安装配置

全局安装:

npm install -g pkg

安装成功,输入pkg -h就可以看到指令信息了。

一般操作说明如下:

pkg 的基本语法是pkg [options] ,那么

[option] 简单说明:

-t:指定打包的目标平台和 Node 版本,如-t node12-linux-x64,node12-win-x64,node12-macos-x64.
-c:指定一个 JSON 配置文件,用来配置额外的打包脚本或资源。
–options:指定 Node 或 V8 运行时选项,打包后默认执行,通过–option name 取消。
-o:指定输出可执行文件名称,若使用了-t 指定多个目标,则需要使用—out-path 指定输出路径。
-d:输出打包日志,以便排查问题。
-b:从 node 源代码编译 node 二进制文件,默认会下载官方预编译的文件,使用该选项便不会有前面说明的 Node 版本支持问题,初次编译会耗用大量时间。

可通过三种方式指定:

项目的入口文件如:pkg app.js;
项目的 package.json 文件,pkg 会使用 package.json 中配置 bin 属性作为入口文件。
项目的路径,pkg 会寻找路径中的 package.json。

打包项目:

我简单的生成一个 express 项目,然后用来说明打包过程

创建 express 项目(如果没有安装过 express-generator,需要先安装npm install express-generator -g,再使用)

express node-express-demo

1、首先在 package.json 中配置 bin 属性作为入口。添加以下属性(express 项目默认入口文件就是./bin/www):

 "bin": "./bin/www"

如存在不能自动打包的文件(如:require(变量)、非 Javascript 文件),则需要通过 pkg 属性手动配置。同样在 package.json 中添加 pkg 属性,类似:

 "pkg": {
    "scripts": ["xxx","xx"],
    "assets": ["xxx","xx"]
  }

再执行 pkg 打包命令(注意你的 os 平台和 nodejs 版本,例如我的是 ubuntu 下 nodejs12.16 版本):

david@ubuntu:~/TTT/node-express-demo$ pkg -t node12-linux-x64 -o pkg-ned -b package.json
> pkg@4.4.2
> Building base binary from source:
  built-v12.13.1-linux-x64
> Cloning Node.js repository from GitHub...
  git                          [====================] 100%
> Checking out v12.13.1
> Applying patches
> Compiling Node.js from sources...
  make                         [====================] 100%
david@ubuntu:~/TTT/node-express-demo$

这个首次编译打包等待时间可能会有好长,好长,好长,耐心等待一下。别看着没进度就忍不住把它终止了。
但这是第一次执行时,后续同样的编译需求,则直接使用缓存的编译环境资源,就几秒钟了。

pkg 无法自动打包二进制模块文件,该类文件与平台有关,若依赖中包含此类文件需手动复制到打包后可执行文件目录下
如 node-java 有编译 nodejavabridge_bindings.node 文件,需从 node_modules/java/build/Release 中复制出来。

运行打包后文件,程序可正常运行。

为方便使用,将 pkg 打包命令写入到 package.json,打包时运行 npm run pkg

在 package.json 的 script 属性下,加入一句,例如:

"pkg": "pkg -t node12-linux-x64 -o pkg-ned -b package.json"

打包成功后,可以在 nodejs 项目的根目录下看见生成了 pkg-ned 文件,直接运行示例如下:

pkg打包文件运行结果

待解决问题:
我之前使用成品的项目打包时没有遇到,但是这次直接使用的 express-generator 生成的模板项目打包,则出现了,运行打包后可执行文件,首页访问报错的问题,如下图:

pkg打包文件运行访问首页报错

这个问题待我找到原因再补充上。

使用 nexe 打包 nodejs 项目(实际测试较好用)

nexe 可以通过命令行将 Node 项目打包为可执行文件。
值得一说的是,nexe 预编译之 Node 只有偶数版本,所以例如使用 v9 版本,只能自行编译。

安装 nexe:
npm install -g nexe

安装成功后,终端输入 nexe -h就可以看到命令格式了。

一般使用格式是

nexe  [options]

简单说明[options]:
-i:指定 Node 项目入口文件
-o:指定输出可执行文件路径名称
-t:指定編譯平臺 Node 版本(支援版本)
-n:指定主模块名称
-r:添加资源文件
-a:指定已构建的 nexe 二进制文件
–build:从源码编译 Node

最简单的使用 ,在 nodejs 项目下运行 nexe <入口文件>,例如我的是 .bin/www,但是 nodejs 版本是 9.x

david@ubuntu:~/TTT/node-express-demo$ nvm use 9.11.2
Now using node v9.11.2 (npm v5.6.0)
david@ubuntu:~/TTT/node-express-demo$ nexe ./bin/www
ℹ nexe 3.3.2
✔ Downloading pre-built Node.js
✔ Finished in 1.037s

Error: https://github.com/nexe/nexe/releases/download/v3.0.0/linux-x64-9.11.2 is not available, create it using the --build flag

See nexe -h for usage..

david@ubuntu:~/TTT/node-express-demo$

因为没有 nodev9 不是预编译版本,所以报错。因此就需要使用–build,如下:

nexe -i app.js -o nexe-ned -t linux-x64-9.11.2 --build

需要等待一些时间,还需要联网。整个过程就是下载对应的 node 资源,然后编译,写文件。当然,这也是第一次执行时,后续同样的编译需求,则直接使用缓存的编译环境资源。

具体显示流程如下:

david@ubuntu:~/TTT/node-express-demo$ nexe -i ./bin/www -o nexe-ned -t linux-x64-9.11.2 --build
ℹ nexe 3.3.2
✔ Node source extracted to: /home/david/.nexe/9.11.2
✔ Node binary compiled
✔ Entry: 'bin/www' written to: nexe-ned
✔ Finished in 2057.42s
david@ubuntu:~/TTT/node-express-demo$

运行效果如下:

运行nexe打包后文件

小结

总结起来,感觉使用 nexe 效果更好一点:
首先没有版本限制,只是看能不能预编译(LTS 版本有预编译);
额外的配置需求相对较少;
测试模板项目测试没有出现意外;
首次编译打包的时间相对较短很多。

当然这只是我的个人看法,也有人询问过 nexe 和 pkg 的区别,也引用如下:

nexe:

  • Bundles the application if desired using webpack.
  • Downloads node source (or a prebuilt binary)
  • Adds your application bundle as a native module (like fs, http, path etc)
  • Applies arbitrary source patches.
  • Maybe compiles downloaded source
  • Inserts bundle into pre-sized binary
  • Code is run as main when executable is run (instead of the repl)

pkg:

  • Bundles the application with a custom v8 script compiler into a snapshot
  • Downloads the node source (or a prebuilt binary)
  • Applies arbitrary source patches
  • Maybe compiles downloaded source
  • Appends snapshotted output to the end of the binary
  • Snapshot (cachedData from v8) is loaded/run when binary executes.

(以上可窥见 pkg 打包文件首页访问出错原因)

不过两者都是不错的 nodejs 打包工具了,有需求都可能尝试一下。