一 調出隱藏的webpack配置文件
npm run eject
發射出配置文件之後會出現兩個文件夾,一個是config文件夾,一個是scripts文件夾。
二 script 文件夾下 start.js
首先關注script文件夾的start.js 文件 - 定義環境變量爲development,然後再應用webpack配置,啓動webpack-dev-server
// start.js
'use strict';
// 定義環境變量
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
process.on('unhandledRejection', err => {
throw err;
});
// 加載環境變量
require('../config/env');
const fs = require('fs');
const chalk = require('react-dev-utils/chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const {
choosePort,
createCompiler,
prepareProxy,
prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const paths = require('../config/paths');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
const useYarn = fs.existsSync(paths.yarnLockFile); // 是否使用yarn
const isInteractive = process.stdout.isTTY;
// 判斷文件是否存在,不存在就退出 (是否包含必要文件)
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// 定義默認端口號
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';
if (process.env.HOST) {
console.log(
chalk.cyan(
`Attempting to bind to HOST environment variable: ${chalk.yellow(
chalk.bold(process.env.HOST)
)}`
)
);
console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
);
console.log(
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
);
console.log();
}
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
// 檢查瀏覽器
checkBrowsers(paths.appPath, isInteractive)
.then(() => {
// We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port.
return choosePort(HOST, DEFAULT_PORT);
})
.then(port => {
if (port == null) {
// We have not found a port.
return;
}
// webpack 開發環境配置 (重點),configFactory 函數 通過傳入參數'development'或者 production 返回開發環境或者生產環境的 webpack配置
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const useTypeScript = fs.existsSync(paths.appTsConfig);
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
const urls = prepareUrls(
protocol,
HOST,
port,
paths.publicUrlOrPath.slice(0, -1)
);
const devSocket = {
warnings: warnings =>
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
errors: errors =>
devServer.sockWrite(devServer.sockets, 'errors', errors),
};
// 創建編譯器
const compiler = createCompiler({
appName,
config,
devSocket,
urls,
useYarn,
useTypeScript,
tscCompileOnError,
webpack,
});
// proxy 代理配置,可在package.json 中進行配置
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(
proxySetting,
paths.appPublic,
paths.publicUrlOrPath
);
// 創建webpack-dev-server的配置
const serverConfig = createDevServerConfig(
proxyConfig,
urls.lanUrlForConfig
);
const devServer = new WebpackDevServer(compiler, serverConfig);
// 啓動 webpackDevServer 服務器
devServer.listen(port, HOST, err => {
if (err) {
return console.log(err);
}
if (isInteractive) {
clearConsole();
}
if (process.env.NODE_PATH) {
console.log(
chalk.yellow(
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
)
);
console.log();
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
['SIGINT', 'SIGTERM'].forEach(function(sig) {
process.on(sig, function() {
devServer.close();
process.exit();
});
});
if (isInteractive || process.env.CI !== 'true') {
// Gracefully exit when stdin ends
process.stdin.on('end', function() {
devServer.close();
process.exit();
});
process.stdin.resume();
}
})
.catch(err => {
if (err && err.message) {
console.log(err.message);
}
process.exit(1);
});
三 config 文件夾下 path.js
再來看看 config文件夾內的paths.js 文件 ,該文件主要作用是 輸出模塊的絕對路徑,防止後續多次查找路徑,輸出模塊擴展名
// path.js
'use strict';
// 處理路徑的模塊
const path = require('path');
const fs = require('fs');
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
// 項目更目錄
const appDirectory = fs.realpathSync(process.cwd());
// 生成絕對路徑的方法
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
// 所有資源的公共防衛 : /
const publicUrlOrPath = getPublicUrlOrPath(
process.env.NODE_ENV === 'development',
require(resolveApp('package.json')).homepage,
process.env.PUBLIC_URL
);
// 擴展名
const moduleFileExtensions = [
'web.mjs',
'mjs',
'web.js',
'js',
'web.ts',
'ts',
'web.tsx',
'tsx',
'json',
'web.jsx',
'jsx',
];
// 解析模塊的方法
// Resolve file paths in the same order as webpack
const resolveModule = (resolveFn, filePath) => {
const extension = moduleFileExtensions.find(extension =>
fs.existsSync(resolveFn(`${filePath}.${extension}`))
);
if (extension) {
return resolveFn(`${filePath}.${extension}`);
}
return resolveFn(`${filePath}.js`);
};
// 獲取文件
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
appJsConfig: resolveApp('jsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
publicUrlOrPath,
};
module.exports.moduleFileExtensions = moduleFileExtensions;
四 最最最重要的文件 webpack.config.js
'use strict';
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safePostCssParser = require('postcss-safe-parser');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const modules = require('./modules');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const postcssNormalize = require('postcss-normalize');
const appPackageJson = require(paths.appPackageJson);
// 默認是生成source-map,如果定義環境變量 爲false,就不適用source-map,
// 通過cross-env 去修改
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// 是否內聯runtime文件
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true';
// 最小轉換爲base64的圖片大小,10kb左右
const imageInlineSizeLimit = parseInt(
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
);
// 判斷是否使用 typescript
const useTypeScript = fs.existsSync(paths.appTsConfig);
// 樣式文件的正則
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
// 生成最終webpack開發或生成環境配置的函數
module.exports = function(webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
// Variable used for enabling profiling in Production
// passed into alias object. Uses a flag if passed into the build command
const isEnvProductionProfile =
isEnvProduction && process.argv.includes('--profile');
// 獲取環境變量的方法
// 加載.env文件的環境變量,REACT_APP 開頭
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
// 處理樣式文件loader的配置
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
isEnvDevelopment && require.resolve('style-loader'),
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
// css is located in `static/css`, use '../../' to locate index.html folder
// in production `paths.publicUrlOrPath` can be a relative path
options: paths.publicUrlOrPath.startsWith('.')
? { publicPath: '../../' }
: {},
},
{
loader: require.resolve('css-loader'),
options: cssOptions,
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
postcssNormalize(),
],
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
].filter(Boolean);
if (preProcessor) {
loaders.push(
{
loader: require.resolve('resolve-url-loader'),
options: {
sourceMap: isEnvProduction && shouldUseSourceMap,
},
},
{
loader: require.resolve(preProcessor),
options: {
sourceMap: true,
},
}
);
}
return loaders;
};
// webpack 配置對象 (核心)
return {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
bail: isEnvProduction, // 生成環境打包出錯就中止打包,開發環境中不用中止
devtool: isEnvProduction
? shouldUseSourceMap
? 'source-map' // 開發環境
: false
: isEnvDevelopment && 'cheap-module-source-map', // 生產環境
entry: [
isEnvDevelopment &&
require.resolve('react-dev-utils/webpackHotDevClient'), // 開發環境下使用 熱加載模塊
// app 下的index.js 作爲入口文件開始打包
paths.appIndexJs,
].filter(Boolean),
output: {
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
// 添加註釋 到文件中
pathinfo: isEnvDevelopment,
// 輸出的文件名,使用contenthash 進行緩存
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: isEnvDevelopment && 'static/js/bundle.js',
// 代碼分割 出來的文件會以 chunkFilename進行命名
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: isEnvDevelopment && 'static/js/[name].chunk.js',
// 默認爲 / ,可通過package.json中的homepage進行配置
publicPath: paths.publicUrlOrPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
path
.relative(paths.appSrc, info.absoluteResourcePath)
.replace(/\\/g, '/')
: isEnvDevelopment &&
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
jsonpFunction: `webpackJsonp${appPackageJson.name}`, // 多模塊 不會產生衝突
// 全局對象 window 或者 global 使用 this
globalObject: 'this',
},
// 啓動壓縮 ,優化
optimization: {
minimize: isEnvProduction,
minimizer: [
// 壓縮js
new TerserPlugin({
terserOptions: {
parse: {
// We want terser to parse ecma 8 code. However, we don't want it
// to apply any minification steps that turns valid ecma 5 code
// into invalid ecma 5 code. This is why the 'compress' and 'output'
// sections only apply transformations that are ecma 5 safe
// https://github.com/facebook/create-react-app/pull/4234
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
// Disabled because of an issue with Terser breaking valid code:
// https://github.com/facebook/create-react-app/issues/5250
// Pending further investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
},
mangle: {
safari10: true,
},
// Added for profiling in devtools
keep_classnames: isEnvProductionProfile,
keep_fnames: isEnvProductionProfile,
output: {
ecma: 5,
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},
sourceMap: shouldUseSourceMap,
}),
// 壓縮 css
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
// `inline: false` forces the sourcemap to be output into a
// separate file
inline: false,
// `annotation: true` appends the sourceMappingURL to the end of
// the css file, helping the browser find the sourcemap
annotation: true,
}
: false,
},
cssProcessorPluginOptions: {
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
},
}),
],
// 代碼分割:將node_modules 單獨成一個chunk,超過20kb重新分割。懶加載,預加載文件進行分割打包
splitChunks: {
chunks: 'all',
name: false,
},
// 解決 contenthash 緩存失效問題,比如a文件引用b文件,b文件發生改變,導致a文件的緩存也失效的問題。runtime文件只保存文件的contenthash值,文件較小
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`,
},
},
// 定義解析規則
resolve: {
modules: ['node_modules', paths.appNodeModules].concat(
modules.additionalModulePaths || []
),
extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: {
'react-native': 'react-native-web',
...(isEnvProductionProfile && {
'react-dom$': 'react-dom/profiling',
'scheduler/tracing': 'scheduler/tracing-profiling',
}),
...(modules.webpackAliases || {}),
},
plugins: [
PnpWebpackPlugin,
// 模塊解析策略,限制查找定定義模塊的範圍,只能在src內部
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
PnpWebpackPlugin.moduleLoader(module),
],
},
// loader的配置
module: {
strictExportPresence: true,
rules: [
// 嚴格導出
{ parser: { requireEnsure: false } },
// 先執行eslint-loader
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
enforce: 'pre',
use: [
{
options: {
cache: true,
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
resolvePluginsRelativeTo: __dirname,
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc, // 只包含src內的文件
},
{
// oneOf是一種性能優化,文件只能匹配數組內的一個loader ,避免多次去匹配沒有必要的loader
oneOf: [
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
// 轉化爲base64 的圖片就不用再進行請求,整體請求數就會少一點,對服務器壓力會小一點。20kb一下能夠接受
limit: imageInlineSizeLimit,
name: 'static/media/[name].[hash:8].[ext]',
},
},
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent:
'@svgr/webpack?-svgo,+titleProp,+ref![path]',
},
},
},
],
],
// It enables caching results in ./node_modules/.cache/babel-loader/
// babel-loader 緩存配置
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
compact: isEnvProduction,
},
},
// Process any JS outside of the app with Babel.
// 除了src以爲的js文件,比如node_modules中js文件
{
test: /\.(js|mjs)$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
// See #6846 for context on why cacheCompression is disabled
cacheCompression: false,
// Babel sourcemaps are needed for debugging into node_modules
// code. Without the options below, debuggers like VSCode
// show incorrect code and set breakpoints on the wrong lines.
sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use MiniCSSExtractPlugin to extract that CSS
// to a file, but in development "style" loader enables hot editing - style-loader自動配置了 HMR - 模塊熱替換
// of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
}),
sideEffects: true, // 樣式文件不會進行tree shaking
},
// 腳手架中有配置 cssModule 的打包策略,使用時候只需要 以.module.css 或者 .module.scss 結尾即可
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'sass-loader'
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
},
'sass-loader'
),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
// In production, they would get copied to the `build` folder.
// This loader doesn't use a "test" so it will catch all modules
// that fall through the other loaders.
// 處理其他文件,多媒體文件等;什麼樣的文件 輸入,就以什麼樣子格式輸出,一般作爲最後一個文件輸出
{
loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects
// its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders.
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/,/.scss$/],
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
}
// ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader.
],
},
],
},
// 插件
plugins: [
// 處理html 插件 : 以template爲模板,創建一個html文件,並將 處理好的bundle文件自動引入到html文件中 (css,js,dll單獨打包的依賴文件等)
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? { // 生產環境下才配置html的壓縮,移除註釋
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
),
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
// https://github.com/facebook/create-react-app/issues/5358
isEnvProduction &&
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]), // 是否內聯runtime文件,作用就是少發一個請求,通過cross-env 將環境變量設置爲true
// 解析index.html 中 public url
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
// 模塊找不到,給出更好的提示
new ModuleNotFoundPlugin(paths.appPath),
// 定義環境變量
new webpack.DefinePlugin(env.stringified),
// 開發環境下,HMR功能 - 熱模塊替換(module-hot-replace)
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
// 文件路徑 嚴格區分大小寫
isEnvDevelopment && new CaseSensitivePathsPlugin(),
// 監視node_modules,一旦發生變化會重啓dev-server,不需要自己手動的進行重啓了。 模塊找不到,不會重啓 webpack-dev-server
isEnvDevelopment &&
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
// 生產環境下提取css樣式文件爲一個單獨的文件
isEnvProduction &&
new MiniCssExtractPlugin({ // mini-css-extract-plugin - css 提取爲單獨的文件的插件
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', // 被代碼劃分出去的css 將以chunkFilename的方式進行命名
}),
// Generate an asset manifest file with the following content:
// - "files" key: Mapping of all asset filenames to their corresponding
// output file so that tools can pick it up without having to parse
// `index.html`
// - "entrypoints" key: Array of files which are included in `index.html`,
// can be used to reconstruct the HTML if necessary
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
);
return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), // moment 中 locale 這個庫太大了,內部的一些資源用不到,不打包進來,比如locale。用日期庫可以使用datejs
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the webpack build.
isEnvProduction &&
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [/\.map$/, /asset-manifest\.json$/],
importWorkboxFrom: 'cdn',
navigateFallback: paths.publicUrlOrPath + 'index.html',
navigateFallbackBlacklist: [
// Exclude URLs starting with /_, as they're likely an API call
new RegExp('^/_'),
// Exclude any URLs whose last part seems to be a file extension
// as they're likely a resource and not a SPA route.
// URLs containing a "?" character won't be blacklisted as they're likely
// a route with query params (e.g. auth callbacks).
new RegExp('/[^/?]+\\.[^/]+$'),
],
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: isEnvDevelopment,
useTypescriptIncrementalApi: true,
checkSyntacticErrors: true,
resolveModuleNameModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
resolveTypeReferenceDirectiveModule: process.versions.pnp
? `${__dirname}/pnpTs.js`
: undefined,
tsconfig: paths.appTsConfig,
reportFiles: [
'**',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!**/src/setupProxy.*',
'!**/src/setupTests.*',
],
silent: true,
// The formatter is invoked directly in WebpackDevServerUtils during development
formatter: isEnvProduction ? typescriptFormatter : undefined,
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser.
// Tell webpack to provide empty mocks for them so importing them works.
// 默認會將node 中包 打包到項目中來,瀏覽器端也根本無法使用,所以講node內的模塊置爲空
node: {
module: 'empty',
dgram: 'empty',
dns: 'mock',
fs: 'empty',
http2: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};
};