
webpack 5 如何在 Node 应用中打包 .node 原生模块并用 dlopen 动态加载【免费下载链接】webpackA bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the application on demand. Through loaders, modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff.项目地址: https://gitcode.com/GitHub_Trending/web/webpack用 webpack 给 Node.js 应用做打包时.node原生模块和普通 JS 模块不一样它不能被合并进 JS bundlewebpack 5 的处理方式是把它当作资源asset随 bundle 一起复制到输出目录然后在运行时用process.dlopen动态加载。webpack 仓库的nodejs-addons示例完整演示了这条路径本文基于该示例examples/nodejs-addons/README.md说明配置、加载代码和验证方式。本仓库为 webpack 5package.json 中版本 5.110.3目标是node环境。示例文件及其作用nodejs-addons 目录下的文件分工如下文件作用example.js入口代码用dlopen加载./file.node调用原生方法webpack.config.js构建配置target: node.node按asset/resource处理file.cc原生 addon 的 C 源码注册hello方法binding.gyp构建描述target_name为filesources为file.ccfile.node已编译好的 addon 产物仓库已直接提供file.cc 中的InitModule通过NODE_SET_METHOD(exports, hello, Method)注册了hello方法Method返回字符串world它按 binding.gyp 的描述编译为.node。仓库中已带有编译好的file.node所以可以直接进入 webpack 打包环节无需自己重新编译。配置 webpack 打包 .node 文件webpack.config.js 的完整内容use strict; /** type {import(webpack).Configuration} */ const config { // mode: development || production, target: node, output: { // We strong recommend use publicPath: auto or do not set publicPath at all to generate relative URLs // publicPath: auto }, module: { rules: [ { test: /\.node$/, type: asset/resource } ] } }; module.exports config;三个关键配置点target: node本场景必须项声明构建产物运行在 Node 环境。output.publicPath配置中的注释强烈建议使用publicPath: auto或不设置publicPath以生成相对 URL。运行时dlopen需要基于 bundle 文件所在位置去解析.node的相对地址相对 URL 才能保证可解析。module.rules中的{ test: /\.node$/, type: asset/resource }让 webpack 不再把.node当 JS 解析而是作为资源原样复制到输出目录输出文件名带内容 hash。运行时用 dlopen 加载 addon入口代码 example.js 的完整内容import { dlopen } from node:process; import { fileURLToPath } from node:url; const file new URL(./file.node, import.meta.url); const myModule { exports: {} }; try { dlopen(myModule, fileURLToPath(file)); } catch (err) { console.log(err) // Handling errors } console.log(myModule.exports.hello()); // Outputs: worlddlopen从node:process导入fileURLToPath从node:url导入把 URL 对象转成文件系统路径。new URL(./file.node, import.meta.url)是这套方案的核心写法。由于输出后的.node文件名带内容 hash文档示例中为5664f09ab8adf033e173.nodebundle 里不能再引用源码中的file.node原名。webpack 会识别这个new URL请求并把它替换为实际输出的资源地址所以源码里写源文件名、运行时拿到的却是 hash 后的真实路径。这也是配置注释推荐相对publicPath的原因。dlopen(myModule, fileURLToPath(file))成功后addon 的导出会写入myModule.exports示例随后调用myModule.exports.hello()。加载失败的处理方式如示例所示try/catch捕获并打印错误具体错误信息需要按运行时的实际输出排查。执行打包仓库 examples/README.md 在 “Building an Example” 一节给出了构建示例的通用步骤在项目根目录依次执行yarn、yarn setup、yarn add --dev webpack-cli再进入具体示例目录构建。不过nodejs-addons目录没有包含build.js脚本该步骤不直接适用于本示例根目录的npm run build:examples由 examples/buildAll.js 驱动会在每个示例目录依次执行node build.js是面向全部示例的批量入口同样不适合作为单任务构建路径。仓库内实际用于编译各示例的是测试管线 test/Examples.test.js它读取每个示例目录的webpack.config.js作为 options未设置entry时默认使用./example.js输出目录为dist/publicPath设为dist/。在自己的项目里可以用同样的 webpack Node API 方式构建以下脚本按该测试管线的做法整理// build.js放在示例目录下./webpack.config.js 即上一节的配置 use strict; const path require(path); const webpack require(webpack); const options require(./webpack.config.js); options.context __dirname; options.output options.output || {}; options.output.path path.join(__dirname, dist); options.output.publicPath dist/; if (!options.entry) options.entry ./example.js; webpack(options, (err, stats) { if (err) { throw err; } if (stats.hasErrors()) { console.log(stats.toString({ all: false, errors: true, errorDetails: true, errorStacks: true })); return; } console.log(compiled); });在示例目录执行node build.js验证结果编译产物nodejs-addons/README.md 记录了两种模式下编译完成的 stdout 输出以下为文档示例具体大小和 hash 值以你本地的实际输出为准。Unoptimized未压缩asset 5664f09ab8adf033e173.node 16.5 KiB [emitted] [immutable] [from: file.node] (auxiliary name: main) asset output.js 5.75 KiB [emitted] (name: main) chunk (runtime: main) output.js (main) 457 bytes (javascript) 16.5 KiB (asset) 1.12 KiB (runtime) [entry] [rendered] ./example.js main runtime modules 1.12 KiB 6 modules dependent modules 16.5 KiB (asset) 126 bytes (javascript) [dependent] 3 modules ./example.js 331 bytes [built] [code generated] [no exports] [used exports unknown] entry ./example.js main webpack X.X.X compiled successfullyProduction modeasset 5664f09ab8adf033e173.node 16.5 KiB [emitted] [immutable] [from: file.node] (auxiliary name: main) asset output.js 463 bytes [emitted] [minimized] (name: main) chunk (runtime: main) output.js (main) 457 bytes (javascript) 16.5 KiB (asset) 264 bytes (runtime) [entry] [rendered] ./example.js main runtime modules 264 bytes 2 modules dependent modules 16.5 KiB (asset) 42 bytes (javascript) [dependent] 1 module ./example.js 2 modules 415 bytes [not cacheable] [built] [code generated] [no exports] [no exports used] entry ./example.js main webpack X.X.X compiled successfully判断要点.node以独立 asset 的形式[emitted]标记[from: file.node]表明来源同时有output.js输出最后一行为webpack X.X.X compiled successfully。运行时用 Node 运行产物output.js示例最后一行打印myModule.exports.hello()的返回值example.js 中的注释说明输出为world编译输出与运行时打印都符合上述预期说明.node被正确复制、且dlopen加载成功。限制与注意事项target: node是本场景的必要配置缺失时不会按 Node 环境处理产物。输出的.node文件名是内容 hash运行时的加载地址必须来自 webpack 替换后的资源地址即源码中的new URL写法不要硬编码file.node去访问输出目录。仓库提供的是预编译好的file.node二进制产物若要换成自己的原生 addon需要自行准备好对应的.node文件。README 只链接了 Node.js 官方 addons 文档来说明 addon 本身未给出重新编译命令。dlopen加载失败时示例的处理方式是try/catch打印错误README 未列举具体错误信息加载失败时以实际打印的错误为准。本示例的配置未显式写entry仓库的示例管线默认取./example.js自行构建时建议显式指定entry避免歧义。完成以上步骤后你可以检查两个结果编译统计中.node以独立资源形式输出且编译成功运行时 bundle 打印出 addon 方法的返回值world。【免费下载链接】webpackA bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the application on demand. Through loaders, modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff.项目地址: https://gitcode.com/GitHub_Trending/web/webpack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考