背景
应用 CRA 脚手架创立的我的项目,如果想要批改编译配置,通常可能会抉择 npm run eject
弹出配置后魔改。然而,eject 是不可逆操作,弹出配置后,你将无奈追随官网的脚步去降级我的项目的 react-script 版本。
如果想要无 eject 重写 CRA 配置,目前成熟的是上面这几种形式
- 通过 CRA 官网反对的
--scripts-version
参数,创立我的项目时应用本人重写过的 react-scripts 包 - 应用 react-app-rewired + customize-cra 组合笼罩配置
- 应用 craco 笼罩配置
第二种形式绝对第三种略简单一些,我本人很有领会并且咱们留神到最新的AntDesign4 官网也开始举荐 craco 了,那咱们还等什么还不快口头起来,明天次要在这里具体讨论一下 craco 的应用,也不便大家给出更好的倡议。
配置步骤
- 首先,应用
create-react-app
创立一个我的项目,这里咱们命名为my-project
npx create-react-app my-project
- 进入我的项目目录,装置根本依赖
yarn add antd @craco/craco craco-less @babel/plugin-proposal-decorators babel-plugin-import -D
3、批改 package.json
中的 scripts
{ "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、我的项目根目录创立 craco.config.js
文件
/* craco.config.js */module.exports = { ...}
下面用到了几个环境变量:PORT
启动端口GENERATE_SOURCEMAP
打包时是否生成 sourceMap
BUILD_ANALYZER
文件形式输入编译剖析
根底的配置到此实现了,接下来是解决各种配置的笼罩,残缺的 craco.config.js 配置文件构造,能够在 craco 官网的文档中具体查问:Configuration Overview 。
扩大 babel 配置
尽管能够在 configure 中定义 babel 配置,但 craco 也提供了快捷的形式独自去书写,增加 @babel/preset-env
配置示例如下:
/* craco.config.js */module.exports = { babel: { presets: [ [ '@babel/preset-env', { modules: false, // 对ES6的模块文件不做转化,以便应用tree shaking、sideEffects等 useBuiltIns: 'entry', // browserslist环境不反对的所有垫片都导入 // 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, // 应用core-js@3 proposals: true, }, }, ], ], plugins: [ // 配置 babel-plugin-import ['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }, 'antd'], // 配置解析器 ["@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; } },}
检测模块编译状况
new WebpackBar({ profile: true }),new CircularDependencyPlugin({ exclude: /node_modules/, include: /src/, failOnError: true, allowAsyncCycles: false, cwd: process.cwd()})
察看打包进度
const SimpleProgressWebpackPlugin = require('simple-progress-webpack-plugin')module export = { webpack: { plugins: [ // 查看打包的进度 new SimpleProgressWebpackPlugin() ] }}
批改打包输入目录
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(__dirname, 'dist'), // 批改输入文件目录 publicPath: '/' } return webpackConfig } }}
如果感觉繁琐也能够间接应用webpack进行configure笼罩、webpackConfig的信息大略有这么多:
热更新Hot-loader扩大
启动热更新如何防止频繁刷新
罕用的热更新计划react-hot-loader
、craco也帮咱们提供了两种craco-plugin-react-hot-reload
、craco-fast-refresh
react-hot-loader
配置如下(传送门)
step1:webpack.config.js中引入别名配置解除相干正告yarn add @hot-loader/react-dommodule.exports = { // ... resolve: { alias: { 'react-dom': '@hot-loader/react-dom', }, },};step2:注入援用App.jsimport { hot } from 'react-hot-loader/root'function App { return ( <div>ceshi</div> )}export default hot(App)
craco-plugin-react-hot-reload
配置如下(传送门)
/* 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-refresh
配置如下(传送门)
这是最近发现的新 craco plugin,绝对于 react-hot-loader
好用得多,零配置,不须要批改我的项目代码,据说性能也更好。
step1:减少插件/* craco.config.js */const FastRefreshCracoPlugin = require('craco-fast-refresh')module.exports = () => { return { plugins: [{ plugin: FastRefreshCracoPlugin }], };};step2: 注入援用App.jsimport React from 'react'import { hot } from 'react-hot-loader'const App = () => <div>Hello World!</div>export default hot(module)(App)
Antd自定义主题配置
配置antd主题色彩可随便对以下计划就行选取
联合lessOptions
step1:运行 yarn add craco-lessstep2:引入 const CracoLessPlugin = require('craco-less')step3:配置{ plugin: CracoLessPlugin, options: { lessLoaderOptions: { lessOptions: { modifyVars: { '@primary-color': '#1DA57A' }, javascriptEnabled: true } } }}
同时craco 也提供了专门的 plugin 来解决 antd 的集成(传送门)配置形式有区别
Craco自定义反对
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-antdstep2: const CracoAntDesignPlugin = require('craco-antd')step3 { plugin: CracoAntDesignPlugin, options: { customizeTheme: { '@primary-color': '#FF061C' } }}
针对customizeTheme
如果想独自抽离可采取如下计划
step1: 新建antd.customize.less文件---------@primary-color: #FF061C;---------step2:读取模式{ plugin: CracoAntDesignPlugin, options: { customizeThemeLessPath: path.join(__dirname,"antd.customize.less")} }
绝对来讲应用会更简洁一些,举荐应用。
总结
的确可能在不 eject 弹出配置的状况下,可能自定义所有的 cra 构建配置,之前进行了具体的阐明,有这方面的需要能够去看看(传送门)。因而在后续的编码中,咱们能够轻易应用这两种形式构建本人的webpack配置。
留神:_configure配置和_craco配置会互斥审慎应用
以下,是我整顿残缺的 craco.config.js 配置,相应的demo不便参照
craco 还提供一些其余 plugin具体依据理论状况自行退出(传送门)
/* craco.config.js *//** * TODO: 辨别环境 —— 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')// 判断编译环境是否为生产const isBuildAnalyzer = process.env.BUILD_ANALYZER === 'true'const pathResolve = pathUrl => path.join(__dirname, pathUrl)module.exports = { webpack: { // 别名配置 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') // 此处是一个示例,理论可依据各自需要配置 }, plugins: [ // webpack构建进度条 new WebpackBar({ profile: true }), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), // 查看打包的进度 new SimpleProgressWebpackPlugin(), // 工夫转换工具采取day替换moment new AntdDayjsWebpackPlugin(), // // 新增模块循环依赖检测插件 ...whenDev( () => [ new CircularDependencyPlugin({ exclude: /node_modules/, include: /src/, failOnError: true, allowAsyncCycles: false, cwd: process.cwd() }), // webpack-dev-server 强化插件 new DashboardPlugin(), new webpack.HotModuleReplacementPlugin() ], [] ), /** * 编译产物剖析 * - https://www.npmjs.com/package/webpack-bundle-analyzer * 新增打包产物剖析插件 */ ...when( isBuildAnalyzer, () => [ new BundleAnalyzerPlugin({ analyzerMode: 'static', // html 文件形式输入编译剖析 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, // 生产环境下移除控制台所有的内容 // drop_debugger: true, // 移除断点 // pure_funcs: ['console.log'] // 生产环境下移除console // } // } // }), // 打压缩包 new CompressionWebpackPlugin({ algorithm: 'gzip', test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'), threshold: 1024, minRatio: 0.8 }) ], [] ) ], //抽离专用模块 optimization: { splitChunks: { cacheGroups: { commons: { chunks: 'initial', minChunks: 2, maxInitialRequests: 5, minSize: 0 }, vendor: { test: /node_modules/, chunks: 'initial', name: 'vendor', priority: 10, enforce: true } } } }, /** * 重写 webpack 任意配置 * - configure 可能重写 webpack 相干的所有配置,然而,依然举荐你优先浏览 craco 提供的快捷配置,把解决不了的配置放到 configure 里解决; * - 这里抉择配置为函数,与间接定义 configure 对象形式互斥; */ configure: (webpackConfig, { env, paths }) => { // paths.appPath='public' paths.appBuild = 'dist' // 配合输入打包批改文件目录 // webpackConfig中能够解构出你想要的参数比方mode、devtool、entry等等,更多信息请查看webpackConfig.json文件 /** * 批改 output */ webpackConfig.output = { ...webpackConfig.output, // ...{ // filename: whenDev(() => 'static/js/bundle.js', 'static/js/[name].js'), // chunkFilename: 'static/js/[name].js' // }, path: path.resolve(__dirname, 'dist'), // 批改输入文件目录 publicPath: '/' } /** * webpack split chunks */ // webpackConfig.optimization.splitChunks = { // ...webpackConfig.optimization.splitChunks, // ...{ // chunks: 'all', // name: true // } // } // 返回重写后的新配置 return webpackConfig } }, babel: { presets: [], plugins: [ // AntDesign 按需加载 ['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }, 'antd'], ['@babel/plugin-proposal-decorators', { legacy: true }] // 用来反对装璜器 ], loaderOptions: {}, loaderOptions: (babelLoaderOptions, { env, paths }) => { return babelLoaderOptions } }, /** * 新增 craco 提供的 plugin */ plugins: [ // 热更新 ...whenDev( () => [{ plugin: FastRefreshCracoPlugin }, { plugin: CracoVtkPlugin() }, { plugin: new AntdDayjsWebpackPlugin() }], [] ), // 计划1、配置Antd主题less // { // plugin: CracoLessPlugin, // options: { // lessLoaderOptions: { // lessOptions: { // modifyVars: { '@primary-color': '#1DA57A' }, // javascriptEnabled: true // } // } // } // }, // 计划2、配置Antd主题 // { // plugin: CracoAntDesignPlugin, // options: { // customizeTheme: { // '@primary-color': '#FF061C' // } // } // }, // 计划3、配置Antd主题 { 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, } } }}
同时咱们也能够看一下官网给咱们裸露了哪些Api
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: {} } ]};
这么多的信息应用起来是不是很爽,想摸索的赶快口头起来共同进步啦
参考
- craco 配置
- less-loader 官网文档
- ant design 官网文档
- craco实际