Webpack 4 如何链接被分割的代码块?

7

问题

我有一个使用Webpack 4的项目,想要启用代码拆分。为了启用拆分功能,我有以下配置。

  optimization: {
    splitChunks: {
      chunks: 'all'
    },
  }

当我运行构建时,会得到一些文件名:
about-d434910cfbfb3b1f4f52.js
billing-d434910cfbfb3b1f4f52.js
login-d434910cfbfb3b1f4f52.js
vendors~about~billing~login~~97fa390a-d434910cfbfb3b1f4f52.js

about.js, billing.jslogin.js 是我的入口点。这三个页面都会使用 jQuery,而它们的依赖文件则包含在 vendor 文件中。据我理解,在模板中需要写入两个 script 标签,如下:

<script type="text/javascript" src="http://0.0.0.0:8000/assets/bundles/vendors~about~billing~login~~97fa390a-d434910cfbfb3b1f4f52.js" ></script>
<script type="text/javascript" src="http://0.0.0.0:8000/assets/bundles/about-d434910cfbfb3b1f4f52.js" ></script>

这是有道理的,但我不清楚如何知道哪些供应商文件应该连接到哪个入口文件。在这种情况下,只有一个供应商文件,但我的项目有更多的依赖关系,实际上可能会有3个以上,更不用说随着代码的变化而改变。

与Require.js的比较

在 Require.js 中,您可以这样做:

define(['jquery', 'my-module'], function($, myModule) {
  ...
});

在 Webpack 4 中,jQuery 将从服务器获取。

import $ from 'jquery';
import MyModule from 'my-module';

但是,在Webpack中这样做时,您必须将依赖捆绑包列为脚本标记。当导入时,它不会去下载jQuery。

Webpack 动态导入

我知道Webpack有动态导入功能,它返回一个Promise:

import('lodash').then(_ => {
    ...
});

这类似于Require.js,但是旨在隔离应用程序功能。这不是我想要做的事情。也许如果import()可以接受多个模块名称并且我只使用它来导入所有内容,那么它可能有效,但根据文档,这绝对不是该功能的预期用法。

问题

1)您如何以编程方式知道哪些文件依赖于哪些文件,以便您可以呈现正确的脚本标签?

2)是否有一种方法可以使用import获取AMD下载模块时执行的行为,当模块尚未链接到页面中时?

2个回答

3

2
这并不是“答案”,因为它只适用于您拥有静态HTML文件的情况。例如,在SSR实现中就不适用。 - bradleygriffith
是的,您说得没错,单独使用HtmlWebpackPlugin实际上基本上意味着静态页面。如果您正在使用另一个系统来渲染服务器端页面,则我发现一个有用的技巧是解析Webpack生成的HTML文件,然后提取脚本标记并将其插入到您系统的模板或渲染器中。在仅使用Webpack的情况下,HtmlWebpackPlugin是正确的答案。进一步的集成超出了Webpack的范围。为此,您需要使用一些插件粘合代码或编写自己的代码。 - hekevintran

1
如果由于某种原因您无法使用HtmlWebpackPlugin,例如在我的情况下,我正在使用PHP并完全在后端生成HTML,则可以使用webpack-manifest-plugin并读取生成的清单以编程方式仅链接所需文件。
请注意,您只会加载一些初始JavaScript(在我的情况下是入口点生成的JavaScript),但随后Webpack会负责链接动态导入生成的文件。
为了生成一个带有入口点的清单文件,您可以使用当前在Create React App中使用的webpack配置:https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js#L672,顺便提一下,他们还在注释中给出了一些关于这个特定用例的提示:
// 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
这里是配置内容,以防将来更改:
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');

然后在plugins数组中:
new WebpackManifestPlugin({
  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,
    };
  },
}),


请注意,如果您的入口点具有不同的名称,例如在我的情况下,我必须将其更改为entrypoints.app,则entrypoints.main部分将无法工作。
生成的清单文件将具有以下结构:
{
  "files": {
    "app.js": "/js/app.9cdce93ada164d829885.js",
    // [more generated files, including the ones generated by dynamic imports]
  },
  "entrypoints": [
    "css/7741.bdbd58131904790cf1ce.css",
    "js/app.9cdce93ada164d829885.js"
    // [other files, for example the runtime file and/or the vendor file]
  ]
}

你需要将资产与所使用的语言链接起来。你可能需要将 CSS 文件与 JS 文件分开,以便以正确的方式链接它们。
在我的情况下,我在 PHP 中创建了一个名为 app_assets() 的方法,该方法将扩展作为参数并返回资产,因此例如在我的 blade 模板中,我可以像这样处理 CSS:
@foreach(app_assets('css') as $link)
  <link rel="stylesheet" href="{{secure_asset($link)}}" type="text/css" />
@endforeach

这是 JavaScript 的代码:
@foreach(app_assets('js') as $link)
  <script src="{{secure_asset($link)}}" type="module"></script>
@endforeach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接