The Copy Monkey
HomeResourceTrendDemoAboutLink

Made with ❤️ by bhwang

© 2025 The Copy Monkey. All rights reserved.

在 craco-less 下加点功能

2021-10-06

前言

用 create-react-app 创建的项目,如果想要修改配置,一般有下面几种方法:

  • 通过官方提供的环境变量或者修改 package.json 中的参数
  • eject 弹出配置
  • 通过 craco 这样的库

我的项目里使用了 craco 这个方案,原因是比较好维护后续更新。

需求

项目里引用了一些 less 库,其中使用了字体文件,形式如下

@font-face {
    font-family: 'iconfont';
    src: url('//example.com/iconfont.eot');
    src: url('//example.com/iconfont.eot?#iefix') format('embedded-opentype'),
        url('//example.com/iconfont.woff') format('woff'),
        url('//example.com/iconfont.ttf') format('truetype'),
        url('//example.com/iconfont.svg#iconfont') format('svg');
}
.iconfont {
    font-family: 'iconfont'!important;
}

这个在普通网页下没什么问题,但是放到 Electron 这样的环境下,就会出现问题。 浏览器会认为网址是 file://example.com/iconfont.woff ,这显然是找不到的。

解决方法

一是直接修改库,这显然不是很好搞;二是通过 postcss 插件,重写 //example.com 为 http://example.com,这种更直接点。

craco-less

craco 通过 craco-less 插件来支持 less 模块导入。

它的原理是给 webpack 增加一个 rule ,当碰到 \.less 或者 \.module\.less 文件的时候,依次使用下面这些 loader 导入文件:

  • style-loader / mini-css-extract-loader
  • css-loader
  • postcss-loader
  • resolve-url-loader
  • less-loader

这里可以发现其实已经使用了 postcss ,只要我们覆盖里面的选项即可满足需求。但是一看文档,根本啥都没写,无奈只能去翻下代码。

/* else */
if (rule.loader.includes(`${pathSep}postcss-loader${pathSep}`)) {
  lessRule.use.push({
    loader: rule.loader,
    options: {
      ...rule.options,
      ...(pluginOptions.postcssLoaderOptions || {})
    }
  });
}

可以看到有个 postcssLoaderOptions 可以用来覆盖 postcss-loader 选项。那么 create-react-app 里面默认的选项都是些什么呢?

查阅下 react-scripts/config/webpack.config.js ,可以找到如下配置

{
  // 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 : isEnvDevelopment,
  },
},

也就是说只要把 plugins 替换成我们自己的选项即可

postcss 插件

参考一些 postcss 插件教程,最后搞了个插件

// custom-url-postcss-plugin.js
const postcss = require('postcss');

module.exports = postcss.plugin('postcss-focus', function () {
  return function (css) {
    css.walkDecls(decl => {
      if (decl.prop === 'src' && /^url\('\/\/baidu.com/.test(decl.value)) {
        decl.value = decl.value.replace(/^url\('\/\/baidu.com/, `url('https://google.com`);
      }
    });
  };
});

覆盖 craco-less 配置

module.exports = {
  plugins: [
    //...
    {
      plugin: LessPlugin,
      options: {
        postcssLoaderOptions: {
          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.
            require('postcss-normalize'),
            require('./custom-url-postcss-plugin')
          ]
        }
      }
    }
  ]
};

感悟

看来要多看看这些脚手架的源码,因为当你要修改一下其中的配置,都不知道从哪个环节下手......