5

From the Webpack documentation (https://webpack.github.io/docs/api-in-modules.html#require-ensure):

Download additional dependencies on demand. The dependencies array lists modules that should be available. When they are, callback is called. If the callback is a function expression, dependencies in that source part are extracted and also loaded on demand. A single request is fired to the server, except if all modules are already available.

If dependencies in the source part are also extracted and loaded on demand, then why bother putting anything in the dependencies list?

I've seen examples like this that are very confusing (https://github.com/webpack/webpack/tree/master/examples/extra-async-chunk):

require.ensure(["./a"], function(require) {
    require("./b");
    require("./d");
});

"b" and "d" are not in the dependencies list, but will be loaded on demand just like "a". So what's the difference?

Nonemoticoner
  • 650
  • 5
  • 14
hekevintran
  • 22,822
  • 32
  • 111
  • 180

3 Answers3

2

The example in the docs you linked to shows one way that behavior differs. When you specify a dependency, it explicitly creates a new chunk, puts the dependency in it, and adds any other dependencies referenced in the callback. When you don't specify a dependency, any dependencies in the callback are added to the 'current' (last?) chunk, a new chunk is not created.

Brendan Gannon
  • 2,632
  • 15
  • 22
  • 1
    Ok, so putting a dependency in the array creates a chunk that will also contain modules imported in the callback. So in the example, the chunk will be created because "a" is in the array. That chunk will also contain "b" and "d" because those modules are imported in the callback. The question is why is the array `["./a"]` instead of `["./a", "./b", "./d"]`? It seems totally arbitrary to me. The array could have been `["./b"]` and the result would have been exactly the same. – hekevintran Mar 16 '16 at 17:19
  • 1
    A follow-up question: Why would you want to put a dependency in the array and then in the callback not import it at all? In the example "a" is in the array, but is not imported in the callback. In what situation would you want to require something that you don't use? – hekevintran Mar 16 '16 at 17:21
  • 1
    For your first question, that kind of makes sense to me: you want 'a' to only be loaded async. But once it's loaded you also need 'b' and 'd' -- however, they might be already bundled elsewhere; it doesn't matter to you where those modules live. For your second question, not sure -- you want to load something async but not execute it yet. I guess there are use cases for that, but I can't really think of anything. – Brendan Gannon Mar 16 '16 at 17:33
  • That must be what the docs mean. If correct then the docs are horribly written. Also it seems totally pointless that the author should think about whether to load a module async or not based on where it is located since the point of a build tool like Webpack is to automate such things. If a module is available it should just be used. If it is not available, then it should be loaded as needed. – hekevintran Mar 16 '16 at 17:39
  • 1
    "If a module is available it should just be used. If it is not available, then it should be loaded as needed" <- that's how it works, really. So if you load 'a' async as a dependency, and then reference 'b' and 'd', b and d will only be in the async chunk if they haven't already been used elsewhere. If you've used them already, webpack will reuse those modules from the main bundle. And plugins can further change/optimize how modules/bundles are arranged. It does do a lot of automation. – Brendan Gannon Mar 16 '16 at 18:04
  • I get all of that and that automation is great. The confusion is that I see negative value in being forced to make a choice (declaring dependencies in an array vs in a callback) that doesn't matter at all. There is no reason why the code author should make a decision of whether they want a module to be loaded async or "maybe async sometimes but not really". – hekevintran Mar 16 '16 at 18:09
1

In short: you should not bother.

Origin of the problem

Tobias Koppers, author of Webpack was asked a similar question on Gitter:

Raúl Ferràs: Is there any advantage between these two forms?

require.ensure(['jquery','Backbone','underscore'], function(require){
    var jQuery = require('jquery'),
    Backbone  = require('Backbone'),
    _ = require('underscore');
};

and this

require.ensure(['jquery'], function(require){
    var jQuery = require('jquery'),
    Backbone  = require('Backbone'),
    _ = require('underscore');
};

Tobias Koppers: No difference... even the generated source is equal. But the spec says the dependency should be in the array.

Webpack's require.ensure was implemented according to the CommonJS Modules/Async/A proposal, which says:

"require.ensure" accepts an array of module identifiers as first argument and a callback function as second argument.

The spec doesn't specify if non-listed modules required in the callback will be loaded asynchronously, but has a sample code where only the module listed in the array is required:

require.ensure(['increment'], function(require) {
    var inc = require('increment').inc;
    var a = 1;
    inc(a); // 2
});

Thus, in my opinion, async loading of required but non-listed modules is a feature of Webpack and a deviation from the spec. Such feature makes the first argument pointless in most cases and raises questions.

Use cases

1. Comply with the spec

One use case for the first argument could be to specify all the dependencies for clarity and to comply with the spec. But that's completely optional.

2. Pull modules into specific chunks

Consider you have two split points in different parts of an app. The first split point depends on module a, the second depends on modules a and b. To eliminate the risk of downloading a twice, you could decide to place both modules into a single chunk:

// First split point
require.ensure(['b'], (require) => {
  require('a');
});
Vitaly Kuznetsov
  • 1,515
  • 1
  • 16
  • 15
-2

The answer is in the docs:

Download additional dependencies on demand. The dependencies array lists modules that should be available. When they are, callback is called. If the callback is a function expression, dependencies in that source part are extracted and also loaded on demand. A single request is fired to the server, except if all modules are already available.

So, if some of the dependencies from the array are not there, then webpack will stop without even calling the callback. But I never encountered a use case when this was useful, normally just passing empty array every time

somebody32
  • 179
  • 1
  • 1
  • 9
  • This answer does nothing to explain what the docs say. I understand that the callback won't run until the dependencies are satisfied. That's not the question. The question is about the difference between putting a dependency in the array and putting it in the callback but not in the array. – hekevintran Mar 16 '16 at 17:14