Electron+Webpack+WebTorrent打包后出现绝对路径致程序无法跨设备运行
问题
使用Electron Forge + Webpack开发应用,引入WebTorrent后打包产物只能在本地运行,其他设备启动失败,原因是打包后的代码中出现了本地绝对路径:
(0,n.createRequire)("file:///Users/prof1/projects/pw_castle/launcher_test/node_modules/node-datachannel/lib/index.js")("../build/Release/node_datachannel.node")
项目创建步骤:
- 初始化项目并安装依赖:
npm init electron-app@latest my-app -- --template=webpack yarn add webtorrent
- 在主进程
main.js中导入WebTorrent:
import WebTorrent from "webtorrent";
- 执行打包命令:
yarn package
移除WebTorrent导入后,打包产物中的绝对路径消失,其他依赖正常工作。当前使用的默认配置文件如下:
webpack.main.config.js
module.exports = { entry: './src/main.js', module: { rules: require('./webpack.rules'), }, };
webpack.rules.js
module.exports = [ { test: /native_modules[/\\].+\.node$/, use: 'node-loader', }, { test: /[/\\]node_modules[/\\].+\.(m?js|node)$/, parser: { amd: false }, use: { loader: '@vercel/webpack-asset-relocator-loader', options: { outputAssetBase: 'native_modules', }, }, }, ];
forge.config.js
const { FusesPlugin } = require('@electron-forge/plugin-fuses'); const { FuseV1Options, FuseVersion } = require('@electron/fuses'); module.exports = { packagerConfig: { asar: true, }, rebuildConfig: {}, makers: [ { name: '@electron-forge/maker-squirrel', config: {}, }, { name: '@electron-forge/maker-zip', platforms: ['darwin'], }, { name: '@electron-forge/maker-deb', config: {}, }, { name: '@electron-forge/maker-rpm', config: {}, }, ], plugins: [ { name: '@electron-forge/plugin-auto-unpack-natives', config: {}, }, { name: '@electron-forge/plugin-webpack', config: { mainConfig: './webpack.main.config.js', renderer: { config: './webpack.renderer.config.js', entryPoints: [ { html: './src/index.html', js: './src/renderer.js', name: 'main_window', preload: { js: './src/preload.js', }, }, ], }, }, }, new FusesPlugin({ version: FuseVersion.V1, [FuseV1Options.RunAsNode]: false, [FuseV1Options.EnableCookieEncryption]: true, [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, [FuseV1Options.EnableNodeCliInspectArguments]: false, [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true, [FuseV1Options.OnlyLoadAppFromAsar]: true, }), ], };
原因分析
WebTorrent依赖的node-datachannel是原生Node.js模块,当前Webpack配置中的@vercel/webpack-asset-relocator-loader没有正确处理它的路径解析逻辑。该模块内部通过相对路径加载.node二进制文件,但asset-relocator-loader在打包时未将路径转换为asar包内的正确路径,反而保留了本地开发环境的绝对路径,导致其他设备无法找到对应二进制文件。
同时,虽然启用了@electron-forge/plugin-auto-unpack-natives插件,但由于Webpack的路径处理错误,插件无法正确识别并提取这个原生模块到asar包外的可访问目录。
解决方案
1. 修改Webpack规则,单独处理node-datachannel
修改webpack.rules.js,添加规则对node-datachannel的.node文件使用node-loader处理,同时排除它被asset-relocator-loader错误处理:
module.exports = [ { test: /native_modules[/\\].+\.node$/, use: 'node-loader', }, // 新增:单独处理node-datachannel的原生模块 { test: /node-datachannel[/\\]build[/\\]Release[/\\]node_datachannel.node$/, use: 'node-loader', }, { test: /[/\\]node_modules[/\\].+\.(m?js|node)$/, // 排除node-datachannel,避免被asset-relocator-loader错误处理 exclude: /node-datachannel/, parser: { amd: false }, use: { loader: '@vercel/webpack-asset-relocator-loader', options: { outputAssetBase: 'native_modules', }, }, }, ];
2. 配置asarUnpack确保原生模块被解压
在forge.config.js的packagerConfig中,明确指定需要解压的原生模块路径,确保node-datachannel的二进制文件被提取到asar包外:
packagerConfig: { asar: true, asarUnpack: [ '**/node-datachannel/build/Release/node_datachannel.node' ] },
3. 重新打包测试
执行以下命令重新构建并打包:
yarn rebuild yarn package
处理后,Webpack会正确将node_datachannel.node打包到产物中并转换为相对路径,Auto-Unpack插件会把它从asar包中提取出来,其他设备启动应用时就能正常加载该原生模块。
内容的提问来源于stack exchange,提问作者Inventor

