zoukankan      html  css  js  c++  java
  • react-create-app 中 webapck配置 解析

    一 调出隐藏的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...
    '));
          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,
      };
    };
    
    
  • 相关阅读:
    SQL Server 执行参数化脚本时的一个性能问题
    2021 年终总结
    循序渐进——NAnt构建实例
    用C#实现单链表(创建单链表,在头部插入)
    用C#实现单链表(插入,在第i个前插,在第i个后插)
    用C#实现单链表(merge两个有序单链表)
    用C#实现单链表(取第i个结点元素,删除第i个结点)
    播放器03:以文件夹的形式添加整个文件夹里面的文件到播放列表,播放刚加进来的第一首歌曲,默认顺序播放
    用C#实现单链表(初始化数据,返回链表元素个数)
    ObjectiveC中创建单例方法的步骤
  • 原文地址:https://www.cnblogs.com/honkerzh/p/13994598.html
Copyright © 2011-2022 走看看