在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' },
}
),
},*/