Leo Jiang
Leo Jiang

Reputation: 26085

Checking if a module is already loaded in Webpack?

I worked at a company that has a custom JS module bundler. The implementation has a function requireIfLoaded that allows you to require a module, but only if it has already been loaded. If the module isn't loaded yet, it throws an error. Using requireIfLoaded doesn't bundle the module. This drastically reduced our file size.

Here's an example of how it can be useful:

if (page === PROFILE) {
  // ProfileHelper should already be downloaded if we're on the profile page.
  const ProfileHelper = requireIfLoaded('ProfileHelper');
  ProfileHelper.doSomething();
} else if (page === FEED) {
  // FeedHelper should already be downloaded if we're on the feed page.
  const FeedHelper = requireIfLoaded('FeedHelper');
  FeedHelper.doSomething();
}

A separate bundle is generated for the profile page and for the feed page. require('ProfileHelper') isn't called in the feed page codepaths, so ProfileHelper isn't included in the feed bundle. require('FeedHelper') isn't called in the profile page codepaths, so FeedHelper isn't included in the profile bundle. Does Webpack have something like this?

Edit for clarification:

If I required both ProfileHelper and FeedHelper all the time, then one of the modules will be unused. At most one of them is loaded on any given page. On the profile page, ProfileHelper is loaded, but not FeedHelper. Vice-versa for the feed page.

Also, I don't want to use require.ensure because it's async.

Upvotes: 17

Views: 2303

Answers (4)

Sachintha Nayanajith
Sachintha Nayanajith

Reputation: 721

I found a documentation which explains this. it says "Despite the various options for loading JavaScript, there is still no way to download a JavaScript file and set it to execute at an arbitrary time. You can say execute immediately, or you can defer until the DOM document is complete, but you can’t specify any other point in time to execute the code. "

for more information Seperating javascript download and installation

Upvotes: 0

Avraham
Avraham

Reputation: 938

From your question and your comments it seems that ProfileHelper is loaded via a separate <script> tag, and is available under the window scope for immediate use, with no further asynchronous loading.

Also this helpers do not appear to be managed by requireIfLoaded, and if they aren't already loaded it will throw.

So, under these assumptions, the role of requireIfLoaded is just to check if the module is available under window, and throw an error if it doesn't.

So... why not create your own requireIfLoaded?

function requireIfLoaded(file) {
  let m = window[file];
  if(m) {
    return m;
  } else {
    throw new Error(`Cannot find module '${file}'`)
  }
}

Did I miss something?

Upvotes: 0

shadymoses
shadymoses

Reputation: 3443

This approach is antithetical to bundling with webpack.

The require statements are the very thing that instruct webpack on what to compile. It won't ever compile your conditional requires because those will be evaluated at runtime (but webpack bundles are pre-compiled) and webpack won't have added any of them into your bundles.

What you are looking for is code-splitting: https://webpack.js.org/guides/code-splitting-require/

Tutorials:

Upvotes: 3

Ambroos
Ambroos

Reputation: 3476

Webpack will deduplicate all modules required multiple times, and modules that are already loaded will not be initialized again (following the CommonJS spec). So, basically, just require all your dependencies directly and you're set!

More importantly: don't wrap your requires if you use webpack. The static analysis to determine what modules you are actually using will stop working accurately, and webpack will bundle too much.

Upvotes: 4

Related Questions