Webpack 2 - 代码拆分顶级依赖项

Ada*_*kis 16 javascript webpack webpack-2

最终编辑

tl; dr决议这是不可能的.虽然下面的最佳答案确实有一些很好的信息.


考虑下面的代码,来自contacts.js.这是一个动态加载的模块,可以根据需要加载System.import到代码中的其他位置.

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

顶级System.importSharedUtil1都不行,因为我的出口依赖于它:出口只能被放置在一个模块的代码顶层,没有任何形式的回调.

Webpack可以实现吗?我的版本是2.0.7测试版.

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

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

export default Contacts;
Run Code Online (Sandbox Code Playgroud)

更新1

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

Webpack中是否有办法表示依赖加载依赖关系X(如果需要),并且有任何导入的模块导入它以透明地等待导入过程?我认为这个用例对于任何远程大型应用程序都是必不可少的,因此我不得不认为我只是遗漏了一些东西.

更新2

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

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

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

我试过运行webpack

webpack --optimize-dedupe

并且还通过添加

plugins: [
    new webpack.optimize.DedupePlugin()
]
Run Code Online (Sandbox Code Playgroud)

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

Tob*_* K. 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
Run Code Online (Sandbox Code Playgroud)

默认情况下,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]
Run Code Online (Sandbox Code Playgroud)

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

但是,如果shared> = async-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)
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

请记住,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


Mar*_*vin 11

如果我已经正确理解了您,那么当不同的代码块将其声明为依赖项时,您希望防止多次加载相同的依赖项.

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

ECMA脚本5

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

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

重复数据删除

webpack文档:

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

重点是我的,而不是来源

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

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

命名大块

webpack文档:

require.ensure函数接受另外的第3个参数.这必须是一个字符串.如果两个分割点传递相同的字符串,则它们使用相同的块... require.include如果模块位于多个子块中,则非常有用.require.include父节点中的A 将包括模块,子块中的模块实例将消失.

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

常见的块插件

webpack文档:

CommonsChunkPlugin可以移动发生在多个条目组块到一个新的条目块(公地块)模块.运行时也会移动到commons块.这意味着旧的条目块现在是初始块.

这非常特定于在多个页面之间共享块,这在其他情况下是不相关的.

ECMA脚本6

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

以下是ES6模块和webpack的一个很好的总结:带有TypeScript和Webpack的ES6模块

以上信息很可能会过时.

建议

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

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

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

尝试在sill不稳定的webpack中使用高级模块加载功能有太多的变化2.等待事情稳定下来,一些非常好的插件在尝试之前变得可用.