16

最终编辑

tl;博士的解决方案是这是不可能的。尽管下面的最佳答案确实有一些很好的信息。


考虑下面的代码,来自contacts.js. System.import这是一个动态加载的模块,在代码中的其他地方按需加载。

如果SharedUtil1也用于其他动态加载的模块System.import我将如何从所有这些模块中SharedUtility1 排除,并且仅在第一次需要时按需加载?

顶级的System.import不起作用SharedUtil1,因为我的导出依赖于它:导出只能放在模块代码的顶级,而不是任何类型的回调中。

这对 Webpack 可行吗?我正在使用 2.0.7 测试版。

import SharedUtil1 from '../../SharedUtilities/SharedUtility1';

class Contacts{
    constructor(data){
        this.data = data;
        this.sharedUtil1 = new SharedUtil1();
    }
}

export default Contacts;

更新 1

我认为捆绑加载器是我想要的,但不,它会将您导入的模块变成一个不同的函数,一旦异步加载完成,您就可以通过回调调用该函数以访问实际模块。这意味着您不能透明地使模块 X 异步加载而不对代码进行重大更改,更不用说您回到最初描述的问题,如果您的顶级模块依赖于 now-asynchronously加载的依赖项,没有办法导出它,因为导出必须在顶层。

Webpack 中是否没有办法表示依赖项 X 将按需加载(如果需要),并且有任何导入的模块将其导入以透明地等待导入过程?我认为这个用例对于任何远程大型应用程序来说都是必要的,所以我不得不认为我只是错过了一些东西。

更新 2

根据彼得的回答,我试图让重复数据删除工作,因为他提到的 commonChunk 插件涉及在端点之间共享代码,并且因为require.ensure将加载的代码放入回调中,从而阻止您使用 ES6export依赖它的任何代码。

至于重复数据删除,contacts.js并且tasks.js都像这样加载相同的 sharedUtil

import SharedUtil1 from '../../sharedUtilities/sharedUtility1';

我尝试将 webpack 运行为

webpack --optimize-dedupe

并且还通过添加

plugins: [
    new webpack.optimize.DedupePlugin()
]

到 webpack.config。在这两种情况下,虽然 sharedUtil 代码仍然放置在联系人和任务包中。

4

4 回答 4

15

看完你的博文,我终于明白你的意思了。我对“顶级依赖项”这个词有点困惑。

您有两个模块 (async-aasync-b),它们从任何地方(这里是模块)按需加载,main并且都对共享模块 ( shared) 有引用。

- - -> on-demand-loading (i. e. System.import)
---> sync loading (i. e. import)

main - - -> async-a ---> shared
main - - -> async-b ---> shared

默认情况下,webpack 创建一个块树,如下所示:

---> chunk uses other chunk (child-parent-relationship)

entry chunk [main] ---> on-demand chunk 1 [async-a, shared]
entry chunk [main] ---> on-demand chunk 2 [async-b, shared]

shared当<或同一用户同时使用和async-a/b的概率很低时,这很好。这是默认设置,因为它是最简单的行为,可能是您所期望的:一个=> 一个块。在我看来,这也是最常见的情况。async-aasync-bSystem.import

但是如果shared>=并且用户加载andasync-a/b的概率很高,则有一个更有效的分块选项:(有点难以想象):async-aasync-b

entry chunk [main] ---> on-demand chunk 1 [async-a]
entry chunk [main] ---> on-demand chunk 2 [async-b]
entry chunk [main] ---> on-demand chunk 3 [shared]

When main requests async-a: chunk 1 and 3 is loaded in parallel
When main requests async-b: chunk 2 and 3 is loaded in parallel
(chunks are only loaded if not already loaded)

这不是默认行为,但有一个插件可以将其存档:CommonChunkPlugin异步模式。它在一堆块中找到公共/共享模块并创建一个包含共享模块的新块。在异步模式下,它确实将新块与原始(但现在更小)块并行加载。

new CommonsChunkPlugin({
    async: true
})

// This does: (pseudo code)
foreach chunk in application.chunks
  var shared = getSharedModules(chunks: chunk.children, options)
  if shared.length > 0
    var commonsChunk = new Chunk(modules: shared, parent: chunk)
    foreach child in chunk.children where child.containsAny(shared)
      child.removeAll(shared)
      foreach dependency in chunk.getAsyncDepenendenciesTo(child)
        dependeny.addChunk(commonsChunk)

请记住,CommonsChunkPlugin 有一个minChunks选项来定义模块何时被线程化shared(随意提供自定义函数来选择模块)。

这是一个详细解释设置和输出的示例:https ://github.com/webpack/webpack/tree/master/examples/extra-async-chunk

另一个配置更多的:https ://github.com/webpack/webpack/tree/master/examples/extra-async-chunk-advanced

于 2016-04-13T06:37:12.813 回答
11

如果我对您的理解正确,您希望防止在不同的代码块将其声明为依赖项时多次加载相同的依赖项。

是的,这是可能的;如何做到这一点取决于应用程序中的上下文以及它是在 ES6 还是 ES5 中。

ECMA 脚本 5

Webpack 1 是在 ECMA Script 5 中构建的,通常使用 CommonJS 或 RequireJS 语法进行模块导出和导入。使用此语法时,可以使用以下功能来防止重复代码:

  • 重复数据删除通过创建重复函数的副本而不是重新定义它们来防止重复文件包含在编译代码中。
  • 命名块允许将块声明为依赖项但不立即评估;相同块的所有出现都将使用相同的实例。
  • CommonsChunkPlugin允许跨多个入口点共享一个块(仅适用于多页网站)。

重复数据删除

webpack 文档

如果你使用一些带有很酷的依赖树的库,可能会出现一些文件是相同的。Webpack 可以找到这些文件并对其进行重复数据删除。这可以防止在你的包中包含重复的代码,而是在运行时应用函数的副本。它不影响语义。

重点是我的,不是来自源头

如文档所述,代码拆分保持不变;每个需要sharedUtil1的模块都应该require正常声明。为了防止多次加载相同的依赖项,启用了 webpack 设置,该设置会导致 webpack 在运行时包含文件之前显式检查文件是否重复。

此选项通过 --optimize-deduperesp 启用。new webpack.optimize.DedupePlugin()

命名块

webpack 文档

require.ensure函数接受一个额外的第三个参数。这必须是一个字符串。如果两个分割点传递相同的字符串,它们使用相同的块...... require.include如果一个模块位于多个子块中,这可能很有用。父块中的Arequire.include将包含该模块,而子块中的模块实例将消失。

简而言之,模块的加载被延迟到编译的后期。这允许在包含重复定义之前将其剥离。该文档提供了示例。

通用块插件

webpack 文档

CommonsChunkPlugin可以将出现在多个条目块中的模块移动到新的条目块(公共块)。运行时也被移动到公共块。这意味着旧条目块现在是初始块。

这对于在多个页面之间共享块非常具体,在其他情况下不相关。

ECMA 脚本 6

对高级模块导入功能的支持是......正在进行中的工作。要了解事物的位置,请参阅以下链接:

这是 ES6 模块和 webpack 的一个很好的总结:ES6 Modules with TypeScript and Webpack

上述信息很可能很快就会过时。

建议

为了您自己的理智,我建议:

如果优化很重要:当 Webpack 2 稳定并发布时,恢复为 CommonJS / RequireJS 语法并升级到 ECMA Script 6。

如果ECMA Script 6 语法很重要:使用标准的 ECMA Script 6 导入导出格式并在它们可用时添加优化功能。

在 sill 不稳定的 webpack 2 中尝试和使用高级模块加载功能实在是太多了。等待事情稳定下来,并在尝试之前等待一些非常好的插件可用。

于 2016-02-23T03:27:40.843 回答
1

根据 Webpack 创建者,这是不可能的。干净利落。有关 Webpack 和 ES6 的大量其他有用信息,请参阅 Peter 的回答。

粘贴的图像是误解的结果。请参阅上面相同用户的答案。

在此处输入图像描述

于 2016-02-24T20:34:20.933 回答
0

System.import已在 Webpack 中弃用。Webpack 现在更倾向于import()需要 polyfill 来实现 promise。

代码拆分 - 使用 import()

于 2017-03-09T15:48:33.890 回答