设想
vite作为现代Web应用程序的构建工具,许多开发人员用于Web应用程序开发(React,Vue)。由于其易用性和高性能,许多网络框架甚至官方插件(Solid,Astro)已为其编写,这使其成为近年来WebPack的成功挑战者。但是,迄今为止,Vite的发展远远超出了Web层工具。它的周围生态系统正在蓬勃发展,产生了一系列外围工具。如上一篇Vite - More Than Just a Build Tool所述,本文专门说明了如何开发Node.js应用程序。
动机
为什么Vite适合开发Node.js应用程序?
首先,即使不使用Vite,您可能需要使用vitest等工具进行单元测试,运行源代码使用TSX/TS节点调试,然后将代码捆绑到最终的JS中,以使用TSUP/ESBUILD运行。因此,如果您使用Vite,所有这些任务将在同一生态系统中完成。
- vitest:单元测试工具,支持ESM,支持TS
- vite节点:一种用于运行TS代码的工具,支持Vite的各种功能,例如
?raw
- vite:将node.js应用程序包装到要运行的最终js中,并且可以选择捆绑依赖项
用法
Vitest和Vite节点都可以开箱即用,因此这里的重点是与Vite构建相关的问题。
首先,安装依赖项
pnpm i -D vite vite-node vitest
齿轮
创建一个单元测试文件,例如src/__tests__/index.test.ts
import { it } from 'vitest'
it('hello world', () => {
expect(1 + 1).eq(2)
})
使用以下命令运行vitest
pnpm vitest src/__tests__/index.test.ts
知识节点
您可以说可以使用它替换Node命令运行任何文件。它比节点命令更强大,包括
- 支持TS/TSX/ESM
- 在ESM,
__dirname
等中具有CJS Polyfill,可以直接使用 - 支持在手表模式下运行
- 支持Vite自己的功能,例如
?raw
- 支持使用Vite插件
例如,创建一个src/main.ts
文件
import { readFile } from 'fs/promises'
console.log(await readFile(__filename, 'utf-8'))
然后用vite节点运行
pnpm vite-node src/main.ts
快速地
要构建使用vite的node.js应用程序,确实需要一些配置和插件修改来解决一些问题:
- 实施CJS Polyfill用于ESM代码,包括
__dirname/__filename/require/self
- 正确捆绑dev依赖性依赖性依赖性,但排除节点/依赖关系依赖关系
- 提供开箱即用的默认配置
然后,我们一个一个
一个人解决这些问题CJS功能的构建时间多填充
安装魔术弦,用于修改代码的同时维护Sourcemap
pnpm i -D magic-string
然后在renderchunk挂钩中添加polyfill代码
import MagicString from 'magic-string'
function sh
ims(): Plugin {
return {
name: 'node-shims',
renderChunk(code, chunk) {
if (!chunk.fileName.endsWith('.js')) {
return
}
// console.log('transform', chunk.fileName)
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(),
}
},
apply: 'build',
}
}
正确捆绑依赖
在这里,为了简化,我们使用现有的croul-plugin节点插件。它可以排除节点依赖项,并会根据package.json中的依赖关系和dev依赖关系自动排除。但是,需要对VITE进行一些较小的兼容性修改。
安装依赖项
pnpm i -D rollup-plugin-node-externals
用简单的代理包裹
import { nodeExternals } from 'rollup-plugin-node-externals'
function externals(): Plugin {
return {
...nodeExternals(),
name: 'node-externals',
enforce: 'pre',
apply: 'build',
}
}
添加默认配置
由于我们有很多项目,因此我们不想每次填写配置。相反,我们通过惯例 +支持配置解决此问题。因此,我们只是实现了Vite插件。
import path from 'path'
function config(options?: { entry?: string }): Plugin {
const entry = options?.entry ?? 'src/main.ts'
return {
name: 'node-config',
config() {
return {
build: {
lib: {
entry,
formats: ['es'],
fileName: path.basename(entry, path.extname(entry)),
},
},
}
},
apply: 'build',
}
}
组合插件
最后,我们将这些插件结合在一起,我们可以使用vite构建node.js应用程序。
export function node(): Plugin[] {
return [shims(), externals(), config()]
}
然后在vite.config.ts
中使用它
export default defineConfig({
plugins: [node()],
})
现在,我们可以使用vite构建node.js应用程序
pnpm vite build
享受Vite带来的一切!
我发布了一个vite插件@liuli-util/vite-plugin-node,该插件解决了上述问题。
限制
好吧,这里还有一些问题,包括
- vite不正式支持building node.js应用程序,它不是项目的主要目标
- vite-plugin节点(NPM现有软件包)仍然有许多问题,例如不自动多利填充
__dirname
等 - Vite的表现仍然比Esbuild差的数量级
别无选择,但我现在选择相信Vite。
遇到的问题
- ZX在构建后无法运行 - 粉笔未正确配置 - 无法识别
imports
中的node
字段 - 维护者不在乎,考虑切换到ANSI-COLORS,REF
:https://github.com/chalk/chalk/issues/535
- 构建后,KOA-Bodyparser存在问题 - 无法正确支持ESM - 等待https://github.com/koajs/bodyparser/pull/152合并