共计 12068 个字符,预计需要花费 31 分钟才能阅读完成。
背景
应用 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-dom
module.exports = {
// ...
resolve: {
alias: {'react-dom': '@hot-loader/react-dom',},
},
};
step2:注入援用 App.js
import {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.js
import 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-less
step2:引入 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-antd
step2: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 实际