在create-react-app
以及eject
之后,为了使用ant design
,需要自行配置less-loader
,根据antd文档,只给出了利用react-app-rewired的解决方案,至于eject
方案,它说:“不过这种配置方式需要你自行探索,不在本文讨论范围内”。所以本文介绍一下作者的探索结果。
当前关于这方面的博客非常多,但大都过时了,本文是2020年3月31日探索的结果。而且发现一些方案不够好,他们可能没有理解create-react-app
里面getStyleLoaders
的本意。
直接上解决方案,再说原因。
注意:在配置Less-loader之前,我以及先配置了babel-plugin-import
,主要改动是在webpack.config.js
添加了这个:
['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }],
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'@svgr/webpack?-svgo,+titleProp,+ref![path]',
},
},
},
],
[
'import',
{
libraryName: 'antd',
libraryDirectory: 'es',
style: true
},
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
compact: isEnvProduction,
},
},
首先安装less和less-loader
yarn add less less-loader
然后开始处理webpack.config.js
这个文件
47-53行,插入了less的两行:
// style files regexes
const cssRegex =/\.css$/;
const cssModuleRegex =/\.module\.css$/;
const lessRegex =/\.less$/;
const lessModuleRegex =/\.module\.less$/;
const sassRegex =/\.(scss|sass)$/;
const sassModuleRegex =/\.module\.(scss|sass)$/;
72-73行,修改了这个函数,添加了第三个参数:
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor, preProcessorOptions={}) => {
114-130行,在添加preProcessor
时,补充了options
:
if (preProcessor) {
loaders.push(
{
loader: require.resolve('resolve-url-loader'),
options: {
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
{
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
...preProcessorOptions,
},
}
);
}
465-493行:
注意:这两行配置同antd文档自定义主题配置,可自行修改相关主题
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader',
{
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
}
),
sideEffects: true,
},
{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
},
'less-loader',
{
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
}
),
},
说几句原因。
CRA默认配置了sass-loader
,如果配置less-loader
只需要照猫画虎就可以。但是CRA中写了一个getStyleLoaders
函数,是为了少些一些重复性代码,因为处理.css
和.module.css
和.(scss|sass)
和.module.(scss|sass)
中有很多重复性的配置。贴出完整的修改前的函数代码看看:
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
// css is located in `static/css`, use '../../' to locate index.html folder
// in production `paths.publicUrlOrPath` can be a relative path
options: paths.publicUrlOrPath.startsWith('.')
? { publicPath: '../../' }
: {},
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
// Options for PostCSS as we reference these options twice
// Adds vendor prefixing based on your specified browser support in
// package.json
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebook/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
// Adds PostCSS Normalize as the reset css with default options,
// so that it honors browserslist config in package.json
// which in turn let's users customize the target behavior as per their needs.
postcssNormalize(),
],
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push(
{
loader: require.resolve('resolve-url-loader'),
options: {
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
{
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
},
}
);
}
return loaders;
};
其实对preProcessor的处理关键就在于最后几行
{
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
},
}
遗憾的是,它把options
写死了,但是为了灵活定制options
,但是又不打破CRA设计这个函数的初衷,必须加个参数来表明options
。
最后想补充一点,如果项目中不会用到.module.less
,可以把这几行注释掉or删掉(其实ant design
中好像没用到.module.less
,都是.less
)。不然每次修改主题都需要改两个地方,比较麻烦(或者提取这个options
或modifyVars
成一个常量放在文件开头常量定义的地方也可以)。
// const lessModuleRegex =/\.module\.less$/;
{
test: lessRegex,
// exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader',
{
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
}
),
sideEffects: true,
},
/*{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
},
'less-loader',
{
javascriptEnabled: true,
modifyVars: { '@primary-color': '#1DA57A' },
}
),
},*/