发布于 2026-01-06 0 阅读
0

使用 Vite 开发和构建 Node.js 应用程序

使用 Vite 开发和构建 Node.js 应用程序

语境

作为一款现代化的 Web 应用构建工具,Vite 已被众多开发者用于创建 Web 应用(例如 React 和 Vue)。凭借其易用性和高性能,许多 Web 框架甚至为其编写了官方插件(例如 Solid 和 Astro),使其近年来成功挑战了 Webpack 的地位。然而,Vite 的功能已远不止于 Web 层工具。其周边生态系统蓬勃发展,催生了一系列外围工具的开发。

动机

为什么Vite适合开发Node.js应用程序?

首先,即使不使用 Vite,也可能需要 vitest(用于单元测试)、tsx/ts-node(用于运行源代码进行调试)以及 tsup/esbuild(用于将代码打包成最终可执行的 js 文件)等工具。因此,如果使用 Vite,这些任务都可以在同一个生态系统中完成。

  • vitest:一款支持 ESM 和 TypeScript 的单元测试工具。
  • vite-node:一个用于运行 TypeScript 代码的工具,支持各种 Vite 功能,例如?raw
  • Vite:将 Node.js 应用程序打包成最终要执行的 JavaScript 文件,并可以选择选择性地打包依赖项。

图片描述

用法

Vitest 和 vite-node 开箱即用,因此本文重点介绍 Vite 的构建方面。

首先,安装依赖项。



pnpm i -D vite vite-node vitest


Enter fullscreen mode Exit fullscreen mode

维特斯

创建一个单元测试文件,例如:src/__tests__/index.test.ts



import { it } from 'vitest'

it('hello world', () => {
  expect(1 + 1).eq(2)
})


Enter fullscreen mode Exit fullscreen mode

使用以下命令运行 vitest



pnpm vitest src/__tests__/index.test.ts


Enter fullscreen mode Exit fullscreen mode

维特节点

它可以替代 node 命令来运行任何文件,并且比标准的 node 命令提供更多功能,包括:

  • 支持 ts/tsx 文件和运行 esm/cjs 模块。
  • ESM 中的 CJS polyfill,允许直接使用__dirname,等等。
  • 支持监视模式执行。
  • 支持 Vite 自身的功能,例如?raw
  • 支持使用 Vite 插件。

例如,创建一个文件src/main.ts



import { readFile } from 'fs/promises'

console.log(await readFile(__filename, 'utf-8'))


Enter fullscreen mode Exit fullscreen mode

然后使用 vite-node 运行它



pnpm vite-node src/main.ts


Enter fullscreen mode Exit fullscreen mode

维特

要使用 Vite 构建 Node.js 应用程序,需要修改一些配置和插件来解决一些关键问题:

  1. 实现 ESM 代码的 CJS polyfill,包括__dirname__filenamerequireself
  2. 正确打包,devDependencies同时排除nodedependencies
  3. 提供开箱即用的默认配置。

让我们逐一解决这些问题。

在构建过程中填充 CJS 功能

__dirname在 Node.js 中,经常使用全局变量。遗憾的是, ESM 不支持这些变量。Node.js 中推荐的做法如下:



import path from 'path'
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)


Enter fullscreen mode Exit fullscreen mode

您可以使用 Vite 插件在已构建代码的开头添加一些额外的代码,从而自动创建这些变量。

以下是具体实现方法:

  1. 安装此magic-string程序可在保持源映射不变的情况下修改代码。


   pnpm i -D magic-string


Enter fullscreen mode Exit fullscreen mode
  1. 然后,在钩子函数中添加 polyfill 代码renderChunk


   import MagicString from 'magic-string'
   import { Plugin } from 'vite'

   function shims(): Plugin {
     return {
       name: 'node-shims',
       renderChunk(code, chunk) {
         if (!chunk.fileName.endsWith('.js')) {
           return null;
         }
         const s = new MagicString(code);
         s.prepend(`
   import __path from 'path';
   import { fileURLToPath as __fileURLToPath } from 'url';
   import { createRequire as __createRequire } from 'module';

   const __getFilename = () => __fileURLToPath(import.meta.url);
   const __getDirname = () => __path.dirname(__getFilename());
   const __dirname = __getDirname();
   const __filename = __getFilename();
   const self = globalThis;
   const require = __createRequire(import.meta.url);
   `);
         return {
           code: s.toString(),
           map: s.generateMap({ hires: true }),
         };
       },
       apply: 'build',
     };
   }


Enter fullscreen mode Exit fullscreen mode

通过将此插件集成到您的 Vite 配置中,您将在构建过程中自动将这些 shim 注入到您的 JavaScript 文件中,从而在 ESM 环境中模拟 Node.js 的 CommonJS 环境。此解决方案简化了现有 Node.js 代码库向 ESM 模块系统的适配过程。

正确打包依赖项

在 Node.js 应用中,通常会包含诸如 `<module>` 之类的模块fs,而 Vite 默认也会尝试打包这些模块。因此,必须将它们视为外部依赖项。幸运的是,已经有一个名为rollup-plugin-node-externals的插件可以排除 Node.js 以及在 `<dependencies>`dependencies字段中声明的依赖项package.json。但是,为了使其与 Vite 无缝协作,还需要进行一些小的兼容性调整。

  1. 安装依赖项:


   pnpm i -D rollup-plugin-node-externals


Enter fullscreen mode Exit fullscreen mode
  1. 将其封装以使其与 Vite 兼容:


   import { nodeExternals } from 'rollup-plugin-node-externals'
   import { Plugin } from 'vite'

   function externals(): Plugin {
     return {
       ...nodeExternals({
         // Options here if needed
       }),
       name: 'node-externals',
       enforce: 'pre', // The key is to run it before Vite's default dependency resolution plugin
       apply: 'build',
     }
   }


Enter fullscreen mode Exit fullscreen mode

添加默认配置

鉴于 Node.js 项目的频繁出现,最好不要为每个项目重复配置。因此,我们采用约定优于配置的方法,并辅以自定义选项。由此,可以按如下方式创建用于通用配置的共享 Vite 插件的简单实现:



import path from 'path'
import { Plugin } from 'vite'

function config(options?: { entry?: string }): Plugin {
  const entry = options?.entry ?? 'src/main.ts'
  return {
    name: 'node-config',
    config() {
      return {
        build: {
          lib: {
            entry: path.resolve(entry),
            formats: ['es'],
            fileName: (format) => `${path.basename(entry, path.extname(entry))}.${format}.js`,
          },
          rollupOptions: {
            external: ['dependencies-to-exclude']
            // Additional Rollup options here
          },
        },
        resolve: {
          // Change default resolution to node rather than browser
          mainFields: ['module', 'jsnext:main', 'jsnext'],
          conditions: ['node'],
        },
      }
    },
    apply: 'build',
  }
}


Enter fullscreen mode Exit fullscreen mode

此设置提供专为 Node.js 项目量身定制的默认配置,从而简化开发流程。通过指定入口文件以及模块解析和构建输出的必要调整,此插件可为各种 Node.js 应用程序提供简便的构建设置。

插件组合

最后,我们将这些插件合并为一个,创建一个新的插件设置:



import { Plugin } from 'vite'

export function node(): Plugin[] {
  return [shims(), externals(), config()]
}


Enter fullscreen mode Exit fullscreen mode

vite.config.ts然后,在你的文件中使用它:



import { defineConfig } from 'vite'
import { node } from './path/to/your/plugin'

export default defineConfig({
  plugins: [node()],
})


Enter fullscreen mode Exit fullscreen mode

现在,您可以使用以下命令通过 Vite 构建 Node.js 应用程序:



pnpm vite build


Enter fullscreen mode Exit fullscreen mode

尽情享受Vite提供的所有服务!

已发布了一个 Vite 插件@liuli-util/vite-plugin-node,开箱即可使用。

局限性

好的,这里仍然存在一些问题,包括:

  • Vite 官方并不支持构建 Node 应用程序,它的主要目标也不是这个。至少可以说,vitest/nuxt 在节点级别上都依赖于 Vite。
  • vite-plugin-node 仍然存在许多问题,例如无法自动进行 polyfill__dirname等。- 实施的。
  • 与esbuild相比,Vite的性能仍然差一个数量级。——目前大多数库的性能问题并不严重,人眼几乎察觉不到。
  • 使用 zx 构建失败,原因是 chalk 配置不正确。——具体来说,未能识别node字段imports。维护者缺乏兴趣表明应该考虑切换到 ansi-colors,正如本问题中所讨论的那样。
  • 使用 koa-bodyparser 构建后出现问题——由于缺乏适当的ESM支持。等待合并此拉取请求

没有哪个选择是完美的,但全力投入 Vite 是经过深思熟虑的决定。

未来目标

  • [x] 支持多个入口点。
  • [x] 类型定义生成。

选择专注于 Vite 意味着要权衡其当前的局限性和潜力,并根据其提供的整体优势做出战略决策,包括其不断增长的生态系统以及简化 Node.js 应用程序开发工作流程的能力。

文章来源:https://dev.to/rxliuli/developing-and-building-nodejs-applications-with-vite-311n