Webpack 5:Hot Module Replacement 编译整个应用程序 + 重新加载

问题描述

我们的 lerna monorepo 中的“apps/admin”包正在从 webpack 4 迁移到 webpack 5。升级后,我们注意到热模块更换“有效”但有两个问题:

  • 它总是完全重新加载
  • 它会在每次更改时重建整个应用,大约需要 60 秒

此外,一般构建时间要慢 40% 以上(对于开发和生产构建)。

为什么需要这么长时间,我们可以防止它每次都完全重建吗?

任何输入将不胜感激;到目前为止,我们已经查看了许多 StackOverflow 问题... :)

Webpack 配置:

const path = require('path');
const webpack = require('webpack');
const sass = require('sass');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); const copyWebpackPlugin = require('copy-webpack-plugin');
// const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

const {
  ADMIN_SENTRY_DSN = 'xxx',API = 'xxx',APP = 'admin',GA_ID = 'xxx',ENV = 'development',NODE_ENV = 'development',PUBLIC_PATH = '/',ZENDESK = 'xxx',CALENDLY = 'xxx',} = process.env;

const DEV = NODE_ENV === 'development';
const build = (ENV === 'staging' || ENV === 'live') ? 'production' : NODE_ENV;

const plugins = [
  new webpack.IgnorePlugin(/^\.\/locale$/,/moment$/),// new BundleAnalyzerPlugin(),new copyWebpackPlugin({
    patterns: [
      {
        from: path.resolve(__dirname,'apps',APP,'public','*.*'),to: path.resolve(__dirname,'dist'),context: path.resolve(__dirname,'public'),},{
        from: path.resolve(__dirname,'_redirects'),],}),new webpack.DefinePlugin({
    'process.env.ADMIN_SENTRY_DSN': JSON.stringify(ADMIN_SENTRY_DSN),'process.env.API': JSON.stringify(API),'process.env.APP': JSON.stringify(APP),'process.env.ENV': JSON.stringify(ENV),'process.env.GA_ID': JSON.stringify(GA_ID),'process.env.NODE_ENV': JSON.stringify(build),'process.env.PUBLIC_PATH': JSON.stringify(PUBLIC_PATH),'process.env.ZENDESK': JSON.stringify(ZENDESK),'process.env.CALENDLY': JSON.stringify(CALENDLY),new HTMLWebpackPlugin({
    filename: path.resolve(__dirname,'dist','index.html'),template: path.resolve(__dirname,'template.ejs'),favicon: path.resolve(__dirname,'favicon.ico'),inject: 'body',cache: false,minify: !DEV ? {
      collapseWhitespace: true,removeComments: true,removeRedundantAttributes: true,removeScriptTypeAttributes: true,removeStyleLinkTypeAttributes: true,useShortDoctype: true,} : false,new MiniCssExtractPlugin({
    filename: '[name].css',chunkFilename: '[id].css',];

if (!DEV) {
  plugins.push(
    new CompressionWebpackPlugin({
      filename: '[path].[base].gz',algorithm: 'gzip',test: /\.js$|\.css$/,minRatio: 1,})
  );
}

module.exports = {
  mode: build,target: DEV ? 'web' : 'browserslist',entry: {
    app: path.resolve(__dirname,`./apps/${APP}/src/index.jsx`),output: {
    filename: '[name].[contenthash].js',chunkFilename: '[name].chunk.[chunkhash].js',path: path.resolve(__dirname,publicPath: PUBLIC_PATH,resolve: {
    extensions: ['.json','.js','.jsx'],fallback: {
      net: false,tls: false,dns: false,plugins,module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg|woff|woff2)$/,use: [
          {
            loader: 'file-loader',{
        test: /\.less$/,use: [
          DEV ? {
            loader: 'style-loader',} : MiniCssExtractPlugin.loader,{
            loader: 'css-loader',{
            loader: 'postcss-loader',{
            loader: 'less-loader',options: {
              modifyVars: {
                'primary-color': '#0c99cc','link-color': '#0c99cc',javascriptEnabled: true,}],{
        test: /\.scss$/,exclude: /(node_modules)/,{
            loader: 'sass-loader',options: {
              implementation: sass,{
        test: /\.(css)$/,{
        test: /\.(js|jsx)$/,use: ['babel-loader'],devServer: {
    publicPath: PUBLIC_PATH,historyApiFallback: true,contentBase: path.resolve(__dirname,hot: true,host: '0.0.0.0',stats: 'minimal',port: 3000,watchOptions: {
      ignored: ['**/node_modules/**'],devtool: 'source-map',optimization: {
    concatenateModules: true,usedExports: true,sideEffects: true,moduleIds: 'deterministic',chunkIds: 'named',removeAvailableModules: true,splitChunks: {
      chunks: 'all',maxInitialRequests: Infinity,minSize: 0,cacheGroups: {
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,name: 'react',vendor: {
          test: /node_modules/,name(module) {
            let packageName = module.context.match(
              /[\\/]node_modules[\\/](.*?)([\\/]|$)/
            )[1];

            if (packageName.startsWith('@carl')) {
              packageName = module.rawRequest;
            }

            packageName = packageName.replace('@','').replace('/','-');

            return `module.${packageName}`;
          },minimize: !DEV,minimizer: [
      '...',new CssMinimizerPlugin(),};

我们的 package.json

{
  "name": "carl-frontend","private": true,"workspaces": [
    "lib/*","apps/*"
  ],"scripts": {
    "admin:dev": "ENV=development API=${API:-xxx} APP=admin webpack serve","admin:staging": "ENV=staging API=${API:-xxx} APP=admin webpack serve","admin:live": "ENV=live API=xxx APP=admin webpack serve","admin:build": "rm -rf ./apps/admin/dist && NODE_ENV=production APP=admin webpack","admin:serve": "http-server ./apps/admin/dist -g","teaser:dev": "cd ./apps/teaser && API=${API:-xxx} yarn dev","teaser:staging": "cd ./apps/teaser && ADMIN_URL=xxx API=${API:-xxx} yarn dev","teaser:live": "cd ./apps/teaser && API=xxx yarn dev","teaser:build": "cd ./apps/teaser && yarn build","teaser:serve": "cd ./apps/teaser && API=${API:-xxx} yarn start","lint": "eslint --cache --cache-location .cache/.eslintcache --ext js --ext jsx lib apps","test": "cross-env NODE_ICU_DATA=node_modules/full-icu API=${API:-xxx} jest --bail --maxWorkers=2","cover": "yarn test --coverage","teaser:cypress": "./cypress/scripts/test-teaser.sh","connect:cypress": "./cypress/scripts/test-connect.sh","connect:backend": "./cypress/mocks/api/run.sh tmp/cypress/mocks/api ${CONTAINER_CTRL:-docker-compose}"
  },"husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },"lint-staged": {
    "linters": {
      "*.{js,jsx}": [
        "eslint"
      ]
    }
  },"dependencies": {
    "@babel/runtime": "^7.4.4","numbro": "^2.3.2","tailwindcss": "^2.1.2"
  },"devDependencies": {
    "@babel/core": "^7.4.4","@babel/plugin-proposal-class-properties": "^7.4.4","@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3","@babel/plugin-proposal-object-rest-spread": "^7.4.4","@babel/plugin-proposal-optional-chaining": "^7.8.3","@babel/plugin-Syntax-dynamic-import": "^7.2.0","@babel/plugin-transform-async-to-generator": "^7.4.4","@babel/plugin-transform-runtime": "^7.4.4","@babel/preset-env": "^7.4.4","@babel/preset-react": "^7.0.0","@testing-library/react-hooks": "^3.2.1","@virtuous/eslint-config": "^2.0.0","@wojtekmaj/enzyme-adapter-react-17": "^0.6.1","babel-jest": "^27.0.1","autoprefixer": "^10.2.6","babel-loader": "^8.0.6","babel-plugin-import": "^1.13.1","babel-plugin-transform-react-remove-prop-types": "^0.4.24","cache-loader": "^3.0.1","compression-webpack-plugin": "^8.0.0","copy-webpack-plugin": "^8.1.1","cross-env": "^7.0.2","css-loader": "^2.1.1","css-minimizer-webpack-plugin": "^3.0.0","cssnano": "^4.1.10","cypress": "^7.1.0","enzyme": "^3.11.0","jest-enzyme": "~7.1.2","jest-environment-jsdom": "^27.0.1","eslint": "^5.16.0","file-loader": "^3.0.1","full-icu": "^1.3.1","html-webpack-plugin": "^5.3.1","http-server": "^0.11.1","husky": "^2.3.0","jest": "^27.0.1","jest-svg-transformer": "^1.0.0","lerna": "^3.13.4","less": "^3.9.0","less-loader": "^5.0.0","lint-staged": "^8.1.7","mini-css-extract-plugin": "^1.6.0","postcss": "^8.3.0","postcss-loader": "^5.3.0","react": "^17.0.2","react-dom": "^17.0.2","sass": "^1.27.0","sass-loader": "^7.1.0","style-loader": "^2.0.0","webpack": "^5.37.1","webpack-bundle-analyzer": "^4.4.2","webpack-cli": "^4.7.0","webpack-dev-server": "^3.11.2"
  },"browserslist": [
    ">0.2%","not dead","not ie <= 11","not op_mini all"
  ]
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)