【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相关应用配置)

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

相关推荐
腾讯TNTWeb前端团队8 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
市民中心的蟋蟀17 小时前
第五章 使用Context和订阅来共享组件状态
前端·javascript·react.js
谦谦橘子18 小时前
服务端渲染原理解析
前端·javascript·react.js
安分小尧1 天前
[特殊字符] 使用 Handsontable 构建一个支持 Excel 公式计算的动态表格
前端·javascript·react.js·typescript·excel
ElasticPDF-新国产PDF编辑器1 天前
React 项目 PDF 批注插件库在线版 API 示例教程
react.js·pdf·json
帅帅哥的兜兜1 天前
react中hooks使用
前端·javascript·react.js
拉不动的猪1 天前
刷刷题49(react中几个常见的性能优化问题)
前端·react.js·面试
小满zs2 天前
React-router v7 第二章(路由模式)
前端·react.js
大莲芒2 天前
react 15-16-17-18各版本的核心区别、底层原理及演进逻辑的深度解析--react18
前端·javascript·react.js