因为 create-react-app 将 webpack 的配置隐藏起来了,你没办法直接修改这个 webpack config,除非执行 yarn run eject
将原始的 webpack config 暴露出来,但这样就没办法再用 react-scripts 的其它命令了。
react-app-rewired 提供了一种新的选择,可以在 config-overrides.js 这个文件中修改 webpack config。
/* config-overrides.js */
module.exports = function override(config, env) {
// 参数中的 config 就是默认的 webpack config
// 可以用 console.log(config) 查看它的完整内容
// 可以对 config 进行任意修改 (但不见得一定会真正生效)
// 比如:
// 加速 rebuild 速度
config.mode = 'development'
config.devtool = 'eval-cheap-module-source-map'
delete config.optimization
// 修改 publicPath 和输出路径
config.output.publicPath = pkg.homepage
// 输出到 build 目录而不是默认的 dist 目录
config.output.path = paths.appBuild
// 最后一定要把新的 config 返回
return config
}
config-overriders.js 导出的是一个函数,这个函数的签名是 const override = (oldWebpackConfig, env) => newWebpackConfig
。(不过 oldWepbackConfig 和 newWebpackConfig 实际指向同一个对象,因为直接在原来的 webpack config 对象上进行修改)
编译时,react-app-rewired 会先取到 create-react-app 生成的默认的 webpack config,然后调用 override(config)
方法,对 config 进行修改,得到新的 webpack config。webpack 最终会使用这个新的 config 进行打包。
react-app-rewired 原生写法,将对 webpack config 的修改全部写在 override()
一个方法中,不够模块化,customized-cra 则将它变得更模块化,它提供了一些 helper 方法,可以将每一个独立的修改放到单独的函数中,再串行执行这些函数。
示例:
const {
override,
addDecoratorsLegacy,
disableEsLint,
addBundleVisualizer,
addWebpackAlias,
} = require('customize-cra')
const path = require('path')
module.exports = override(
// enable legacy decorators babel plugin
addDecoratorsLegacy(),
// disable eslint in webpack
disableEsLint(),
// add webpack bundle visualizer if BUNDLE_VISUALIZE flag is enabled
process.env.BUNDLE_VISUALIZE == 1 && addBundleVisualizer()
// add an alias for "ag-grid-react" imports
addWebpackAlias({
['@lib']: path.resolve(__dirname, 'src/lib/*'),
}),
// ...
)
新的 override() 方法,它是一个高阶函数,它接受可变数量个参数,每个参数都是签名为 const fn = (oldConfig) => newConfig
的函数。它会返回一个新的函数,这个函数的签名也是 const fn = (oldConfig) => newConfig
。
它会在内部依次调用这些参数函数,把前一个函数返回的 newConfig 作为参数调用后一个函数,得到最终的 webpack config。
大致实现应该是这样的:
function override(fns) {
return function (oriConfig) {
let finalConfig = oriConfig
for (const fn of fns) {
finalConfig = fn(finalConfig)
}
return finalConfig
}
}
由上得知,addWebpackAlias() 得到是一个函数,那 addWebpackAlias 自身应该是一个高阶函数。它接受一些选项参数,然后返回 (config) => config
签名的函数。
下面是 addWebpackAlias 方法的原型:
export const addWebpackAlias = (alias) => (config) => {
if (!config.resolve) {
config.resolve = {}
}
if (!config.resolve.alias) {
config.resolve.alias = {}
}
Object.assign(config.resolve.alias, alias)
return config
}
我们可以自定义一个 log 函数放到 override() 中,观察最终 webpack config 会长成啥样:
const logConfig = () => (config) => {
console.log(config)
}
module.exports = override(
...,
logConfig()
)
当然,由于 logConfig 方法不带参数,上面的代码也可以简化成:
const logConfig = (config) => {
console.log(config)
}
module.exports = override(
...,
logConfig
)
如果同时还要修改 webpackDevServer 的 config,则 config-overrides.js 要这样写:
const {
override,
disableEsLint,
overrideDevServer,
watchAll,
} = require('customize-cra')
module.exports = {
webpack: override(
// usual webpack plugin
disableEsLint()
),
devServer: overrideDevServer(
// dev server plugin
watchAll()
),
}
参考: