More sophisticated create react app development environment configuration craco

Time:2020-11-23

More sophisticated create react app development environment configuration craco

background

For projects created with CRA scaffolding, if you want to modify the compilation configuration, you may choosenpm run ejectPop up configuration after magic change. However, eject is an irreversible operation. After you pop up the configuration, you will not be able to follow the official steps to upgrade the react script version of the project.

If you want to rewrite CRA configuration without eject, the following methods are mature at present

  1. Officially supported by CRA--scripts-versionParameter to create a project using the react scripts package that you have rewritten
  2. usereact-app-rewired + customize-craCombined overlay configuration
  3. usecracoOverlay configuration

The second method is a little more complicated than the third one, which I have experienced very well and we have noticed the latest oneAntDesign4Officials also began to recommend itcracoNow, what are we waiting for? Let’s take action soon. Today, we will discuss it in detail herecracoIt is also convenient for you to give better suggestions.

Configuration steps

  1. First, use thecreate-react-appCreate a project calledmy-project
npx create-react-app my-project
  1. Enter the project directory and install the basic dependencies
yarn add antd @craco/craco craco-less @babel/plugin-proposal-decorators babel-plugin-import -D

3. Modificationpackage.jsonMediumscripts

{
  "scripts":{
    "start": "set PORT=5000 && craco start FAST_REFRESH=true",
    "build": "set GENERATE_SOURCEMAP=false && craco build",
    "analyzer": "env NODE_ENV=production BUILD_ANALYZER=true yarn start",
    "test": "craco test"
  }
} 

4. Project root directory creationcraco.config.jsDocuments

/* craco.config.js */

module.exports = {
  ...
}

Several environment variables are used:
PORTBoot port
GENERATE_SOURCEMAPWhether to generate when packagingsourceMap
BUILD_ANALYZERAnalysis of file output compilation

This completes the basic configuration. Next, we deal with the coverage of various configurations, complete craco.config.js The configuration file structure can be found in the official craco documentsConfiguration Overview 。

Extended Babel configuration

Although you can define Babel configuration in configure, craco also provides a quick way to write and add@babel/preset-envThe configuration example is as follows:

/* craco.config.js */

module.exports = {
  babel: {
    presets: [
      [
        '@babel/preset-env',
        {
          Modules: false, // do not convert the module file of ES6, so as to use tree shaping, sideeffects, etc
          Usebuiltins: 'entry', // all gaskets not supported by browserlist environment are imported
          // https://babeljs.io/docs/en/babel-preset-env#usebuiltins
          // https://github.com/zloirock/core-js/blob/master/docs/2019-03-19-core-js-3-babel-and-a-look-into-the-future.md
          corejs: {
            Version: 3, // using [email protected]3
            proposals: true,
          },
        },
      ],
    ],
    plugins: [
        //Configure Babel plugin import
        ['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }, 'antd'],
        //Configure parser
       ["@babel/plugin-proposal-decorators", { "legacy": true }],
         ["@babel/plugin-proposal-class-properties", { "loose": true }],
       ["babel-plugin-styled-components", { "displayName": true }]
    ],
       loaderOptions: {},
       loaderOptions: (babelLoaderOptions, { env, paths }) => { return babelLoaderOptions; }
    
  },
}

Check the compilation of modules

new WebpackBar({ profile: true }),
new CircularDependencyPlugin({
  exclude: /node_modules/,
  include: /src/,
  failOnError: true,
  allowAsyncCycles: false,
  cwd: process.cwd()
})

More sophisticated create react app development environment configuration craco
More sophisticated create react app development environment configuration craco

Observe packing progress

const SimpleProgressWebpackPlugin = require('simple-progress-webpack-plugin')
module export = {
  webpack: {

    plugins: [
      //View the progress of packaging
      new SimpleProgressWebpackPlugin()
    ]
  }
}

More sophisticated create react app development environment configuration craco

Modify package output directory

module.exports = {
  webpack: {
    configure: (webpackConfig, {
      env, paths
    }) => {
      // paths.appPath='public'
      paths.appBuild = 'dist'
      webpackConfig.output = {
        ...webpackConfig.output,
          // ...{
          //   filename: whenDev(() => 'static/js/bundle.js', 'static/js/[name].js'),
          //   chunkFilename: 'static/js/[name].js'
          // },
          path:  path.resolve (E) Dirname, 'dist'), // modify the output file directory
          publicPath: '/'
      }
      return webpackConfig
    }
  }
}

If you feel cumbersome, you can directly use webpack to configure. There are so many information about webpackconfig
More sophisticated create react app development environment configuration craco

Hot update hot loader extension

How to avoid frequent refresh when starting hot update
Common hot renewal schemesreact-hot-loaderCraco also provides us with two kinds ofcraco-plugin-react-hot-reloadcraco-fast-refresh

react-hot-loaderThe configuration is as follows(portal)

step1: webpack.config.js Introduce alias configuration to remove related warnings
yarn add @hot-loader/react-dom
module.exports = {
  // ...
  resolve: {
    alias: {
      'react-dom': '@hot-loader/react-dom',
    },
  },
};
Step2: injection reference App.js
import { hot } from 'react-hot-loader/root'
function App {
    return (
      <div>ceshi</div>
     )
}
export default hot(App)

craco-plugin-react-hot-reloadThe configuration is as follows(Portal)

/* craco.config.js */
const reactHotReloadPlugin = require('craco-plugin-react-hot-reload')
const reactHotReloadPlugin = require('craco-plugin-react-hot-reload')
module.exports = {
  plugins: [{
    plugin: reactHotReloadPlugin
  }]
}

craco-fast-refreshThe configuration is as follows(Portal)

This is a recent discovery of acroreact-hot-loader Much easier to use, zero configuration, no need to modify the project code, it is said that the performance is better.

Step1: add plug-ins
/* craco.config.js */
const FastRefreshCracoPlugin = require('craco-fast-refresh')
module.exports = () => {
  return {
    plugins: [{
      plugin: FastRefreshCracoPlugin
    }],
  };
};
Step2: injection reference App.js
import React from 'react'
import { hot } from 'react-hot-loader'
const App = () => <div>Hello World!</div>

export default hot(module)(App)

Antd custom theme configuration

Configure antd theme color, you can choose the following scheme at will

combinationlessOptions

Step1: run yarn add craco less
Step 2: introduce const cracolessplugin = require ('craco less')
Step3: Configuration
{
  plugin: CracoLessPlugin,
  options: {
    lessLoaderOptions: {
      lessOptions: {
        modifyVars: {
          '@primary-color': '#1DA57A'
        },
        javascriptEnabled: true
      }
    }
  }
}

At the same time, craco also provides a special plugin to handle the integration of antd(Portal)The configuration mode is different

Craco custom support

craco-antd includes:

  • Less (provided by craco-less)
  • babel-plugin-import to only import the required CSS, instead of everything
  • An easy way to customize the theme. Set your custom variables in ./antd.customize.less
step1: yarn add craco-antd
step2: const CracoAntDesignPlugin = require('craco-antd')
step3 
{
  plugin: CracoAntDesignPlugin,
  options: {
    customizeTheme: {
      '@primary-color': '#FF061C'
    }
  }
}

in the light ofcustomizeThemeIf you want to separate, you can take the following plan

Step1: New antd.customize.less file
---------
@primary-color: #FF061C;
---------
Step2: read mode
{
  plugin: CracoAntDesignPlugin,
  options: {
  customizeThemeLessPath: path.join(__dirname,"antd.customize.less")}
 }

Relatively speaking, it will be more concise and recommended.

summary

It is really possible to customize all CRA build configurations without eject pop-up configuration. The detailed description was given before. You can go and see the requirements in this respect(Portal)。 Therefore, in the subsequent coding, we can use these two ways to build our own webpack configuration.
Note:_ Configure configuration and_ The craco configuration will be mutually exclusive and used with caution

The following is my complete arrangement craco.config.js Configuration, correspondingdemoConvenient reference
Craco also provides some other plugins, which can be added according to the actual situation(Portal)

/* craco.config.js */
/**
 *Todo: distinguish environment -- node_ ENV
 * - whenDev ☞ process.env.NODE_ENV === 'development'
 * - whenTest ☞ process.env.NODE_ENV === 'test'
 * - whenProd ☞ process.env.NODE_ENV === 'production'
 */
const {
  when, whenDev, whenProd, whenTest, ESLINT_MODES, POSTCSS_MODES
} = require('@craco/craco')
const webpack = require('webpack')
const CracoLessPlugin = require('craco-less')
const CracoAntDesignPlugin = require('craco-antd')
const CracoVtkPlugin = require('craco-vtk')
const WebpackBar = require('webpackbar')
const CircularDependencyPlugin = require('circular-dependency-plugin')
const FastRefreshCracoPlugin = require('craco-fast-refresh')
const TerserPlugin = require('terser-webpack-plugin')
const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin')
const {
  BundleAnalyzerPlugin
} = require('webpack-bundle-analyzer')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const DashboardPlugin = require('webpack-dashboard/plugin')
const SimpleProgressWebpackPlugin = require('simple-progress-webpack-plugin')

const path = require('path')

//Determine whether the compilation environment is production
const isBuildAnalyzer = process.env.BUILD_ANALYZER === 'true'

const pathResolve = pathUrl => path.join(__dirname, pathUrl)

module.exports = {
  webpack: {
    //Alias configuration
    alias: {
      '@': pathResolve('.'),
      src: pathResolve('src'),
      assets: pathResolve('src/assets'),
      common: pathResolve('src/common'),
      components: pathResolve('src/components'),
      hooks: pathResolve('src/hooks'),
      pages: pathResolve('src/pages'),
      store: pathResolve('src/store'),
      utils: pathResolve('src/utils')
        //Here is an example, which can be configured according to different requirements
    },
    plugins: [
      //Webpack build progress bar
      new WebpackBar({
        profile: true
      }),
      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
      //View the progress of packaging
      new SimpleProgressWebpackPlugin(),
      //The time conversion tool replaces moment with day
      new AntdDayjsWebpackPlugin(),
      //// add module cyclic dependency detection plug-in
      ...whenDev(
        () => [
          new CircularDependencyPlugin({
            exclude: /node_modules/,
            include: /src/,
            failOnError: true,
            allowAsyncCycles: false,
            cwd: process.cwd()
          }),
          //Webpack dev server enhancement plug-in
          new DashboardPlugin(),
          new webpack.HotModuleReplacementPlugin()
        ], []
      ),
      /**
       *Compiler product analysis
       *  - https://www.npmjs.com/package/webpack-bundle-analyzer
       *Add package product analysis plug-in
       */
      ...when(
        isBuildAnalyzer, () => [
          new BundleAnalyzerPlugin({
            Analyzermode: 'static' // HTML file output compilation analysis
            openAnalyzer: false,
            reportFilename: path.resolve(__dirname, `analyzer/index.html`)
          })
        ], []
      ),
      ...whenProd(
        () => [
          // new TerserPlugin({
          //   // sourceMap: true, // Must be set to true if using source-maps in production
          //   terserOptions: {
          //     ecma: undefined,
          //     parse: {},
          //     compress: {
          //       warnings: false,
          //       drop_ Console: true, // remove all contents of the console in the production environment
          //       drop_ Debugger: true, // remove breakpoints
          //       pure_ funcs: [' console.log '] // remove the console in the production environment
          //     }
          //   }
          // }),
          //Packing
          new CompressionWebpackPlugin({
            algorithm: 'gzip',
            test: new RegExp('\.(' + ['js', 'css'].join('|') + ')$'),
            threshold: 1024,
            minRatio: 0.8
          })
        ], []
      )
    ],
    //Extract common module
    optimization: {
      splitChunks: {
        cacheGroups: {
          commons: {
            chunks: 'initial',
            minChunks: 2,
            maxInitialRequests: 5,
            minSize: 0
          },
          vendor: {
            test: /node_modules/,
            chunks: 'initial',
            name: 'vendor',
            priority: 10,
            enforce: true
          }
        }
      }
    },
    /**
     *Rewrite any configuration of webpack
     *- configure can rewrite all configurations related to webpack. However, it is still recommended that you read the quick configuration provided by craco first, and put the configuration that cannot be solved into configure;
     *- here, select configuration as a function, which is mutually exclusive with defining the configure object directly;
     */
    configure: (webpackConfig, {
      env, paths
    }) => {
      // paths.appPath='public'
      paths.appBuild  ='dist '// modify the file directory with output package
        //Webpackconfig can deconstruct the parameters you want, such as mode, devtool, entry, etc. for more information, please check webpackConfig.json file
        /**
         *Modify output
         */
      webpackConfig.output = {
          ...webpackConfig.output,
            // ...{
            //   filename: whenDev(() => 'static/js/bundle.js', 'static/js/[name].js'),
            //   chunkFilename: 'static/js/[name].js'
            // },
            path:  path.resolve (E) Dirname, 'dist'), // modify the output file directory
            publicPath: '/'
        }
        /**
         * webpack split chunks
         */
        // webpackConfig.optimization.splitChunks = {
        //   ...webpackConfig.optimization.splitChunks,
        //   ...{
        //     chunks: 'all',
        //     name: true
        //   }
        // }
        //Returns the new configuration after rewriting
      return webpackConfig
    }
  },
  babel: {
    presets: [],
    plugins: [
      //Ant Design on demand loading
      ['import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: true
      }, 'antd'],
      ['@babel/plugin-proposal-decorators', {
        legacy: true
      }]// used to support decorators
    ],
    loaderOptions: {},
    loaderOptions: (babelLoaderOptions, {
      env, paths
    }) => {
      return babelLoaderOptions
    }
  },
  /**
   *Add plugins provided by craco
   */
  plugins: [
    //Hot update
    ...whenDev(
      () => [{
        plugin: FastRefreshCracoPlugin
      }, {
        plugin: CracoVtkPlugin()
      }, {
        plugin: new AntdDayjsWebpackPlugin()
      }], []
    ),
    //Antless 1 theme configuration
    // {
    //   plugin: CracoLessPlugin,
    //   options: {
    //     lessLoaderOptions: {
    //       lessOptions: {
    //         modifyVars: { '@primary-color': '#1DA57A' },
    //         javascriptEnabled: true
    //       }
    //     }
    //   }
    // },
    //Scheme 2: configure antd theme
    // {
    //   plugin: CracoAntDesignPlugin,
    //   options: {
    //     customizeTheme: {
    //       '@primary-color': '#FF061C'
    //     }
    //   }
    // },
    //Scheme 3: configure antd theme
    {
      plugin: CracoAntDesignPlugin,
      options: {
        customizeThemeLessPath: path.join(
          __dirname,
          "antd.customize.less"
        ),
      },
    },
  ],
  devServer: {
    port: 9000,
    proxy: {
      '/api': {
        target: 'https://placeholder.com/',
        changeOrigin: true,
        secure: false,
        xfwd: false,
      }
    }
  }
}

At the same time, we can also take a look at what APIs have been exposed to us by the government

const { when, whenDev, whenProd, whenTest, ESLINT_MODES, POSTCSS_MODES } = require("@craco/craco");

module.exports = {
    reactScriptsVersion: "react-scripts" /* (default value) */,
    style: {
        modules: {
            localIdentName: ""
        },
        css: {
            loaderOptions: { /* Any css-loader configuration options: https://github.com/webpack-contrib/css-loader. */ },
            loaderOptions: (cssLoaderOptions, { env, paths }) => { return cssLoaderOptions; }
        },
        sass: {
            loaderOptions: { /* Any sass-loader configuration options: https://github.com/webpack-contrib/sass-loader. */ },
            loaderOptions: (sassLoaderOptions, { env, paths }) => { return sassLoaderOptions; }
        },
        postcss: {
            mode: "extends" /* (default value) */ || "file",
            plugins: [],
            env: {
                autoprefixer: { /* Any autoprefixer options: https://github.com/postcss/autoprefixer#options */ },
                stage: 3, /* Any valid stages: https://cssdb.org/#staging-process. */
                features: { /* Any CSS features: https://preset-env.cssdb.org/features. */ }
            },
            loaderOptions: { /* Any postcss-loader configuration options: https://github.com/postcss/postcss-loader. */ },
            loaderOptions: (postcssLoaderOptions, { env, paths }) => { return postcssLoaderOptions; }
        }
    },
    eslint: {
        enable: true /* (default value) */,
        mode: "extends" /* (default value) */ || "file",
        configure: { /* Any eslint configuration options: https://eslint.org/docs/user-guide/configuring */ },
        configure: (eslintConfig, { env, paths }) => { return eslintConfig; },
        loaderOptions: { /* Any eslint-loader configuration options: https://github.com/webpack-contrib/eslint-loader. */ },
        loaderOptions: (eslintOptions, { env, paths }) => { return eslintOptions; }
    },
    babel: {
        presets: [],
        plugins: [],
        loaderOptions: { /* Any babel-loader configuration options: https://github.com/babel/babel-loader. */ },
        loaderOptions: (babelLoaderOptions, { env, paths }) => { return babelLoaderOptions; }
    },
    typescript: {
        enableTypeChecking: true /* (default value)  */
    },
    webpack: {
        alias: {},
        plugins: [],
        configure: { /* Any webpack configuration options: https://webpack.js.org/configuration */ },
        configure: (webpackConfig, { env, paths }) => { return webpackConfig; }
    },
    jest: {
        babel: {
            addPresets: true, /* (default value) */
            addPlugins: true  /* (default value) */
        },
        configure: { /* Any Jest configuration options: https://jestjs.io/docs/en/configuration. */ },
        configure: (jestConfig, { env, paths, resolve, rootDir }) => { return jestConfig; }
    },
    devServer: { /* Any devServer configuration options: https://webpack.js.org/configuration/dev-server/#devserver. */ },
    devServer: (devServerConfig, { env, paths, proxy, allowedHost }) => { return devServerConfig; },
    plugins: [
        {
            plugin: {
                overrideCracoConfig: ({ cracoConfig, pluginOptions, context: { env, paths } }) => { return cracoConfig; },
                overrideWebpackConfig: ({ webpackConfig, cracoConfig, pluginOptions, context: { env, paths } }) => { return webpackConfig; },
                overrideDevServerConfig: ({ devServerConfig, cracoConfig, pluginOptions, context: { env, paths, proxy, allowedHost } }) => { return devServerConfig; },
                overrideJestConfig: ({ jestConfig, cracoConfig, pluginOptions, context: { env, paths, resolve, rootDir } }) => { return jestConfig },
            },
            options: {}
        }
    ]
};

Is it cool to use so much information? If you want to explore, please act quickly and make progress together

reference resources

Recommended Today

Regular expression sharing for checking primes

This regular expression is shown as follows: Regular expressions for checking prime numbers or not To use this positive regular expression, you need to convert the natural number into multiple 1 strings. For example, 2 should be written as “11”, 3 should be written as “111”, 17 should be written as “11111111111”. This kind of […]