用 webpack-chain 来配置 webpack5
缘由
webpack 虽然不陌生,但实际配置起来,相信大部分人并没有觉得多轻松。以前在使用 umi 框架时,接触了一点 webpack-chain,感觉很不错的样子。最近想试试 webpack5 的模块联邦,于是把项目里的 webpack4 升级成 webpack5,里面就使用了 webpack-chain。配合 typescript 的类型提示,就很爽。
配置
entry
const path = require("path");
const Config = require("webpack-chain");
const config = new Config();
config.entry("index").add(path.join(__dirname, "../src/index.tsx"));
output
config.output
.path(path.join(__dirname, "../dist"))
.pathinfo(true)
.filename('static/js/[name].[contenthash:8].bundle.js')
.chunkFilename('static/js/[name].[contenthash:8].chunk.js')
.publicPath('./')
.globalObject('this');
resolve
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
// 模块查询
config.resolve.modules
.add('node_modules')
.end()
.mainFields.clear()
.add('main')
.add('browser')
.end()
.extensions
.add('.ts')
.add('.tsx')
.add('.js')
.end()
.alias
.set('@/*', paths.appSrc)
.end()
.plugin('tsconfig-path')
.use(TsconfigPathsPlugin, [
{
configFile: path.join(__dirname, "../tsconfig.json"),
},
])
.end();
fallback
webpack4 默认会添加 node 模块的 polyfill,webpack5 就取消 polyfill 了,所以需要自己设置。
config.resolve.set('fallback', {
os: require.resolve('os-browserify'),
path: false,
zlib: false,
stream: require.resolve('stream-browserify'),
fs: false,
tty: require.resolve('tty-browserify'),
memcpy: false,
});
module
图片
config.module
.rule('image')
.test(/\.(bmp|gif|jpg|jpeg|png)$/)
.type('asset')
.parser({
dataUrlCondition: {
maxSize: 8 * 1024,
},
});
字体文件
config.module
.rule('assets')
.test(/\.(ico|woff|woff2|ttf|eot)$/)
.type('asset/resource');
svg
config.module
.rule('svg')
.test(/\.svg$/)
.use('@svgr/webpack')
.loader('@svgr/webpack')
.end()
.use('url-loader')
.loader('url-loader');
js、ts、tsx等
config.module
.rule('babel')
.test(/\.(js|jsx|ts|tsx|mjs|cjs)$/)
.exclude.add(/node_modules/)
.add(/bower_components/)
.end()
.include
.add(path.join(__dirname, "../src"))
.end()
.use('thread-loader')
.loader('thread-loader')
.end()
.use('babel-loader')
.loader('babel-loader')
.options(babelOptions);
less
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
config.module
.rule('less')
.test(/\.less/)
.when(
process.env.NODE_ENV === 'developement',
(c) => c.module.rule('less').use('style-loader').loader('style-loader'),
(c) => c.module.rule('less').use('css-extract').loader(MiniCssExtractPlugin.loader)
)
.end()
.use('css-loader')
.loader('css-loader')
.end()
.use('postcss-loader')
.loader('postcss-loader')
.end()
.use('less-loader')
.loader('less-loader')
.options({
lessOptions: {
modifyVars: require('../theme'),
javascriptEnabled: true,
},
sourceMap: true,
});
这里使用了 when
的语法,根据运行环境的不同,使用不同的 loader 来处理 css
scss
scss 和 less 的配置类似
plugins
const webpack = require("webpack");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
config
.plugin('define-plugin')
.use(webpack.DefinePlugin, [env.stringified])
.end()
.plugin('ignore-plugin')
.use(webpack.IgnorePlugin, [/^\.\/locale$/, /moment$/])
.end()
.plugin('provider-plugin')
.use(webpack.ProvidePlugin, [
{
Buffer: ['buffer', 'Buffer'],
},
])
.end()
.when(process.env.NODE_ENV === 'production', (c) => c.config.plugin('css-extract-plugin').use(MiniCssExtractPlugin, [
{
filename: 'static/css/[name].css',
chunkFilename: 'static/css/[name].[contenthash:8].css',
},
]))
DefinePlugin
常见用法
config.plugin('define-plugin').use(webpack.DefinePlugin, [{
"process.env.API_URL": JSON.stringify('http://api.xxxx.com')
}])
高级用法
dotenv 可以读取 .env 这类文件的里写的环境变量。
eg: .env.development
# 开发环境接口地址
REACT_APP_API_URL=http://api.xxx.com
eg: .env.js, 读取自定义的环境变量
const path = require("path");
const fs = require("fs");
const dotenv = path.join(__dirname, "../.env");
// 读取.env, .env.xxx, .env.xxx.local 文件,读取环境变量
var dotenvFiles = [
`${dotenv}.${NODE_ENV}.local`,
`${dotenv}.${NODE_ENV}`,
NODE_ENV !== 'test' && `${dotenv}.local`,
dotenv,
].filter(Boolean);
dotenvFiles.forEach((dotenvFile) => {
if (fs.existsSync(dotenvFile)) {
require('dotenv').config({
path: dotenvFile,
});
}
});
const REACT_APP = /^REACT_APP_/i;
function getClientEnvironment(publicUrl) {
const raw = Object.keys(process.env)
.filter((key) => REACT_APP.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key];
return env;
},
{
NODE_ENV: process.env.NODE_ENV || 'development',
PUBLIC_URL: publicUrl,
}
);
// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
return { raw, stringified };
}
optimization
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
config.optimization
.splitChunks({
chunks: 'async',
minSize: 20000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: false,
cacheGroups: {
common: {
name: 'common',
test: /[\\/]src[\\/]/,
chunks: 'initial',
minChunks: 2, //一般为非第三方公共模块
priority: -20,
reuseExistingChunk: true,
},
},
})
.runtimeChunk('multiple')
.minimizer('css-minimizer')
.use(CssMinimizerPlugin) // 压缩css
.end()
.minimizer('teser-plugin')
.use(TerserPlugin) // 压缩js
.end()
.concatenateModules(true)
.minimize(true);
导出配置
module.exports = config.toConfig();
npm scripts
{
"scripts": {
"start": "webpack serve --config ./config/webpack.config.dev.js --progress",
"build:prod": "cross-env REACT_APP_API_URL=http://api.xxx.com webpack --config ./config/webpack.config.prod.js --progress"
}
}
总结
总的来说,配合 ts 的类型提示,不用一直去翻文档,写起来比较方便。推荐大家可以尝试下。
转载自:https://juejin.cn/post/7006675512290443295