前言
eject 不可逆,想试不 eject 情况下改 webpack 配置。
主要介绍的是配置 less-loader 遇到的坑。
正文
1. react-app-rewired + customize-cra
一开始按照 customize-cra - api.md 在 config-overrides.js 中配置 less-loader,然后报错 options has an unknown property 'plugins'. These properties are valid: object { postcssOptions?, execute?, sourceMap?, implementation? };
原因:postcss-loader 版本原因,有新的配置写法。
源码 customize-cra 1.0.0 addLessLoader 中
// addLessLoader - getStyleLoaders
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
postcssNormalize(),
],
sourceMap: isEnvProduction && shouldUseSourceMap,
},
}
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'postcss-preset-env',
{
// Options
},
],
],
},
},
}
不难看出少了 postcssOptions。当然,在报错信息中是有提及到 postcssOptions,但是一开始只关注到 options has an unknown property 'plugins' ,后面是试了源码才知道的。
解决方案:
adjustStyleLoaders(({ use: [, , postcss] }) => {
const postcssOptions = postcss.options;
postcss.options = { postcssOptions };
})
试过可以解决,但发现部分 options 已包含 postcssOptions,如果使用可以自己做下判断兼容。
// config-overrides.js
config.module.rules.forEach(item => {
if (item.oneOf) {
item.oneOf.forEach(item => {
item.use?.forEach(item => {
if (
item.loader?.includes('postcss-loader') &&
!item?.options?.postcssOptions
) {
const postcssOptions = item.options;
item.options = { postcssOptions };
}
});
});
}
})
module.exports = {
webpack: override(
addWebpackModuleRule({
test: [/\.css$/, /\.less$/],
use: ['style-loader', 'css-loader', 'postcss-loader', { loader: 'less-loader' }]
})
)
}
本地包版本:
css-loader "^6.5.1"
mini-css-extract-plugin "^2.4.5"
postcss "^8.4.4"
postcss-flexbugs-fixes "^5.0.2"
postcss-loader "^6.2.1"
postcss-normalize "^10.0.1"
postcss-preset-env "^7.0.1"
style-loader "^3.3.1"
// 自个安装
less-loader@^12.2.0
less@^4.2.0
值得注意的是:用 config-overrides.js 覆写 webpack.config.js 配置,在反复测验解决方案时,已经解决了的配置代码注释掉再启动,这时是不会复现已解决的问题的,是因为 webpack 缓存 的原因,手动删除缓存文件 node_modules/.cache 就可以了。
最后贴个配置:
// config-overrides.js
const {
addLessLoader,
override,
addWebpackAlias,
adjustStyleLoaders,
} = require('customize-cra');
const path = require('path');
module.exports = {
webpack: override(
addWebpackAlias({
['@']: path.resolve(__dirname, 'src'),
}),
addLessLoader({
lessOptions: {
javascriptEnabled: true,
},
additionalData: `@import './src/assets/css/values.less';`,
}),
adjustStyleLoaders(({ use: [, , postcss] }) => {
if (!postcss.options.postcssOptions) {
const postcssOptions = postcss.options;
postcss.options = { postcssOptions };
}
})
),
devServer: overrideDevServer((config) => ({
...config,
compress: true,
proxy: {
'/api': {
target: 'http://localhost:3000', // 服务器地址
changeOrigin: true,
pathRewrite: {
'^/api': '',
},
},
},
})),
};
2. @crao/craco
参照 craco.js 文档,在根目录创建 craco.config.js。
设置别名:
const path = require('path');
module.exports = {
webpack: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
};
或者
const path = require('path');
module.exports = {
webpack: {
configure: {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
},
},
};
配置 less-loader 时发现官方的 style 中并没有提及 less。然后尝试用官方提供的 webpack.configure 可以写 module.rules,也就是
// 无效
module.exports = {
webpack: {
configure: {
module: {
rules: [
{
test: [/\.less$/i],
use: ['style-loader', 'css-loader', 'less-loader'],
},
],
},
},
},
};
但是无效,原因是 craco 本身对 webpack 配置的 modules.rules 中写了一个 oneOf,属性值是数组,此规则将对所有文件进行解析,末尾数组项是
而在 craco.config.js 中续写了对 less 文件的解析规则排在上面规则之后,导致 less 文件解析无法使用正确的 loader。
解决方案:
① 将规则提前
module.exports = {
plugins: [
{
plugin: {
overrideWebpackConfig: ({ webpackConfig }) => {
const { module } = webpackConfig;
const index = module.rules.findIndex((i) => i.oneOf);
if (index !== -1) {
module.rules[index].oneOf.unshift({
test: [/\.less$/i],
use: ['style-loader', 'css-loader', 'less-loader'],
});
}
return webpackConfig;
},
},
},
],
};
const CracoLessPlugin = require('craco-less');
module.exports = {
plugins: [
{
plugin: CracoLessPlugin,
},
],
};
最后贴个:
// craco.config.js
const CracoLessPlugin = require('craco-less');
const path = require('path');
module.exports = {
webpack: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
plugins: [
{
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
additionalData: `@import './src/assets/css/values.less';`,
},
},
},
],
devServer: {
proxy: [
{
context: ['/api'],
target: 'http://localhost:3000', // 服务器地址
pathRewrite: { '^/api': '' },
},
],
},
};
注:如有侵权,告知即删。如有错漏之处,敬请指正。