之前写过
webpack
打包web
、node
、electron
的文章,最后只写了打包web
;因为当时觉得
node
和electron
没必要打包,但是总有一些需求还是要打包的,例如代码混淆、压缩等等,所以这次就来写一下node
的打包。
网上很多关于webpack
打包node
的文章,但是他们都是只打包你写的node.js
的代码,node_modules
直接都被排除了,这样处理的话如果发布到线上,还是要将node_modules
一起发布,这样就很麻烦了,所以这次我就来写一下如何将node_modules
也打包进去。
1. 网上的文章的操作
网上查到的资料,大多数都是这样的操作:
const path = require('path'); const webpack = require('webpack'); const nodeExternals = require('webpack-node-externals'); module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'index.js', }, target: 'node', externals: [nodeExternals()], };
就这么一丢丢代码,而且 100% 不会出问题,因为他使用了webpack-node-externals
这个库,这个库的作用就是将node_modules
排除掉,所以打包出来的文件就不会包含node_modules
里面的代码了。
然后你发布到线上,必须要将node_modules
一起发布,这样感觉打不打包区别不大,所以这种方式就没什么意义了。
webpack-node-externals
这个库的作用就是将package.json
里面的dependencies
和
devDependencies
里面的包排除掉,所以打包出来的文件就不会包含node_modules
里面的代码了。
externals
这个配置项的作用就是排除掉一些包,不打包进去,这样打包出来的文件就会小一些。
2. 打包node_modules
通过上面的解释的意思,我们只需要将webpack-node-externals
这个库去掉,然后将node_modules
也打包进去就可以了。
const path = require('path'); const webpack = require('webpack'); module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'index.js', }, target: 'node', };
是不是感觉配置很简单,然后执行npx webpack
,然后等待打包完成,然后你就会发现,node_modules
也被打包进去了,这样就可以直接发布到线上了。
3. node-gyp
的排除
当然上面的方案还有其他的问题,例如node-gyp
这种包,他是需要编译的,所以直接打包进去是不行的,所以我们需要将node-gyp
这种包排除掉。
module.exports = { // ... externals: [ function (context, request, callback) { if (/^node-gyp$/.test(request)) { return callback(null, 'commonjs ' + request); } callback(); }, ], };
这样就可以将node-gyp
排除掉了,然后你再次执行npx webpack
,然后你就会发现,node-gyp
这种包就不会被打包进去了。
除了node-gyp
这种包,还有其他的包也需要排除,这个就要看你的项目了,如果你的项目里面有其他的包需要排除,那么你就需要自己去排除了。
module.exports = { // ... externals: [ function (context, request, callback) { if (/^node-gyp$/.test(request)) { return callback(null, 'commonjs ' + request); } callback(); }, { // 可以手动排除 'package-name': 'commonjs package-name', } ], };
4. node-gyp
的打包
当然我们的代码依赖了这些包,那么我们最终打包的时候排除了,那么我们的代码就会报错,所以我们需要将这些包编译出来,然后再打包进去。
当然node-gyp
这种包是不能直接通过webpack
打包的,我们可以直接通过copy-webpack-plugin
这个插件将这些包拷贝到dist
目录下面。
const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { // ... plugins: [ new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, 'node_modules/package-name'), to: path.resolve(__dirname, 'dist/package-name'), }, ], }), ], };
这样就可以将node-gyp
这种包拷贝到dist
目录下面了,然后我们再次执行npx webpack
,然后你就会发现,node-gyp
这种包就被拷贝到dist
目录下面了。
不过这样的话我们生产环境下的代码就需要将package.json
也发布到线上了,因为我们的代码写的是包名,而不是包的路径,解决这个问题我们可以修改externals
配置。
module.exports = { // ... externals: [ { // 这里的 package-name 就是你的包名,index.js 就是你的包的入口文件,通常情况下会写到 package.json 的 main 字段里面 'package-name': 'commonjs ./package-name/index.js', }, ], };
这样就可以将node-gyp
这种包的路径指向dist
目录下面了,然后我们再次执行npx webpack
,然后你就会发现,node-gyp
这种包就被指向dist
目录下面了。
通常情况下应该很少会有node-gyp
这种包,但是如果你的项目里面有的话,就需要这样处理了,不过我上面写的问题可能不是很全面,可能你还会遇到其他的问题,不管是什么问题,基本上都是包依赖的问题。
5. 外部配置文件
有时候我们的项目里面会有一些配置文件,比如config.json
,这个文件里面会有一些配置,比如数据库的配置,这个我们喜欢放到外部,这样就可以手动修改,而不是每次都要重新打包。
这个时候我们还是可以通过copy-webpack-plugin
这个插件将这些配置文件拷贝到dist
目录下面。
const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { // ... plugins: [ new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, 'config.json'), to: path.resolve(__dirname, 'dist/config.json'), }, ], }), ], };
但是这样配置只是将这个文件拷贝到dist
目录下面了,代码打包还是将这个文件打包进去了,这个时候有很多解决方案:
5.1. 使用node
的fs
模块
第一种是通过node
的fs
模块来读取这个文件,然后将这个文件的内容作为配置,这样就可以避免将这个文件打包进去了。
const fs = require('fs'); const config = JSON.parse(fs.readFileSync('./config.json', 'utf-8'));
这种方式的好处是不需要修改webpack
的配置,但是缺点是每次都需要读取文件,这样会影响性能。
而且还需要开发环境和生产环境的目录结构一致,不然就会报错。
5.2. 使用webpack
的DefinePlugin
第二种是通过webpack
的DefinePlugin
插件来定义一个全局变量,然后在代码里面通过process.env
来获取这个全局变量,这样就可以避免将这个文件打包进去了。
const { DefinePlugin } = require('webpack'); module.exports = { // ... plugins: [ new DefinePlugin({ 'process.env': { CONFIG: JSON.stringify(require('./config.json')), }, }), ], };
这种方式的好处是不需要修改代码,但是缺点是需要修改webpack
的配置,而且使用方式也不太友好,需要同团队成员约定好。
5.3. 使用webpack
的externals
,将配置文件作为外部依赖
第三种可以通过webpack
的externals
来将这个文件指向dist
目录下面,这样就可以避免将这个文件打包进去了。
module.exports = { // ... externals: [ { './config.json': 'commonjs ./config.json', }, ], };
但是这种方式如果多个文件需要用,那么就需要配置多个externals
,不过externals
也支持正则,或者使用function
来匹配。
module.exports = { // ... externals: [ function (context, request, callback) { if (/config.json$/.test(request)) { return callback(null, 'commonjs ./config.json'); } callback(); }, ], };
这种方式是比较推荐的,因为不需要修改代码,团队成员也不需要约定,只需要修改webpack
的配置就可以了。
6. 总结
上面就是关于webpack
打包配置文件的一些方法,就这,respect!