create-react-app项目配置全解析

目录

  • 引言
  • 准备工作
  • 启动命令
  • start.js
  • build.js
  • 目录结构
  • 配置解析
  • weback.config.js
  • 结语

引言 create-react-app(以下简称cra)作为react官方提供的脚手架工具,是目前生成react项目一个非常常用和主流的工具。很多企业级的应用搭建也是基于这个脚手架工具上二次开发。最近这段正好最近学习了webpack打包配置工程化的一些内容,索性就以cra的配置为例,对这段时间的学习做一个总结。

准备工作 首先,我们要用cra创建一个项目。这个没啥好说,有手就行。
create-react-app cra-config-project

这样初始化后创建出来项目的配置信息是隐藏在node_modules中的react-scripts中的。为了更直观的看到配置信息和修改,使用
eject命令将配置弹射出来。
yarn eject

完成后,我们项目配置的目录结构变成这样。
【create-react-app项目配置全解析】
截屏2022-04-01 上午11.31.06.png

启动命令 打开package.json 文件,在scripts中看到以下三条命令
"scripts": {"start": "node scripts/start.js","build": "node scripts/build.js","test": "node scripts/test.js"},

很明显,这分别是项目的启动开发环境,构建,测试的命令。我们重点看一下scripts中开发和构建的脚本。

start.js 在大概115行的位置,我们看到这样一段代码
const devServer = new WebpackDevServer(serverConfig, compiler); // Launch WebpackDevServer.devServer.startCallback(() => {...

很明显,这就是启动开发服务器的关键代码。在开发环境的时候,我们通过webpack-dev-server来启动一个本地的服务器,然后把随时构建出来的项目放在这个服务器下面运行。实例化这个devServer对象时候传的第一个参数是服务器的配置项,包括端口号,代理,静态资源目录等,具体见https://webpack.docschina.org/configuration/dev-server/;第二个参数是webpack的相关配置。如下所示:
compiler = webpack(config);


build.js 构建脚本直接输出打包结果,自然不再需要启动本地服务。因此在获取了编译结果后,直接运行即可。因此在140行中
compiler.run((err, stats) => {//... }

在代码中我们可以看到构建时,编译过程通过promise封装,对各种错误情况进行了处理。

目录结构 在看具体的配置之前,让我们回到这张图,看一下eject命令都弹射出了哪些配置放到了项目目录中来。

截屏2022-04-01 上午11.37.56.png
比起初使状态,现在的项目目录中除了装有启动脚本文件的目录scripts外,另外增加的就是config目录。打开config目录,webpack.config.js和webpackDevServer.config.js赫然在目,根据这个文件名我们可以很明显得知,这两个文件一个是webpack的配置,一个是开发服务器devServer 的配置。接下来,我们就可以从这两个文件按图索骥,学习cra的基本配置了。

配置解析
weback.config.js 在webpack.config.js中,默认导出了一个接受一个环境变量作为返回一个配置对象的方法。那传这个环境变量的目的不言而喻,一定有很多配置开发和生产环境是不同的。接下来重头戏来了,让我们来一条一条地学习下react官方对react开发环境是怎么配置的吧。
1.entry
entry: paths.appIndexJs,

也就是 src目录下的index.js,因为cra构建的是单页应用,只有一个入口文件
  • output
output: {path: paths.appBuild, // 打包后文件目录 在config目录中path.js中配置pathinfo: isEnvDevelopment, // webpack 在 bundle 中是否引入「所包含模块信息」的相关注释 开发环境打开 生产环境关闭filename: isEnvProduction? 'static/js/[name].[contenthash:8].js': isEnvDevelopment & & 'static/js/bundle.js',//打包后文件名,生产环境根据name放在不同文件,开发环境放在一个bundle.js文件中chunkFilename: isEnvProduction? 'static/js/[name].[contenthash:8].chunk.js': isEnvDevelopment & & 'static/js/[name].chunk.js',//chunk文件名称,生产环境和开发环境的区别是文件名中加上了hashassetModuleFilename: 'static/media/[name].[hash][ext]',//打包后的静态资源目录和文件名规则,如不指定直接放在打包后的根目录中publicPath: paths.publicUrlOrPath,//打包后的文件部署的url地址devtoolModuleFilenameTemplate: isEnvProduction? info => path.relative(paths.appSrc, info.absoluteResourcePath).replace(/\\/g, '/'): isEnvDevelopment & & (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),//自定义source-map文件数组使用名称}

  • target
target: ['browserslist'],

构建目标,从 browserslist-config 中推断出平台和 ES 特性 默认是browserslist 如果browserslist不存在,为web(cra项目中browserslist在package.json中)
  • bail
bail: isEnvProduction,

错误出现时是否立即退出,生产环境下打开
  • devtool
devtool: isEnvProduction? shouldUseSourceMap? 'source-map': false: isEnvDevelopment & & 'cheap-module-source-map',

生成sourceMap方式,cra配置为生产环境source-map,开发环境为cheap-module-source-map。这两者的区别source-map调试时会显示列信息。devtool的配置有很多种,具体见https://webpack.docschina.org/configuration/devtool/#root
  • cache
cache: {type: 'filesystem',//缓存生成的 webpack 模块和 chunk,来改善构建速度 开发环境下默认为type:'memory' 生产环境下关闭version: createEnvironmentHash(env.raw),cacheDirectory: paths.appWebpackCache,//缓存目录store: 'pack',buildDependencies: {defaultWebpack: ['webpack/lib/'],config: [__filename],tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f => fs.existsSync(f)),},},

  • optimization 优化项
optimization: {minimize: isEnvProduction, //只在生产环境下开启minimizer: [//js TerserPlugin开启代码压缩new TerserPlugin({terserOptions: {parse: {ecma: 8,},compress: {ecma: 5,warnings: false,comparisons: false,inline: 2,},mangle: {safari10: true,},keep_classnames: isEnvProductionProfile,keep_fnames: isEnvProductionProfile,output: {ecma: 5,comments: false,ascii_only: true,},},}),//css 代码压缩new CssMinimizerPlugin(),],},

  • resolve 解析
resolve: {modules: ['node_modules', paths.appNodeModules].concat(modules.additionalModulePaths || []),//解析模块时应该搜索的目录extensions: paths.moduleFileExtensions.map(ext => `.${ext}`).filter(ext => useTypeScript || !ext.includes('ts')),//如果有多个文件有相同的名字,但后缀名不同时webpack按顺序解析这些后缀名,使用户在引入模块时不带扩展名alias: {'react-native': 'react-native-web',...(isEnvProductionProfile & & {'react-dom$': 'react-dom/profiling','scheduler/tracing': 'scheduler/tracing-profiling',}),...(modules.webpackAliases || {}),},//创建 import 或 require 的别名,来确保模块引入变得更简单。可以给utils之类的文件色之后plugins: [new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson,reactRefreshRuntimeEntry,reactRefreshWebpackPluginRuntimeEntry,babelRuntimeEntry,babelRuntimeEntryHelpers,babelRuntimeRegenerator,]),],},//应该使用的额外的解析插件列表

  • performance
performance: false,

关闭了webpack本身的性能提示,cra本身提供了FileSizeReporter来计算和报告文件大小
10. module终于进入到我们这个比较重要的module配置项,module配置决定了webpack如何解析非js的模块,项目中的各种静态资源,样式文件,乃至于ts tsx jsx等loader配置都是在这个模块中配置。
  • source-map loader
shouldUseSourceMap & & {enforce: 'pre',exclude: /@babel(?:\/|\\{1,2})runtime/,test: /\.(js|mjs|jsx|ts|tsx|css)$/,loader: require.resolve('source-map-loader'),},

  • 静态资源loader
{test: [/\.avif$/],type: 'asset',mimetype: 'image/avif',parser: {dataUrlCondition: {maxSize: imageInlineSizeLimit,},},},{test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],type: 'asset',parser: {dataUrlCondition: {maxSize: imageInlineSizeLimit,},},},{test: /\.svg$/,use: [{loader: require.resolve('@svgr/webpack'),options: {prettier: false,svgo: false,svgoConfig: {plugins: [{ removeViewBox: false }],},titleProp: true,ref: true,},},{loader: require.resolve('file-loader'),options: {name: 'static/media/[name].[hash].[ext]',},},],issuer: {and: [/\.(ts|tsx|js|jsx|md|mdx)$/],},},

对各种格式的图片,svg文件的处理
  • 样式文件loader
{test: cssRegex,exclude: cssModuleRegex,use: getStyleLoaders({importLoaders: 1,sourceMap: isEnvProduction? shouldUseSourceMap: isEnvDevelopment,modules: {mode: 'icss',},}),sideEffects: true,},{test: cssModuleRegex,use: getStyleLoaders({importLoaders: 1,sourceMap: isEnvProduction? shouldUseSourceMap: isEnvDevelopment,modules: {mode: 'local',getLocalIdent: getCSSModuleLocalIdent,},}),},{test: sassRegex,exclude: sassModuleRegex,use: getStyleLoaders({importLoaders: 3,sourceMap: isEnvProduction? shouldUseSourceMap: isEnvDevelopment,modules: {mode: 'icss',},},'sass-loader'),sideEffects: true,},{test: sassModuleRegex,use: getStyleLoaders({importLoaders: 3,sourceMap: isEnvProduction? shouldUseSourceMap: isEnvDevelopment,modules: {mode: 'local',getLocalIdent: getCSSModuleLocalIdent,},},'sass-loader'),},

对样式文件的处理, 主要是scss和css,cra 为什么没有配置less文件loader呢?开发环境下直接将所有样式注入head中style中,生产环境下结合下面要介绍的miniCssExtractPlugin插件抽出后放入不同css文件。另外,这里cra还对以.module.css 和 .module.sass后缀结尾的文件进行了css module处理,如果开发者需要对样式文件要用modules规则,可以将文件的后缀写成这两种。
11. 插件
  • htmlWebpackPlugin
new HtmlWebpackPlugin(Object.assign({},{inject: true,template: paths.appHtml,},isEnvProduction? {minify: {removeComments: true,collapseWhitespace: true,removeRedundantAttributes: true,useShortDoctype: true,removeEmptyAttributes: true,removeStyleLinkTypeAttributes: true,keepClosingSlash: true,minifyJS: true,minifyCSS: true,minifyURLs: true,},}: undefined)),

没啥好说的,地球人都知道的一个插件,把打包好的js文件注入到html中去,要注意的是在生产环境了开启了移除注释,合并空格一系列优化配置
  • InlineChunkHtmlPlugin
isEnvProduction & & shouldInlineRuntimeChunk & & new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),

这个插件辅助将一些chunk出来的模块内联到html中,比如runtime的代码,代码量不大。生产环境下开启
  • InterpolateHtmlPlugin
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),

HtmlWebpackPlugin的辅助插件,可以在html文件中加入变量
  • ModuleNotFoundPlugin
  • ReactRefreshWebpackPlugin
isEnvDevelopment & & shouldUseReactRefresh & & new ReactRefreshWebpackPlugin({overlay: false,}),

热更新 react 组件,开发环境下开启
  • MiniCssExtractPlugin
isEnvProduction & & new MiniCssExtractPlugin({filename: 'static/css/[name].[contenthash:8].css',chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',}),

抽离css文件插件,生产环境下开启
  • WebpackManifestPlugin
  • ForkTsCheckerWebpackPlugin
useTypeScript & & new ForkTsCheckerWebpackPlugin({async: isEnvDevelopment,typescript: {typescriptPath: resolve.sync('typescript', {basedir: paths.appNodeModules,}),configOverwrite: {compilerOptions: {sourceMap: isEnvProduction? shouldUseSourceMap: isEnvDevelopment,skipLibCheck: true,inlineSourceMap: false,declarationMap: false,noEmit: true,incremental: true,tsBuildInfoFile: paths.appTsBuildInfoFile,},},context: paths.appPath,diagnosticOptions: {syntactic: true,},mode: 'write-references',},issue: {include: [{ file: '../**/src/**/*.{ts,tsx}' },{ file: '**/src/**/*.{ts,tsx}' },],exclude: [{ file: '**/src/**/__tests__/**' },{ file: '**/src/**/?(*.){spec|test}.*' },{ file: '**/src/setupProxy.*' },{ file: '**/src/setupTests.*' },],},logger: {infrastructure: 'silent',},}),

强制ts类型检查,如果项目使用了typescript编写的话使用
  • webpack.definePlugin
new webpack.DefinePlugin(env.stringified),

wepack内置插件,在浏览器环境中定义环境变量
  • webpack.ignorePlugin
new webpack.IgnorePlugin({resourceRegExp: /^\.\/locale$/,contextRegExp: /moment$/,}),

wepack内置插件,可以在打包时有选择的忽略一些内容,这里的配置是在打包moment的时候忽略moment的本地化内容
isEnvDevelopment & & new CaseSensitivePathsPlugin(),

解决为了解决mac系统中文件名大小写不敏感导致的打包不报错的问题,详见https://github.com/facebook/create-react-app/issues/240

结语 对于工程化经验特别少的开发者来说,webpack的配置浩如烟海,宛如一本百科全书让人望而兴叹。但是掌握webpack可以说是前端开发者进阶的必经之路。在学习的过程中,可以自己多搞一些demo,多去尝试和实践,就会渐渐的对它熟悉起来。之后,笔者计划对webpack打包的性能优化从配置项的各个维度做一个总结,请拭目以待。
以上就是create-react-app项目配置全解析的详细内容,更多关于create-react-app项目配置的资料请关注脚本之家其它相关文章!

    推荐阅读