从运行时块加载生成的 webpack 块

Sji*_*iep 5 javascript webpack

我正在使用新的(反应)代码部分更新现有的 Web 应用程序,并使用 webpack 将所有内容捆绑在一起进行生产。因为现有的 HTML 页面(实际上是 XML 转换为 HTML)已经存在,所以我无法index.html使用HtmlWebpackPlugin.

我想要实现的是 webpack 生成一个小的runtime.bundle.js它将动态加载其他生成的块(main.[contenthash]vendor.[contenthash]),而不是将这些条目作为script标签添加到index.html. 这样,runtime.bundle.js可以将其设置为nocache,而其他大块可以由浏览器缓存,并在代码更改时正确获取。

作为示例,这是生成的主体块index.html,请注意注释:

<html>
  <head>...</head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script type="text/javascript" src="runtime.bundle.js"></script>

    <!-- I want these two files below not injected as script tags, 
         but loaded from the runtime.bundle.js file above  -->

    <script type="text/javascript" src="vendors.31b8acd750477817012d.js"></script>
    <script type="text/javascript" src="main.1e4a456d496cdd2e1771.js"></script>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

运行时文件已经加载了一个不同的块,该块是使用以下代码从 JS 动态导入的:

const App = React.lazy(() => import(/* webpackChunkName: "modulex" */ './App'));
Run Code Online (Sandbox Code Playgroud)

这会在以下位置创建以下代码片段runtime.bundle.js

          a = document.createElement('script');
        (a.charset = 'utf-8'),
          (a.timeout = 120),
          i.nc && a.setAttribute('nonce', i.nc),
          (a.src = (function(e) {
            return (
              i.p +
              '' +
              ({ 1: 'modulex' }[e] || e) +
              '.' +
              { 1: '0e0c4000d075e81c1e5b' }[e] +
              '.js'
            );
Run Code Online (Sandbox Code Playgroud)

vendors那么对于and mainchunk可以实现同样的效果吗?

我能想到的唯一的其他替代解决方案是使用 来WebpackManifestPlugin生成manifest.json并使用它来将块注入到已经存在的 HTML 文件中。

Sji*_*iep 3

我最终通过创建一个脚本解决了这个问题,该脚本使用manifest.json(由 生成WebpackManifestPlugin)生成一个runtime.js脚本,该脚本将在页面加载时动态加载块并将其插入runtime.jsindex.html. 这可以从npm scripts使用任务文件npm 包的部分调用。

在 webpack 配置中,将插件添加到插件数组中:

{
  // your other webpack config
  plugins: [
    new ManifestPlugin(),
    // other webpack plugins you need
  ],
}
Run Code Online (Sandbox Code Playgroud)

我有以下外部 JS 文件,可以使用npm scripts任务文件 npm 包调用该文件,该文件配置为调用此函数:

// The path where webpack saves the built files to (this includes manifest.json)
const buildPath = './build';
// The URL prefix where the file should be loaded
const urlPrefix = 'https://www.yourdomain.com';

function buildRuntime() {
  const manifest = require(`${buildPath}/manifest`);
  // Loop through each js file in manifest file and append as script element to the head
  // Execute within an IIFE such that we don't pollute global namespace
  let scriptsToLoad = Object.keys(manifest)
    .filter(key => key.endsWith('.js'))
    .reduce((js, key) => {
      return (
        js +
        `
        script = document.createElement('script');
        script.src = urlPrefix + "/js/${manifest[key]}";
        document.head.appendChild(script);`
      );
    }, `(function(){var script;`);
  scriptsToLoad += '})()';

  // Write the result to a runtime file that can be included in the head of an index file
  const filePath = `${buildPath}/runtime.js`;
  fs.writeFile(filePath, scriptsToLoad, err => {
    if (err) {
      return console.log('Error writing runtime.js: ', err);
    }
    console.log(`\n${filePath} succesfully built\n`);
  });
}
Run Code Online (Sandbox Code Playgroud)

该函数基本上循环遍历manifest.json.
然后使用这些条目作为src属性创建脚本标记,然后将这些脚本标记添加到子document.head项中(触发条目的加载)。最后这个脚本被保存到一个runtime.js文件中并存储在构建目录中。

现在,您可以将此runtime.js文件包含到 html 文件中,如果所有路径设置正确,则应该加载块。