網站首頁 編程語言 正文
引言
create-react-app(以下簡稱cra)作為react官方提供的腳手架工具,是目前生成react項目一個非常常用和主流的工具。很多企業級的應用搭建也是基于這個腳手架工具上二次開發。最近這段正好最近學習了webpack打包配置工程化的一些內容,索性就以cra的配置為例,對這段時間的學習做一個總結。
準備工作
首先,我們要用cra創建一個項目。這個沒啥好說,有手就行。
create-react-app cra-config-project
這樣初始化后創建出來項目的配置信息是隱藏在node_modules中的react-scripts中的。為了更直觀的看到配置信息和修改,使用
eject命令將配置彈射出來。
yarn eject
完成后,我們項目配置的目錄結構變成這樣。
啟動命令
打開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命令都彈射出了哪些配置放到了項目目錄中來。
比起初使狀態,現在的項目目錄中除了裝有啟動腳本文件的目錄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構建的是單頁應用,只有一個入口文件
2.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文件名稱,生產環境和開發環境的區別是文件名中加上了hash
assetModuleFilename: '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文件數組使用名稱
}
3.target
target: ['browserslist'],
構建目標,從 browserslist-config 中推斷出平臺和 ES 特性 默認是browserslist 如果browserslist不存在,為web(cra項目中browserslist在package.json中)
4.bail
bail: isEnvProduction,
錯誤出現時是否立即退出,生產環境下打開
5.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
6.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)
),
},
},
7.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(),
],
},
8.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,
]),
],
},//應該使用的額外的解析插件列表
9.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打包的性能優化從配置項的各個維度做一個總結,請拭目以待。
原文鏈接:https://www.jianshu.com/p/db6b835d279c
相關推薦
- 2024-07-18 【探索SpringCloud】服務發現-Nacos服務端數據結構和模型
- 2022-12-13 C++實現一個簡易版的事件(Event)的示例代碼_C 語言
- 2022-05-18 python操作jira添加模塊的方法_python
- 2023-01-27 解決Pycharm模塊安裝慢問題的兩種方法_python
- 2022-09-26 tomcat下載安裝及配置環境變量,但打開startup文件出現閃退問題解決方法
- 2022-09-02 一文詳解Python中的重試機制_python
- 2022-07-24 .Net行為型設計模式之中介者模式(Mediator)_基礎應用
- 2023-03-30 C語言對結構體數組按照某項規則進行排序的實現過程探究_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支