2,093

将Node.js应用程序编译成.exe文件

在开发阶段,开发者要执行几个安装和编码的过程来构建和运行一个应用程序。但是,终端用户只是对运行应用程序感兴趣,而不是对底层代码和进程感兴趣。

这就导致需要一个简单的可执行文件,可以在任何操作系统上运行,而不需要用户执行其他步骤来运行该应用程序。

在本教程中,我们将学习如何为我们的Node.js应用程序创建可执行文件。然后,我们将把我们的JavaScript文件编译成一个可执行文件。

在本教程结束时,你将学到以下内容。

  • 什么是 .exe 文件以及它们的重要性。
  • 我们可以用来将你的Node.js应用程序编译成 .exe 文件的包。
  • 创建一个简单的Node.js应用程序。
  • 将Node.js文件编译成各种操作系统的可执行文件。
  • 在操作系统中运行可执行文件。
  • 要想快速跟上本教程,你至少需要具备以下条件。

  • 基本的网络开发技能。
  • 一些JavaScript编码技能。
  • 在你的机器上安装Node.js。
  • 一个稳定的网络连接。
  • 如果你对上述要求都准备好了,让我们看看一些软件包来实现我们的目标。

    什么是可执行文件(.exe)?

    可执行文件(.exe文件)。这些文件包含一组编码指令,一旦用户点击该文件或运行该文件就会按顺序执行。

    .exe 是Windows的一个扩展名,指的是可执行文件。其他操作系统也有可执行文件,但扩展名各不相同。例如,在Linux或基于Unix的操作系统中,以 、 或 为扩展名的文件是可执行文件,而在MacOS中,它们 .bin .elf [none] 没有 扩展名。

    我们将专注于创建Windows可执行文件,并在接下来的过程中触及一些其他操作系统。

    可执行文件的优点

    让我们看看为什么用户首先喜欢可执行文件。

    可执行文件的一些优点包括。

    快速的无代码执行 :只需要点击一下就能运行文件,仅此而已。不需要在终端上运行命令来执行程序。

    防止不需要的代码改变 :它可以防止由于计划外和意外地修改源代码而产生的不需要的错误。

    支持分发 :由于与硬件无关,用户不需要安装任何软件包或依赖性。这使得人们可以在任何机器上运行代码的多个实例,而没有进一步的要求。

    使用的软件包

    两个最常用的用于将JavaScript文件编译成可执行文件的软件包是。

  • nexe :它是一个简单的命令行工具,将你的Node.js应用程序编译成一个可执行文件。默认情况下,它将其转换为Windows可执行文件。
  • pkg 。它是一个Node.js包,可以将你的Node.js应用转换为多个可执行文件,用于不同的操作系统(一次完成)或单个操作系统。
  • 我们现在将得到一个正在运行的简单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版本平台架构
    node8alpinex64
    node10linuxarm64
    node12linuxstatic(armv6)
    node14win(armv7)
    node16macos
    最新的(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.joingetDir() 取代它,在文件的开始部分使用const path = require('path');process.cwd() 总是用于那些在构建时不能使用但在执行时需要的文件。

    进一步的实践

  • 检查一下nexe/nexevercel/pkg的附加功能,并尝试一下。
  • 你可以尝试在各自的虚拟机中运行其他两个文件,看看结果如何。
  • 只要安装其他两个操作系统,并对它们进行配置。然后,运行这些文件,在选择的网络浏览器上测试结果。

    如前所述,你可以把它复制到另一台机器上,其他人不需要做任何软件包安装过程。

    由于项目在SDLC过程中需要更多的测试,代码修改是人们不希望遇到的问题。此外,人们更喜欢快速发布项目。我们可以通过将它们编译成Node.js可执行文件来解决这些问题,可以是有资源的,也可以是没有资源的。