create-react-app項目配置全解析
引言
create-react-app(以下簡稱cra)作為react官方提供的腳手架工具,是目前生成react項目一個非常常用和主流的工具。很多企業(yè)級的應(yīng)用搭建也是基于這個腳手架工具上二次開發(fā)。最近這段正好最近學(xué)習(xí)了webpack打包配置工程化的一些內(nèi)容,索性就以cra的配置為例,對這段時間的學(xué)習(xí)做一個總結(jié)。
準備工作
首先,我們要用cra創(chuàng)建一個項目。這個沒啥好說,有手就行。
create-react-app cra-config-project
這樣初始化后創(chuàng)建出來項目的配置信息是隱藏在node_modules中的react-scripts中的。為了更直觀的看到配置信息和修改,使用
eject命令將配置彈射出來。
yarn eject
完成后,我們項目配置的目錄結(jié)構(gòu)變成這樣。
啟動命令
打開package.json 文件,在scripts中看到以下三條命令
"scripts": { "start": "node scripts/start.js", "build": "node scripts/build.js", "test": "node scripts/test.js" },
很明顯,這分別是項目的啟動開發(fā)環(huán)境,構(gòu)建,測試的命令。我們重點看一下scripts中開發(fā)和構(gòu)建的腳本。
start.js
在大概115行的位置,我們看到這樣一段代碼
const devServer = new WebpackDevServer(serverConfig, compiler); // Launch WebpackDevServer. devServer.startCallback(() => { ...
很明顯,這就是啟動開發(fā)服務(wù)器的關(guān)鍵代碼。在開發(fā)環(huán)境的時候,我們通過webpack-dev-server來啟動一個本地的服務(wù)器,然后把隨時構(gòu)建出來的項目放在這個服務(wù)器下面運行。實例化這個devServer對象時候傳的第一個參數(shù)是服務(wù)器的配置項,包括端口號,代理,靜態(tài)資源目錄等,具體見https://webpack.docschina.org/configuration/dev-server/;第二個參數(shù)是webpack的相關(guān)配置。如下所示:
compiler = webpack(config);
build.js
構(gòu)建腳本直接輸出打包結(jié)果,自然不再需要啟動本地服務(wù)。因此在獲取了編譯結(jié)果后,直接運行即可。因此在140行中
compiler.run((err, stats) => { //... }
在代碼中我們可以看到構(gòu)建時,編譯過程通過promise封裝,對各種錯誤情況進行了處理。
目錄結(jié)構(gòu)
在看具體的配置之前,讓我們回到這張圖,看一下eject命令都彈射出了哪些配置放到了項目目錄中來。
比起初使?fàn)顟B(tài),現(xiàn)在的項目目錄中除了裝有啟動腳本文件的目錄scripts外,另外增加的就是config目錄。打開config目錄,webpack.config.js和webpackDevServer.config.js赫然在目,根據(jù)這個文件名我們可以很明顯得知,這兩個文件一個是webpack的配置,一個是開發(fā)服務(wù)器devServer 的配置。接下來,我們就可以從這兩個文件按圖索驥,學(xué)習(xí)cra的基本配置了。
配置解析
weback.config.js
在webpack.config.js中,默認導(dǎo)出了一個接受一個環(huán)境變量作為返回一個配置對象的方法。那傳這個環(huán)境變量的目的不言而喻,一定有很多配置開發(fā)和生產(chǎn)環(huán)境是不同的。接下來重頭戲來了,讓我們來一條一條地學(xué)習(xí)下react官方對react開發(fā)環(huán)境是怎么配置的吧。
1.entry
entry: paths.appIndexJs,
也就是 src目錄下的index.js,因為cra構(gòu)建的是單頁應(yīng)用,只有一個入口文件
2.output
output: { path: paths.appBuild, // 打包后文件目錄 在config目錄中path.js中配置 pathinfo: isEnvDevelopment, // webpack 在 bundle 中是否引入「所包含模塊信息」的相關(guān)注釋 開發(fā)環(huán)境打開 生產(chǎn)環(huán)境關(guān)閉 filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/bundle.js',//打包后文件名,生產(chǎn)環(huán)境根據(jù)name放在不同文件,開發(fā)環(huán)境放在一個bundle.js文件中 chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && 'static/js/[name].chunk.js',//chunk文件名稱,生產(chǎn)環(huán)境和開發(fā)環(huán)境的區(qū)別是文件名中加上了hash assetModuleFilename: 'static/media/[name].[hash][ext]',//打包后的靜態(tài)資源目錄和文件名規(guī)則,如不指定直接放在打包后的根目錄中 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文件數(shù)組使用名稱 }
3.target
target: ['browserslist'],
構(gòu)建目標,從 browserslist-config 中推斷出平臺和 ES 特性 默認是browserslist 如果browserslist不存在,為web(cra項目中browserslist在package.json中)
4.bail
bail: isEnvProduction,
錯誤出現(xiàn)時是否立即退出,生產(chǎn)環(huán)境下打開
5.devtool
devtool: isEnvProduction ? shouldUseSourceMap ? 'source-map' : false : isEnvDevelopment && 'cheap-module-source-map',
生成sourceMap方式,cra配置為生產(chǎn)環(huán)境source-map,開發(fā)環(huán)境為cheap-module-source-map。這兩者的區(qū)別source-map調(diào)試時會顯示列信息。devtool的配置有很多種,具體見https://webpack.docschina.org/configuration/devtool/#root
6.cache
cache: { type: 'filesystem',//緩存生成的 webpack 模塊和 chunk,來改善構(gòu)建速度 開發(fā)環(huán)境下默認為type:'memory' 生產(chǎn)環(huán)境下關(guān)閉 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) ), }, },
7.optimization 優(yōu)化項
optimization: { minimize: isEnvProduction, //只在生產(chǎn)環(huán)境下開啟 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(), ], },
8.resolve 解析
resolve: { modules: ['node_modules', paths.appNodeModules].concat( modules.additionalModulePaths || [] ),//解析模塊時應(yīng)該搜索的目錄 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 || {}), },//創(chuàng)建 import 或 require 的別名,來確保模塊引入變得更簡單??梢越outils之類的文件色之后 plugins: [ new ModuleScopePlugin(paths.appSrc, [ paths.appPackageJson, reactRefreshRuntimeEntry, reactRefreshWebpackPluginRuntimeEntry, babelRuntimeEntry, babelRuntimeEntryHelpers, babelRuntimeRegenerator, ]), ], },//應(yīng)該使用的額外的解析插件列表
9.performance
performance: false,
關(guān)閉了webpack本身的性能提示,cra本身提供了FileSizeReporter來計算和報告文件大小
10. module
終于進入到我們這個比較重要的module配置項,module配置決定了webpack如何解析非js的模塊,項目中的各種靜態(tài)資源,樣式文件,乃至于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'), },
- 靜態(tài)資源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呢?開發(fā)環(huán)境下直接將所有樣式注入head中style中,生產(chǎn)環(huán)境下結(jié)合下面要介紹的miniCssExtractPlugin插件抽出后放入不同css文件。另外,這里cra還對以.module.css 和 .module.sass后綴結(jié)尾的文件進行了css module處理,如果開發(fā)者需要對樣式文件要用modules規(guī)則,可以將文件的后綴寫成這兩種。
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中去,要注意的是在生產(chǎn)環(huán)境了開啟了移除注釋,合并空格一系列優(yōu)化配置
- InlineChunkHtmlPlugin
isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
這個插件輔助將一些chunk出來的模塊內(nèi)聯(lián)到html中,比如runtime的代碼,代碼量不大。生產(chǎn)環(huán)境下開啟
- InterpolateHtmlPlugin
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
HtmlWebpackPlugin的輔助插件,可以在html文件中加入變量
- ModuleNotFoundPlugin
- ReactRefreshWebpackPlugin
isEnvDevelopment && shouldUseReactRefresh && new ReactRefreshWebpackPlugin({ overlay: false, }),
熱更新 react 組件,開發(fā)環(huán)境下開啟
- MiniCssExtractPlugin
isEnvProduction && new MiniCssExtractPlugin({ filename: 'static/css/[name].[contenthash:8].css', chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', }),
抽離css文件插件,生產(chǎn)環(huán)境下開啟
- 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內(nèi)置插件,在瀏覽器環(huán)境中定義環(huán)境變量
- webpack.ignorePlugin
new webpack.IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/, }),
wepack內(nèi)置插件,可以在打包時有選擇的忽略一些內(nèi)容,這里的配置是在打包moment的時候忽略moment的本地化內(nèi)容
isEnvDevelopment && new CaseSensitivePathsPlugin(),
解決為了解決mac系統(tǒng)中文件名大小寫不敏感導(dǎo)致的打包不報錯的問題,詳見https://github.com/facebook/create-react-app/issues/240
結(jié)語
對于工程化經(jīng)驗特別少的開發(fā)者來說,webpack的配置浩如煙海,宛如一本百科全書讓人望而興嘆。但是掌握webpack可以說是前端開發(fā)者進階的必經(jīng)之路。在學(xué)習(xí)的過程中,可以自己多搞一些demo,多去嘗試和實踐,就會漸漸的對它熟悉起來。之后,筆者計劃對webpack打包的性能優(yōu)化從配置項的各個維度做一個總結(jié),請拭目以待。
以上就是create-react-app項目配置全解析的詳細內(nèi)容,更多關(guān)于create-react-app項目配置的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
在?React?中如何從狀態(tài)數(shù)組中刪除一個元素
這篇文章主要介紹了在?React?中從狀態(tài)數(shù)組中刪除一個元素,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03react中關(guān)于Context/Provider/Consumer傳參的使用
這篇文章主要介紹了react中關(guān)于Context/Provider/Consumer傳參的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09