Require.ensure()非阻塞

时间:2016-08-17 13:04:30

标签: javascript webpack amd commonjs

如果我们有 webpack 创建的不同捆绑包,并且我们require.ensure要在以后的某个时间动态传输+ eval ,则会通过< em> jsonPadding 和一些webpack js魔术。如果我们有

require.ensure([ ], ( require ) => {
    console.log('before...');
    var data = require( './myModule.js' );
    console.log('after...');
  }, 'myModule')
完全转移和评估该模块时会遇到

"after..."。如果碰巧是这个 / 模块非常大,包含图像,css和诸如此类的东西,那么当webpack javascript代码解包时,加载将几乎锁定浏览器包含其所有组件的包。

问题:有没有办法“挂钩”require魔法?例如,对于以下内容进行回调将是一个梦想的场景:

  • 整个文件/块已转移
  • 图像[1]已经过评估
  • css [1]被评估/样式标签被注入
  • 评估了javascript

依此类推,假设我们传输的包中包含大量数据。一般来说,让我很难有一个很好的选择来动态地异步传输整个bundle,但仍然必须以完全同步/阻塞的方式加载那个bundle。

4 个答案:

答案 0 :(得分:4)

  

让我先说我知道这可能是一个'烦人'的答案,因为它没有直接回答你的问题而是提供了一个   另类,务实,解决浏览器悬挂问题。一世   我自己使用这种模式来管理上下文中的资产加载   一个沉重的3D网页游戏。

     

我写这个是一个答案,而不是一个评论,所以它可能会起作用   其他遇到同样问题的人。如果这确实回答你的   case,我很乐意提供实际的代码来实现和生成   这些模块。

如果我理解正确,基本上你想要的是一种将MyModule分解成离散组件的方法,这些组件可以在一个require.ensure的上下文中进行原子加载和评估,但是处理评估以便不一切都进行评估,导致浏览器挂起。

另一种看待这种情况的方法是使用requireensure方法本身作为加载/评估机制。考虑MyModule.js,这是一个包含依赖项Css1, Css2, ... CssN以及JS1, JS2, ... JSN和图片的大型加载模块。

我的建议是将其细分为SuperMyModule.js,这需要MyModuleLogic.js以及所有CSS,图片和JS。

节点,您可以在SuperMyModule.js中执行:

let myModuleLogic = require("myModuleLogic");
console.log('JS was evaluated');

require.ensure(['image1.png'], ( require ) => {
    let data = require( './images1.png' );
    console.log('image[1] was evaluated');
    // register that resource was evaluated/fire event
})
require.ensure(['style1.css'], ( require ) => {
    let data = require( './style1.css' );
    console.log('css[1] was evaluated');
    // register that resource was evaluated/fire event
})

//after all resources evaluated/fire callback or event

然后在原始文件中,就像您要求的那样:

require.ensure([ ], ( require ) => {
    console.log('before...');
    let myModule = require( './superMyModule.js' );
    console.log('after...');
  })

如果你将模块实例设置为事件发射器,可能会挂钩加载资源,如下所示:

require.ensure([ ], ( require ) => {
    let myModule = require( './superMyModule.js' );
    myModule.on("loadResource", myCallback)
  })

答案 1 :(得分:1)

我想我自己对这个话题感到困惑,所以我的问题可能不够精确,无法得到适当的回答。但是,我对整个“commonJS动态模块加载”上下文的误解是,require.ensure()只会传输模块代码(分别是webpack创建的 Chunk )通过电线。之后,传输的 Chunk 基本上只是一个大的ECMAscript文件,就在浏览器中,缓存但尚未评估。对整个 Chunk 的评估仅在实际的require()调用中发生。

说完了,你完全掌握了如何解耦和评估模块 / Chunk 的各个部分。例如,在我的原始问题中,某些 CSS文件中的模块requires(),某些图像和某些 HTML ,所有这些都在require.ensure()电话上异步转移。以require()(以及评估)这些部分的方式完全取决于您,您可以在必要时自行解除这些调用。

例如,模块看起来像这样:

<强> Module1.js

"use strict";
import { io } from 'socket.io-client';

document.getElementById( 'foo' ).addEventListener('click', ( event ) => {
    let partCSS = require( 'style/usable!./someCoolCSS.css' ),
        moarCSS = require( 'style/usable!./moarCoolCSS.css' ),
        tmpl    = require( './myTemplate.html' ),
        image1  = require( './foo.jpg' ),
        image2  = require( './bar.png' );
}, false);

当然,当其他一些模块调用时,所有这些文件都已包含在传输到客户端的Chunk中:

require.ensure([ 'Module1.js' ], ( require ) => {
}, 'Module1');

这是我的困惑。现在,我们可以在require()内自己进行module1.js次调用。如果我们确实需要大量文件,我们甚至可以使用setTimeout / setImmediate失控定时器来解除每个require()调用之间的同步评估(如果必要或需要)。

对于一个非常简单的故事实际上是一个很长的答案。

<强> TL; DR: require.ensure通过网络传输整个块。此块包含所有文件,这些文件是受保护模块中require()调用的一部分。但这些文件得到自动评估。只有在运行时匹配实际的require()调用时才会发生这种情况(此时由webpackJSONP调用表示)

答案 2 :(得分:0)

您可以卸载主线程并避免使用worker loader进行阻塞。

下行:在主窗口和工作人员之间传递了额外的消息。

https://github.com/webpack/worker-loader

您还可以尝试在大型模块中发送加载事件,以跟踪更细微的进度。

进一步参考:

答案 3 :(得分:0)

如果您想通过require.ensure以及其他Promise开始加载异步JavaScript包,请按以下步骤操作:

const requireEnsurePromise = new Promise((resolve) => {
  require.ensure(['./modulePath'], function (requireEnsure) {
    console.log('The module is fetched but not evaluated yet');
    resolve(requireEnsure.bind(null, require.resolve('./modulePath')));
  });
});

Promise.all([
  fetch('/api/relevant/stuff').then(response => response.json()),
  requireEnsurePromise,
]).then((values) => {
  if (values[0]) {
    // DO STUFF
  }
  console.log('right before module is evaluated');
  const evaluatedModule = values[1]();
});

Webpack静态地确定模块路径对应于Webpack内部表示(可以是整数或字符串)。每当Webpack识别require.ensure([], fn)模式时,它都会查看fn回调的函数体并执行此操作。为了延迟以Promise方式获取JavaScript包之后的评估时间,require('./modulePath')成功回调中不能出现require.ensure,因为它将评估模块。 Webpack会将require('./modulePath')翻译为类似__webpack_require__(2343423)的内容,这就是您希望在此方案中避免使用它的原因。