从Webpack 1.x迁移到2.x

17

在 Webpack 1.x 中,我经常这样做:

require.ensure([ './mod2.js' ], ( require ) => {
    setTimeout(() => {
        // some later point in time, most likely through any kind of event
        var data = require( './mod2.js' ); // actual evaluating the code
    },1100);
}, 'myModule2');
使用这种技术,我们能够通过网络传输一个“webpack-bundle”,在稍后的某个时间点评估该捆绑包中的实际内容(JavaScript代码)。同时,使用“require.ensure”可以给捆绑包命名,在这种情况下为“myModule2”,因此当执行Webpack时,我们可以看到名称/别名。
在Webpack 2.x中,新的方法是使用“System.import”。虽然我现在喜欢收到一个Promise对象,但我有两个问题与此样式有关。相当于上述代码的等效代码如下:
System.import( './mod2.js' ).then( MOD2 => {
    // bundle was transferred AND evaluated at this point   
});
  • 现在我们如何分离转移和评估?
  • 我们仍然如何命名这个捆绑包?

Webpack文档在Github上说:

完全动态的 require 现在默认失败

只有表达式的依赖项(例如 require(expr))现在会创建一个空上下文,而不是整个目录的上下文。

最好将此代码重构,因为它无法与ES6模块一起使用。 如果不可能,您可以使用ContextReplacementPlugin提示编译器正确解析。

我不确定这在这种情况下是否起作用。他们也提到了代码拆分,但很简短,他们没有提及任何“问题”或解决方法。


1
为什么不将 Promise 储存在一个变量中,然后在其他代码片段中触发 .then 方法呢? - Claies
@Claies 我不认为那会有任何帮助。Promise 仍然会处理“两个”操作,即传输和评估。如果我们有两个单独的 Promise,那就另当别论了。 - jAndy
1个回答

5

tl;dr: 如果你想要的功能是大部分 System.resolveSystem.register 都能满足。本回答剩下的内容会解释为什么 require.ensure 不能实现,以及 System.import 如何调用其他方法。

我认为 ES6 模块阻止了这种方式的良好工作,尽管通过相关规范来跟进它是很棘手的,所以我可能完全错了。

话虽如此,我们先看几个参考资料:

  1. WhatWG module loader
  2. ES6 specification on modules (§15.2)
  3. CommonJS module specification
  4. fantastic 2ality article on ES6 modules

第一个参考资料解释了更多的行为,尽管我不完全确定它的规范性。后者解释了 JS 方面的实现细节。由于没有平台实现这个功能,所以我没有关于它在实际生活中如何工作的参考资料,我们将不得不依赖于规范。

webpack 1.x 中可用的 require 是 CommonJS 和 AMD require 的混合体。其中的 CommonJS 部分在 ref#3 中描述,特别是“模块上下文”部分。那里没有提到 require.ensure,AMD “规范”(就算它是什么样)也没有提到,所以这纯粹是 webpack 的发明。也就是说,这个功能从未真正存在过,因为它没有被某个官方和花哨的地方指定。

话虽如此,我认为 require.ensure 与 ES6 模块有冲突。调用 System.import 应该会调用来自 Loader 对象import 方法。在 ref#2 的相关部分没有明确说明,但是 §10.1 中提到了将加载器附加到 System 上。

Loader.prototype.import方法很简单,只有第4步是我们感兴趣的:
  1. 使用成功处理程序将Resolve(loader, name, referrer)进行转换,并返回结果。当使用参数key调用成功处理程序时,执行以下步骤:
    1. 让entry等于EnsureRegistered(loader, key)。
    2. 使用成功处理程序将LoadModule(entry, "instantiate")进行转换,并返回结果。当调用时,执行以下步骤:
      1. 返回EnsureEvaluated(entry)。
流程是解析-注册-加载-评估,您想在加载和评估之间断开连接。但请注意,加载阶段使用stage设置为"instantiate"来调用LoadModule。这意味着并且可能要求模块已经通过RequestTranslate进行了翻译,这样做可以完成大部分繁重的解析工作,以查找模块的入口点等。
从听起来的声音来看,这已经做了比您想要的更多的工作。由于模块加载的基础要求已知入口点,我不认为有一种方法可以避免使用System公开的调用来解析和部分评估模块。您已经知道了这一点。
问题在于,在解析之前,System.import不可能知道模块是必须评估的ES6模块还是可以延迟处理的webpack捆绑包。必须进行解析才能确定是否需要解析,从而导致鸡生蛋的问题。
到目前为止,我们一直在按照System.importLoader的路径进行操作。import调用正在指定进口的阶段,假定您希望通过完整的端到端加载过程进行操作。像Loader.prototype.load之类的底层调用为这些阶段提供精细的控制。
我不确定如何调用前两个阶段(获取和翻译),但如果您能够翻译和注册模块,则稍后的调用应该只需评估和返回它。
如果规范准确,则支持实现应通过System.loader属性公开,并具有所需的方法。您无法访问流程中的大部分部分,因此建议您不要这样做,而是设置代码,使加载模块时不运行任何重要的内容。如果这不可能,请重新创建注册流程,但在评估之前停止。

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