【React】Craco相关应用配置

Craco简介

Craco(Create React App Configuration Override) 是一个用于覆盖React项目配置的社区方案,它允许开发者在不使用 eject 命令的情况下(eject命令可以将配置完全暴露出来,但该操作不可逆,会导致失去CRA带来的便利和后续升级),轻松地自定义 Babel、ESLint、PostCSS 等配置。

开发者可以通过在项目根目录添加一个 craco.config.js 文件来定制化自己的项目配置,这使开发者不仅可以享受到 Create React App 的所有好处,同时还能保留对项目配置的完全控制。

安装依赖

sql 复制代码
npm install @craco/craco -D
或
yarn add @craco/craco -D

修改 package.json 配置

json 复制代码
"scripts": {
-   "start": "react-scripts start",
-   "build": "react-scripts build",
-   "test": "react-scripts test",
+   "start": "craco start",
+   "build": "craco build",
+   "test": "craco test",
}

在项目根目录创建 craco.config.js ,目录结构如下:

lua 复制代码
my-react-app  
    ├── src  
  + ├── craco.config.js  
    └── package.json

craco.config.js 写入如下格式配置

ini 复制代码
module.exports = {
  // ...
};

多配置文件

如果项目中有多个配置文件,Craco 会默认使用查找列表中的第一个,例如以下两个配置,craco 默认会使用 craco.config.ts

arduino 复制代码
1、craco.config.ts
2、craco.config.js

如果要使用指定名称的配置文件,可以在 package.json 中通过指定名称使用

json 复制代码
"scripts": {
    "start": "craco start --config ./craco.config.js",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },

设置别名

craco.config.js 中修改webpack的 alias 属性,在 alias 中添加别名记录

javascript 复制代码
const path = require('path');

function resolve(dir) {
  return path.resolve(__dirname, dir);
}

module.exports = {
  reactScriptsVersion: 'react-scripts',
  webpack: {
    alias: { // 设置别名
      '@': resolve('src'),
    },
  },
}

devServer开发配置

java 复制代码
module.exports = {
  reactScriptsVersion: 'react-scripts',
  devServer: {
    port: 4000,
    host: '0.0.0.0',
    proxy: { // 配置代理
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },
}

支持less

安装依赖

安装依赖 craco-less

csharp 复制代码
npm install craco-less -D
或
yarn add craco-less -D

自定义配置

修改 src/App.csssrc/App.less,然后修改样式引入

arduino 复制代码
- import './App.css';
+ import './App.less';

修改 craco.config.js 覆盖less变量

css 复制代码
const CracoLessDesignPlugin = require('craco-less');
module.exports = {
  reactScriptsVersion: 'react-scripts',
  plugins: [
    {
      plugin: CracoLessDesignPlugin,
      options: { // 修改less变量
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: { 
              '@primary-color': 'red'
            },
            javascriptEnabled: true,
          },
        },
      }
    },
  ],
}

在样式中使用less变量

less 复制代码
.App {
  text-align: center;
  background-color: @primary-color;
}

组件按需引入

组件按需引入需要借助 babel-plugin-import 插件

安装依赖

arduino 复制代码
npm install babel-plugin-import -D
或
yarn add babel-plugin-import -D

自定义配置

修改 craco.config.js 添加 babel 插件配置

java 复制代码
const CracoLessDesignPlugin = require('craco-less');
module.exports = {
  reactScriptsVersion: 'react-scripts',
  plugins: [],
  babel: {
    plugins: [
      [
        'import',
        {
          libraryName: 'antd',
          libraryDirectory: 'es',
          style: true,
        },
      ],
    ],
  }
}

支持postcss-pxtorem

postcss-pxtorem 支持可以将px转为rem

安装依赖

php 复制代码
$ npm install postcss postcss-pxtorem -D

自定义配置

javascript 复制代码
const PostCSSPx2rem = require('postcss-pxtorem');
module.exports = {
  reactScriptsVersion: 'react-scripts',
  style: {
    postcss: {
      mode: 'extends',
      loaderOptions: (postcssOptions, { env, paths }) => {
        return {
          postcssOptions: {
            ident: 'postcss',
            config: false,
            plugins: [
              PostCSSPx2rem({
                rootValue: 37.5, // 设计稿的宽度/10
                propList: ['*'], // 需转换的属性, 默认值['*'] 匹配所有css3属性的 px值
                exclude: /node_modules/i, // 忽略转换正则匹配项,如 node_modules 、.svg等
                selectorBlackList: [], // 忽略转换正则匹配项,如 .ant- 、.ant-btn等
                // minPixelValue: 0.1, // 小于或等于1px不转换
                // precision: 8, // 转换后的精度
                // unitPrecision: 5, // 转换后的小数位数
                // replace: true, // 是否替换转换后的属性
              })
            ]
          },
          sourceMap: false, // 关闭sourceMap
        }
      }
    },
  },
}

Antd主题及按需引入

安装依赖

php 复制代码
$ npm install craco-antd -D

按需引入

craco-antd 插件中包含了 babel-plugin-importcraco-less 插件,默认配置了按需引入,无需额外引入

自定义主题

修改 craco.config.js 覆盖less变量

ini 复制代码
const CracoAntDesignPlugin = require('craco-antd');
module.exports = {
  plugins: [
    {
      plugin: CracoAntDesignPlugin,
      options: { // 自定义覆盖变量
        customizeTheme: { 
          '@primary-color': '#1DA57A',
        },
      },
    },
  ],
};

也可以自定义样式文件路径

ini 复制代码
const CracoAntDesignPlugin = require('craco-antd');
const path = require('path');
module.exports = {
  reactScriptsVersion: 'react-scripts',
  plugins: [
    {
      plugin: CracoAntDesignPlugin,
      options: { // 设置path
        customizeThemeLessPath: path.join(__dirname, './src/style/antd.customize.less'),
      }
    },
  ],
}

重修服务,看到 antd 主题色发生改变即为成功。

包大小分析

安装依赖

php 复制代码
$ npm install webpack-bundle-analyzer -D

自定义配置

javascript 复制代码
const path = require('path');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
function resolve(dir) {
  return path.resolve(__dirname, dir);
}
module.exports = {
  reactScriptsVersion: 'react-scripts',
  webpack: {
    alias: { // 设置别名
      '@': resolve('src'),
    },
    configure: (webpackConfig, { env, paths }) => {
      // 生产环境配置
      if (env === 'production') {
        // 移除map文件
        webpackConfig.devtool = false;
        // 拆包
        webpackConfig.optimization.splitChunks = {
          chunks: 'async', // async异步代码分割 initial同步代码分割 all同步异步都分割
          minSize: 30000, // 分割的代码最小为30kb
          maxAsyncRequests: 5, // 异步代码最多分割出5个请求
          maxInitialRequests: 10,// 初始代码最多分割出10个请求
          automaticNameDelimiter: '~', // 缓存组之间的连接符
          name: false, // 缓存组名称
          cacheGroups: { // 缓存组
            antd: {
              test: /[\\/]node_modules[\\/](antd|@ant-design)[\\/]/,
              name: 'chunk-antd',
              chunks: 'all',
              priority: -7, // 优先级, 默认0, 优先级越高,优先抽离
              enforce: true,
            },
            common: {
              test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom|react-router|react-redux)[\\/]/,
              name: 'chunk-common',
              chunks: 'all',
              priority: -9,
            },
            vendors: {
              test:/[\\/]node_modules[\\/](axios|loadsh)[\\/]/,
              name: 'chunk-vendors',
              chunks: 'all',
              priority: -10,
            }
          }
        },
        webpackConfig.output = {
          ...webpackConfig.output,
          publicPath: './',
        }   
        return webpackConfig; 
      }
    },
    plugins: [
      new BundleAnalyzerPlugin({
        analyzerMode: 'static', // 静态文件模式
        reportFilename: 'report.html', // 默认在项目根目录生成report.html
        openAnalyzer: false, // 不自动打开浏览器
      }),
    ]
  },
}

包大小分析结果如下:

拆包

自定义配置

javascript 复制代码
const path = require('path');
function resolve(dir) {
  return path.resolve(__dirname, dir);
}
module.exports = {
  reactScriptsVersion: 'react-scripts',
  webpack: {
    alias: { // 设置别名
      '@': resolve('src'),
    },
    configure: (webpackConfig, { env, paths }) => {
      // 生产环境配置
      if (env === 'production') {
        // 移除map文件
        webpackConfig.devtool = false;
        // 拆包
        webpackConfig.optimization.splitChunks = {
          chunks: 'async', // async异步代码分割 initial同步代码分割 all同步异步都分割
          minSize: 30000, // 分割的代码最小为30kb
          maxAsyncRequests: 5, // 异步代码最多分割出5个请求
          maxInitialRequests: 10,// 初始代码最多分割出10个请求
          automaticNameDelimiter: '~', // 缓存组之间的连接符
          name: false, // 缓存组名称
          cacheGroups: { // 缓存组
            antd: {
              test: /[\\/]node_modules[\\/](antd|@ant-design)[\\/]/,
              name: 'chunk-antd',
              chunks: 'all',
              priority: -7, // 优先级, 默认0, 优先级越高,优先抽离
              enforce: true,
            },
            common: {
              test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom|react-router|react-redux)[\\/]/,
              name: 'chunk-common',
              chunks: 'all',
              priority: -9,
            },
            vendors: {
              test:/[\\/]node_modules[\\/](axios|loadsh)[\\/]/,
              name: 'chunk-vendors',
              chunks: 'all',
              priority: -10,
            }
          }
        },
        webpackConfig.output = {
          ...webpackConfig.output,
          publicPath: './',
        }   
        return webpackConfig; 
      }
    },
  },
}

拆包结果如下:

移除log

安装依赖

arduino 复制代码
$ npm install babel-plugin-transform-remove-console -D

自定义配置

ini 复制代码
const babelPluginTransformRemoveConsole = require('babel-plugin-transform-remove-console');
// 生产环境变量
const isProd = process.env.NODE_ENV === 'production';
module.exports = {
  reactScriptsVersion: 'react-scripts',
  babel: {
    plugins: [
      [
        babelPluginTransformRemoveConsole, 
        {
          exclude: isProd ? ['error', 'warn'] : [], // 移除console.log, 保留error, warn
        },
      ],
    ],
  },
}

完整示例

javascript 复制代码
const CracoAntDesignPlugin = require('craco-antd');
const path = require('path');
const PostCSSPx2rem = require('postcss-pxtorem');
// const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const babelPluginTransformRemoveConsole = require('babel-plugin-transform-remove-console');
// 生产环境变量
const isProd = process.env.NODE_ENV === 'production';
function resolve(dir) {
  return path.resolve(__dirname, dir);
}
module.exports = {
  reactScriptsVersion: 'react-scripts',
  plugins: [
    {
      plugin: CracoAntDesignPlugin,
      options: {
        customizeTheme: {
          '@primary-color': '#1DA57A'
        },
      },
    },
  ],
  style: {
    postcss: {
      mode: 'extends',
      loaderOptions: (postcssOptions, { env, paths }) => {
        return {
          postcssOptions: {
            ident: 'postcss',
            config: false,
            plugins: [
              PostCSSPx2rem({
                rootValue: 37.5, // 设计稿的宽度/10
                propList: ['*'], // 需转换的属性, 默认值['*'] 匹配所有css3属性的 px值
                exclude: /node_modules/i, // 忽略转换正则匹配项,如 node_modules 、.svg等
                selectorBlackList: [], // 忽略转换正则匹配项,如 .ant- 、.ant-btn等
                // minPixelValue: 0.1, // 小于或等于1px不转换
                // precision: 8, // 转换后的精度
                // unitPrecision: 5, // 转换后的小数位数
                // replace: true, // 是否替换转换后的属性
              })
            ]
          },
          sourceMap: false, // 关闭sourceMap
        }
      }
    },
  },
  webpack: {
    alias: { // 设置别名
      '@': resolve('src'),
    },
    configure: (webpackConfig, { env, paths }) => {
      // 生产环境配置
      if (env === 'production') {
        // 移除map文件
        webpackConfig.devtool = false;
        // 拆包
        webpackConfig.optimization.splitChunks = {
          chunks: 'async', // async异步代码分割 initial同步代码分割 all同步异步都分割
          minSize: 30000, // 分割的代码最小为30kb
          maxAsyncRequests: 5, // 异步代码最多分割出5个请求
          maxInitialRequests: 10,// 初始代码最多分割出10个请求
          automaticNameDelimiter: '~', // 缓存组之间的连接符
          name: false, // 缓存组名称
          cacheGroups: { // 缓存组
            antd: {
              test: /[\\/]node_modules[\\/](antd|@ant-design)[\\/]/,
              name: 'chunk-antd',
              chunks: 'all',
              priority: -7, // 优先级, 默认0, 优先级越高,优先抽离
              enforce: true,
            },
            common: {
              test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom|react-router|react-redux)[\\/]/,
              name: 'chunk-common',
              chunks: 'all',
              priority: -9,
            },
            vendors: {
              test:/[\\/]node_modules[\\/](axios|loadsh)[\\/]/,
              name: 'chunk-vendors',
              chunks: 'all',
              priority: -10,
            }
          }
        },
        webpackConfig.output = {
          ...webpackConfig.output,
          publicPath: './',
        }   
        return webpackConfig; 
      }
    },
    plugins: [
      // new BundleAnalyzerPlugin({
      //   analyzerMode: 'static', // 静态文件模式
      //   reportFilename: 'report.html', // 默认在项目根目录生成report.html
      //   openAnalyzer: false, // 不自动打开浏览器
      // }),
    ]
  },
  babel: {
    plugins: [
      [
        babelPluginTransformRemoveConsole, // 移除console.log, 保留error, warn
        {
          exclude: isProd ? ['error', 'warn'] : [],
        },
      ],
    ],
  },
}

问题

依赖冲突

安装依赖时,时常会出现类似问题,该问题主要是插件设置预依赖库版本导致,解决方案忽略预设置

css 复制代码
npm install craco-antd --legacy-peer-deps
或者
npm install craco-antd --force

参考


链接

友情提示

见原文:【React】Craco相关应用配置)

本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。

相关推荐
中微子3 分钟前
React Router 面试指南:从基础到实战
前端·react.js·前端框架
前端_学习之路1 小时前
React--Fiber 架构
前端·react.js·架构
coderlin_1 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
甜瓜看代码1 小时前
1.
react.js·node.js·angular.js
伍哥的传说1 小时前
React 实现五子棋人机对战小游戏
前端·javascript·react.js·前端框架·node.js·ecmascript·js
Misha韩3 小时前
React Native 一些API详解
react native·react.js
小李飞飞砖3 小时前
React Native 组件间通信方式详解
javascript·react native·react.js
小李飞飞砖3 小时前
React Native 状态管理方案全面对比
javascript·react native·react.js
前端小盆友8 小时前
从零实现一个GPT 【React + Express】--- 【5】实现网页生成能力
gpt·react.js·express