简体   繁体   English

块加载错误:加载块 XY 失败。 - 生产中随机变得致命

[英]ChunkLoadError: Loading chunk XY failed. - Randomly getting fatal on PRODUCTION

we have got our ecommerce platform already in production and we are experiencing weird ChunkLoadError.我们的电子商务平台已经投入生产,我们遇到了奇怪的 ChunkLoadError。 This error happens randomly, and is not replicable.此错误随机发生,不可复制。 When we are trying to open failed file it is there and can be loaded normaly.当我们尝试打开失败的文件时,它就在那里并且可以正常加载。

If user get's this error, he get's white screen (logicaly) but after refresh everything is fine.如果用户得到这个错误,他得到的白屏(逻辑)但刷新后一切正常。

We are running SSR ecommerce on React (latest), Express (latest)我们在 React(最新)、Express(最新)上运行 SSR 电子商务

our webpack / razzle config我们的 webpack / razzle 配置


const path = require('path');
const autoprefixer = require('autoprefixer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const LoadablePlugin = require('@loadable/webpack-plugin');

const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const TerserPlugin = require('terser-webpack-plugin');
// const DuplicatePackageCheckerPlugin = require('duplicate-package-checker-webpack-plugin');

module.exports = {
  modify: (baseConfig, env, webpack) => {
    const { target, dev } = env;
    const appConfig = { ...baseConfig };
    // Setup SCSS
    if (target === 'web') {
      const filename = path.resolve(__dirname, 'build');

      const cssLoader = {
        loader: 'css-loader',
        options: {
          minimize: !dev,
          sourceMap: false,
          importLoaders: 1
        }
      };

      const postCSSLoader = {
        loader: 'postcss-loader',
        options: {
          ident: 'postcss',
          sourceMap: false,
          plugins: () => [
            autoprefixer({
              browsers: [
                '>1%',
                'last 4 versions',
                'Firefox ESR',
                'not ie < 9' // React doesn't support IE8 anyway
              ]
            })
          ]
        }
      };

      const sassLoader = {
        loader: 'sass-loader',
        options: {
          minimize: !dev,
          sourceMap: false,
          importLoaders: 1
        }
      };

      if (dev) {
        appConfig.output.filename = 'static/js/[name].js';
        appConfig.module.rules.push({
          test: /\.scss$/,
          use: ['style-loader', cssLoader, postCSSLoader, sassLoader]
        });
      } else {
        appConfig.output.filename = 'static/js/[name].[chunkhash:8].js';

        // For production, extract CSS
        appConfig.module.rules.push({
          test: /\.scss$/,
          use: [MiniCssExtractPlugin.loader, cssLoader, postCSSLoader, sassLoader]
        });

        appConfig.plugins.push(
          new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
          new webpack.IgnorePlugin(/moment/, /react-kronos/),
          new webpack.optimize.OccurrenceOrderPlugin(),
          // new webpack.optimize.LimitChunkCountPlugin({maxChunks: 50}),
          new CompressionPlugin()
          ,new BundleAnalyzerPlugin({
            analyzerMode: 'static',
            generateStatsFile: true,
            openAnalyzer: false
          })
          // ,new DuplicatePackageCheckerPlugin()
        );
      }

      // optimization
      appConfig.optimization = {
        ...baseConfig.optimization,
        minimize: !dev,
        minimizer: [new TerserPlugin({
          parallel: true,
        })],
        splitChunks: {
          chunks: 'initial',
          minSize: 30000,
          // minRemainingSize: 0,
          maxSize: 0,
          minChunks: 1,
          maxAsyncRequests: 6,
          maxInitialRequests: 4,
          automaticNameDelimiter: '~',
          automaticNameMaxLength: 30,
          cacheGroups: {
            defaultVendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10
            },
            default: {
              minChunks: 2,
              priority: -20,
              reuseExistingChunk: true
            }
          }
        },
        moduleIds: 'total-size', //added in future deterministic
        chunkIds: 'total-size', //added
        mangleWasmImports: !dev, //added
        removeAvailableModules: !dev, //added
        mergeDuplicateChunks: !dev, //added
        flagIncludedChunks: !dev,
        occurrenceOrder: false,
        usedExports: !dev,
        // namedModules: true,
        // namedChunks: true,
        runtimeChunk: 'single'
        // runtimeChunk: {
        //   name: entrypoint => `runtimechunk~${entrypoint.name}`
        // }
      };

      appConfig.plugins.push(
        new LoadablePlugin({
          outputAsset: false,
          writeToDisk: { filename }
        }),
        new LodashModuleReplacementPlugin({
          collections: true,
          cloning: true,
          deburring: true,
          // coercions: true,
          flattening: true,
          paths: true,
          // placeholders: true
          shorthands: true
          // caching: true
        })
      );
    } else {
      appConfig.module.rules.push({
        test: /\.(scss)$/,
        use: ['ignore-loader']
      });
    }
    return appConfig;
  },
  modifyBabelOptions: mainBabelOptions => {
    return {
      ...mainBabelOptions,
      ...{ plugins: [].concat(mainBabelOptions.plugins ? mainBabelOptions.plugins : [], ['lodash']) }
    };
  }
};

multiple errors多个错误

Here is randomly picked trace这是随机挑选的痕迹

(error: https://www.freshbox.sk/static/js/common-blocks-functional-userButton.3074d9ca.chunk.js)
  at m.e(webpack/bootstrap:170:18)
  at importAsync(./src/common/blocks/header/HeaderVariant2.jsx:35:9)
  at requireAsync(./src/common/blocks/header/HeaderVariant2.jsx:34:28)
  at loadAsync(./node_modules/@loadable/component/dist/loadable.esm.js:217:31)
  at componentDidMount(./node_modules/@loadable/component/dist/loadable.esm.js:147:16)
  at Ji(./node_modules/react-dom/cjs/react-dom.production.min.js:212:132)
  at b(./node_modules/react-dom/cjs/react-dom.production.min.js:255:229)
  at If(./node_modules/scheduler/cjs/scheduler.production.min.js:19:467)
  at cg(./node_modules/react-dom/cjs/react-dom.production.min.js:122:325)
  at Jj(./node_modules/react-dom/cjs/react-dom.production.min.js:248:370)
  at yj(./node_modules/react-dom/cjs/react-dom.production.min.js:239:376)
  at Ig(./node_modules/react-dom/cjs/react-dom.production.min.js:230:137)
  at bk(./node_modules/react-dom/cjs/react-dom.production.min.js:281:43)
  at a(./node_modules/react-dom/cjs/react-dom.production.min.js:284:301)
  at Nj(./node_modules/react-dom/cjs/react-dom.production.min.js:240:120)
  at ik(./node_modules/react-dom/cjs/react-dom.production.min.js:284:287)
  at hydrate(./node_modules/react-dom/cjs/react-dom.production.min.js:290:206)
  at done(./src/client/index.js:81:3)
  at checkReadyState(./node_modules/@loadable/component/dist/loadable.esm.js:428:11)
  at E/</n.push(./node_modules/@loadable/component/dist/loadable.esm.js:435:7)
  at ? (/static/js/common-components-category-listing-_default-LayoutSwitcher.869947cb.chunk.js:1:75)```

If the code is split into chunks in order to optimise the loading, the index file usually contains the names and path to all the chunks according to the current webpack build.如果为了优化加载将代码拆分成块,索引文件通常包含根据当前 webpack 构建的所有块的名称和路径。 Making changes to the code and building again can rename the chunks with edited code.更改代码并再次构建可以使用编辑过的代码重命名块。 The browser however loads the index first along with certain required chunks, and then the rest of the chunks are fetched on demand, to make the whole code-splitting optimisation work.然而,浏览器首先加载索引以及某些所需的块,然后按需获取其余的块,以使整个代码拆分优化工作。

I suspect that in this case, there is a production deploy after the index is loaded by the browser, and some of the chunks get renamed by the changes next build.我怀疑在这种情况下,浏览器加载索引后会进行生产部署,并且某些块会因下一次构建的更改而重命名。 Which results in invalid addresses for certain chunks in the stale index.这会导致陈旧索引中某些块的地址无效。 Afterwards when the old index which was in the browser tries to load those non-existent chunks when the user navigated to that part of the website, it throws Loading chunk XY failed .之后,当用户导航到网站的该部分时,浏览器中的旧索引尝试加载那些不存在的块时,它会抛出Loading chunk XY failed Refreshing should update the index and resolve the issue.刷新应该更新索引并解决问题。

One way to resolve this would be to use service workers.解决此问题的一种方法是使用服务工作者。 The way it could work is as follows:它的工作方式如下:

  • The browser loads the index and the specific required chunks to render the requested section.浏览器加载索引和特定的所需块以呈现请求的部分。
  • As the user views/uses the loaded section, the service worker installs in the background and loads all the remaining chunks into memory, so that all the chunks can be served offline when requested.当用户查看/使用加载的部分时,Service Worker 会在后台安装并将所有剩余的块加载到内存中,以便在请求时可以离线提供所有块。 Basically creating a copy of the current state of the server in the browser memory.基本上是在浏览器内存中创建服务器当前状态的副本。 This not only preserves files even after the server updates, but also improves load times of other chunks drastically.这不仅可以在服务器更新后保留文件,还可以大大缩短其他块的加载时间。
  • When there is a new production deploy, the installed service worker can serve the locally cached files initially to prevent any crash.当有新的生产部署时,已安装的 Service Worker 可以最初为本地缓存的文件提供服务,以防止任何崩溃。
  • The service worker can update in the background and serve the new version of the site when the user visits it next, or show a message that a new version is available and ask the user to reload. Service Worker 可以在后台更新并在用户下次访问站点时提供新版本的站点,或者显示新版本可用的消息并要求用户重新加载。

Using the stale-while-revalidate or cache-first logic for service workers should work here.在这里应该可以为 Service Worker 使用 stale-while-revalidate 或 cache-first 逻辑。 Hope that helps.希望有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 未处理的拒绝 (ChunkLoadError):加载块 1 失败 - Unhandled Rejection (ChunkLoadError): Loading chunk 1 failed Uncaught SyntaxError: Unexpected token &#39;&lt;&#39; and ChunkLoadError: Loading chunk 16 failed - Uncaught SyntaxError: Unexpected token '<' and ChunkLoadError: Loading chunk 16 failed 反应代码拆分:ChunkLoadError:加载块 0 失败 - React Code-Splitting: ChunkLoadError: Loading chunk 0 failed Vercel Next js Uncaught (in promise) ChunkLoadError: Loading chunk 0 failed - Vercel Next js Uncaught (in promise) ChunkLoadError: Loading chunk 0 failed ChunkLoadError:加载块 node_modules_next_dist_client_dev_noop_js 失败 - ChunkLoadError: Loading chunk node_modules_next_dist_client_dev_noop_js failed Webpack - 加载块 0 失败 - Webpack - Loading chunk 0 failed 在 GCP App Engine 上使用代码拆分部署应用程序 - 加载块 * 失败 - Deploying an App with Code Splitting on GCP App Engine - Loading chunk * failed Webpack 代码拆分“加载块失败”错误文件路径错误 - Webpack Code Splitting 'Loading chunk failed' error wrong file path Create React App 抛出“Loading chunk # failed”错误 - Create React App is throwing "Loading chunk # failed" error 哭泣。 使用 `import()` 后加载块失败 - Wepback. Loading chunk failed after using `import()`
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM