将Node.js应用程序编译成.exe文件
在开发阶段,开发者要执行几个安装和编码的过程来构建和运行一个应用程序。但是,终端用户只是对运行应用程序感兴趣,而不是对底层代码和进程感兴趣。
这就导致需要一个简单的可执行文件,可以在任何操作系统上运行,而不需要用户执行其他步骤来运行该应用程序。
在本教程中,我们将学习如何为我们的Node.js应用程序创建可执行文件。然后,我们将把我们的JavaScript文件编译成一个可执行文件。
在本教程结束时,你将学到以下内容。
.exe
文件以及它们的重要性。
.exe
文件的包。
要想快速跟上本教程,你至少需要具备以下条件。
如果你对上述要求都准备好了,让我们看看一些软件包来实现我们的目标。
什么是可执行文件(.exe)?
可执行文件(.exe文件)。这些文件包含一组编码指令,一旦用户点击该文件或运行该文件就会按顺序执行。
.exe
是Windows的一个扩展名,指的是可执行文件。其他操作系统也有可执行文件,但扩展名各不相同。例如,在Linux或基于Unix的操作系统中,以 、 或 为扩展名的文件是可执行文件,而在MacOS中,它们
.bin
.elf
[none]
没有
扩展名。
我们将专注于创建Windows可执行文件,并在接下来的过程中触及一些其他操作系统。
可执行文件的优点
让我们看看为什么用户首先喜欢可执行文件。
可执行文件的一些优点包括。
快速的无代码执行 :只需要点击一下就能运行文件,仅此而已。不需要在终端上运行命令来执行程序。
防止不需要的代码改变 :它可以防止由于计划外和意外地修改源代码而产生的不需要的错误。
支持分发 :由于与硬件无关,用户不需要安装任何软件包或依赖性。这使得人们可以在任何机器上运行代码的多个实例,而没有进一步的要求。
使用的软件包
两个最常用的用于将JavaScript文件编译成可执行文件的软件包是。
我们现在将得到一个正在运行的简单Node.js应用程序,并将其作为我们的应用程序。
一个简单的网络应用程序
这个网络应用将在5000端口的localhost上运行,并显示在images文件夹中找到的图像。首先,创建一个名为 "可执行 "的目录,并创建文件和文件夹,如下面的文件夹结构所示。
用以下方法初始化该项目。
npm init -y
使用安装项目所需的包。
npm i express
文件夹结构
该应用程序的文件夹结构将如下图所示。
├── node_modules (folder)
├── views (folder)
│ └── images (folder)
│ └── tv1.jpg (file)
│ └── index.html (file)
│ └── style.css (file)
├── index.js (file)
└── package.json (file)
在图像文件夹中,从网上下载一个免费的760×380的电视图像,并将其命名为 "tv1.jpg"。
index.html
HTML标记代码如下。
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exercutable Files</title>
<link rel="stylesheet" href="style.css">
</head>
<!--Display text and a tv image-->
<h1>Hello, world!</h1>
<p>Its you again, Hello there!</p>
<div class="photo">
<h2>TV 1</h2>
<img src="./images/tv1.jpg" alt="tv1" class="img">
<h2>TV 2</h2>
<img src="./images/tv1.jpg" alt="tv2" class="img">
</div>
</body>
</body>
</html>
style.css
html {
font-size: 62.5%;
font-family: serif;
body {
font-size: 1.8rem;
line-height: 1.618;
max-width: 38em;
margin: auto;
color: #4a4a4a;
background-color: #f9f9f9;
padding: 13px;
font-size: 2.35em;
font-size: 2em;
border-color: #2c8898;
img {
max-width: 50vh;
index.js
/*jshint strict:false */
(function() {
'use strict';
// this function is strict...
}());
// Setting up our app requirements
const express = require('express');
const app = express();
const Server = require('http').Server;
const server = new Server(app);
const path = require('path');
const port = 5000;
// Setting up our port
server.listen(port, () => console.log("Server at 5000"));
// Configuiring simple express routes
// getDir() function is used here along with package.json.pkg.assets
app.use('/', express.static(getDir() + '/views'));
app.get('/', function(req, res) {
res.sendFile(getDir() + '/views/index.html');
// Using a function to set default app path
function getDir() {
if (process.pkg) {
return path.resolve(process.execPath + "/..");
} else {
return path.join(require.main ? require.main.path : process.cwd());
注意:在使用Express或fastify路由时,使用getDir()
函数或path.join
,而不是__dirname
,以避免错误。
package.json
自动生成的代码将如下图所示。
"name": "executable",
"version": "1.0.0",
"description": "Simple express app",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1"
在PC上使用Ctrl +
或在Mac上使用Control + Shift +
,打开内置的Visual Studio代码终端。
在终端上使用以下命令运行该应用程序,以获得 "服务器在5000 "的输出。
node index.js
打开浏览器,通过访问localhost:5000
,打开该应用程序。完成后,让我们使用软件包将该应用程序转换为Windows可执行文件。
让我们用所有需要的包和资源将我们的项目编译成一个可执行文件。除了 "node_modules "之外,其他需要的资源也在根目录中找到,那就是 "views "文件夹。
Node.js软件包将被自动添加,所以我们只指定其他所需的资源。
nexe安装
在终端运行以下命令,在全局范围内安装nexe
。
npm i nexe -g
如果你使用的是Ubuntu,请运行。
sudo npm i nexe -g
注意:与pkg模块不同,我们不在本地安装软件包,而是在全局安装。如果你在本地安装它,你将面临许多错误。该错误的一个例子是Command' nexe' not found
。
完成后,运行以下程序来验证它是否被安装。
nexe -h
使用nexe将一个JavaScript文件编译成一个可执行文件
现在,让我们运行下面的程序,将应用程序的入口点转换成可执行文件。
nexe index.js
这告诉nexe
,在构建过程中,它应该只查看 "index.js "文件并将其编译为可执行文件。完成后,在终端上运行以下命令,开始构建过程。
nexe --build
我们将创建一个新的可执行文件,其名称与我们根目录的名称 "executable "相同。默认情况下,它将使用系统的操作系统和架构类型作为应用程序的目标。
这是为了让你能在你的系统上快速运行它。在我们的例子中,它将创建名称为 "可执行 "的Windows可执行文件。
双击它或右键单击并选择运行命令来启动该应用程序。它会自动注销 "服务器在5000",意味着它正在运行。
在首选浏览器中访问该应用程序,网址是:localhost:5000
。你可以通过点击关闭窗口的按钮来终止该进程。
为了证明只有入口点被编译,而不是在根目录中发现的资源。资源,如 "views "文件夹中的资源,将可执行文件复制并粘贴在一个单独的文件夹中并运行它。它会弹出错误,因为应用程序将无法访问资源。
在Linux或MacOs上运行
如果你是在Linux上,你可以使用下面的命令运行生成的可执行文件。
./executable
你可以用Ctrl + C
关闭它。
在MacOS上,只要双击就可以打开。然后,用关闭按钮将其关闭。
使用nee将整个项目编译成一个可执行文件
我们可以通过两种方式来完成这个任务。
首先,我们可以在命令行中使用--resources
或-r
标志添加资源,如下图所示。
nexe -r "views/**/*"
我们还可以添加额外的命令,如使用--output
或-o
输出可执行文件的路径,使用--target
或-t
目标,使用--name
或-n
可执行文件的名称,使用--build
或-b
从源代码构建等等。
nexe
中的'target'用于指定平台(Windows、Linux或MacOS)、arch或架构类型(x86、x64)以及Node.js版本(12、14、16)。
我们可以在 "package.json "文件中定义资源。我推荐这样做,因为它可以保存你的配置,以便以后重新运行。由于我们已经声明我们的应用程序 "main "是 "index.js",我们将额外引入一个构建脚本。
前往 "package.json "文件,在scripts标签下,添加一个构建脚本,如下所示。
"scripts": {
"start": "node index.js",
"build": "nexe -r views/**/*",
"test": "echo \"Error: no test specified\" && exit 1"
这将指定我们在构建时在本地找到的资源。
现在运行下面的命令,告诉nexe
,以使用构建标签中的规格。
npm run build
要启动构建过程,请使用以下命令。
npm --build
你可以运行创建的文件。你也可以把它复制到另一个没有任何资源的目录,并尝试运行它来验证资源是否被编译到可执行文件中。
让我们使用之前创建的项目,看看结果。
你可以删除之前创建的根目录(可执行文件)的内容,或者用不同的名字创建一个新的目录,并重复我们上面的步骤来创建一个新的项目。这可以减少由于软件包之间的冲突而导致的漏洞错误。
pkg安装
你可以先用下面的命令安装软件包,用于本地安装。
npm i pkg
或对于全局安装使用。
npm i pkg -g
请等待几秒钟,让它完成这个过程。然后,在完成后,检查并关闭任何在后台运行代码的实例。
使用pkg将一个JavaScript文件编译成一个可执行文件
请运行以下命令,其中我们将调用pkg模块和我们应用程序的入口。
在我们的例子中,是index.js
。
pkg index.js
由于没有指定目标,这将把我们应用程序的入口点编译成三个不同的可执行文件。只需等待一些时间来完成这个过程。到主目录去看看。你会发现三个新生成的文件。
用于Windows (index-win.exe)
用于Linux (index-linux)
用于Mac(index-macos)
这是因为我们没有指定一个操作系统。运行适合你操作系统的文件。例如,在Windows中,双击 "index-win.exe "图标。
它将启动该应用程序,并显示一个控制台,作为你和该应用程序之间的互动界面。在首选浏览器中访问该应用程序,网址是:localhost:5000
。正如所见,它运行良好。
注意:只要可执行文件在运行,该应用程序就在运行。
在Linux或MacOS上运行
如果你使用的是Ubuntu,在终端上运行./index-linux
。然后,确认它是否在浏览器上运行:localhost:5000
。
使用Ctrl + C
关闭它。
如果你使用的是MacOS,双击 "index-macos "图标并运行你编译的应用程序。然后,使用关闭按钮将其关闭。
更深入的研究
现在让我们再深入一点,指定操作系统和Node.js版本。在此,我们使用--targets
标志来设置目标操作系统。
删除前面过程中生成的可执行文件。
在终端运行以下内容。
pkg index.js --targets node12-win-x64
这指定了该项目应被编译成可执行文件,在Node.js版本12和64位架构的Windows O/S上运行。
一些支持的Node.js范围、平台和架构如下表所示。
Node.js版本 平台 架构 node8 alpine x64 node10 linux arm64 node12 linuxstatic (armv6) node14 win (armv7) node16 macos 最新的 (freebsd)
使用pkg将你的项目编译成可执行文件
在这里,我们应该告诉pkg
我们的资源文件夹。同样,我们可以在package.json
文件中使用脚本和资产配置来做这件事。
让我们到我们的package.json
文件中,添加一个 "pkg",如下图所示。
"name": "executable",
"bin": "index.js",
"version": "1.0.0",
"description": "Simple express app",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
"pkg": {
"assets": [
"views/**/*"
"output": "dist"
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.17.1",
"pkg": "^5.2.1"
注意,我们还添加了 "bin"。这告诉pkg
,应用程序的入口点在哪里。在 "pkg "脚本中,我们添加了 "assets "来显示哪个文件包含我们的资源。
你可以添加更多的内容,只需用逗号将它们分开。我们用 "outputPath "进一步指定了输出的存储位置。
使用下面的命令运行pkg
,以使用我们在package.json
的配置。
pkg .
pkg package.json
在构建文件或特定目标的情况下,你也可以向pkg指定它们,以获得以下格式的输出。
"pkg": {
"scripts": "build/**/*.js",
"assets": "views/**/*",
"targets": [ "node14-linux-arm64" ],
"outputPath": "dist"
文件或目录未包含错误
如果你在pkg中遇到了 "was not included "的错误,那么这就是如何解决这个问题的。在pkg模块中,我们不会使用相对路径的直接串联,例如res.sendFile(__dirname + '/views/index.html');
。
这是因为这不仅是一种不好的编程做法,而且还会返回一个错误,如下图所示。
john@john:~/Tofa/Projects/Convert node project into .exe/Secondtest/express$ ./express
Error: File or directory '/**/express/views/index.html' was not included into executable at compilation stage. Please recompile adding it as asset or script.
at error_ENOENT (pkg/prelude/bootstrap.js:539:17)
at findNativeAddonForStat (pkg/prelude/bootstrap.js:1201:32)
at statFromSnapshot (pkg/prelude/bootstrap.js:1224:25)
at Object.stat (pkg/prelude/bootstrap.js:1250:5)
at SendStream.sendFile (/snapshot/express/node_modules/send/index.js:721:6)
at SendStream.pipe (/snapshot/express/node_modules/send/index.js:595:8)
at sendfile (/snapshot/express/node_modules/express/lib/response.js:1103:8)
at ServerResponse.sendFile (/snapshot/express/node_modules/express/lib/response.js:433:3)
at /snapshot/express/index.js:21:9
at Layer.handle [as handle_request] (/snapshot/express/node_modules/express/lib/router/layer.js:95:5)
这是因为pkg
不会识别这种模式,因此它将无法正确编译项目资源和路径。
不要使用__dirname
,用path.join
或getDir()
取代它,在文件的开始部分使用const path = require('path');
。process.cwd()
总是用于那些在构建时不能使用但在执行时需要的文件。
进一步的实践
检查一下nexe/nexe和vercel/pkg的附加功能,并尝试一下。
你可以尝试在各自的虚拟机中运行其他两个文件,看看结果如何。
只要安装其他两个操作系统,并对它们进行配置。然后,运行这些文件,在选择的网络浏览器上测试结果。
如前所述,你可以把它复制到另一台机器上,其他人不需要做任何软件包安装过程。
由于项目在SDLC过程中需要更多的测试,代码修改是人们不希望遇到的问题。此外,人们更喜欢快速发布项目。我们可以通过将它们编译成Node.js可执行文件来解决这些问题,可以是有资源的,也可以是没有资源的。