Lazy loading of modules in Nodejs

2019-06-28 02:24发布

问题:

My first question is : Who is responsible to handle require statements in a Nodejs application ? is it Node itself ? or CommonJS ? or RequireJS ? Is CommonJS included in Node ? what about RequireJS?

Now my second question :

I have an if-else statement , which decides if we are rendering on server side or client side. I want to load different libraries when it's rendering on client side or server side. Is it possible to load the modules in runtime ? exactly at the moment it's required ?

    if (typeof window === undefined){
       var serverSideLibrary = require('A');
       //.... 
    }else{
       var clientSideLibrary = require('B');
    }

It looks like Node loads everything required before starting the application. So it's not important if you require it at the top of the code or in that if-else block.

回答1:

In Node.js, Node itself handles require. And you're mistaken—a require is not evaluated until the program's evaluation reaches it. If you have this code:

var mod;

setInterval(function() {
  if (true) {
    mod = require("a");
  } else {
    mod = require("b");
  }
}, 5000);

...you can be sure of two things: 1. Module b will never be loaded, and 2. Module a won't be loaded until five seconds have elapsed.

In the browser, require is only defined if you use a library that defines it, like RequireJS, Browserify or Webpack. In general these tools stay close to Node's behavior: While the browser might download all of the code at once (especially if you have a build step that puts all of your modules into a single file), they will wrap each module in a function so that it won't actually be evaluated until it's required.

If you want to load different modules depending on whether your code is running on the client or the server, I would recommend doing this in your build step—most build tools, like those mentioned above, have this functionality or it's available as a plugin—instead of just an if statement, because with the if statement you're still making the browser download code it's never going to use.



回答2:

Override the .js file extension to hide .js files from the directory loop which happens by default when require is called, and create custom methods which programmatically call require on demand:

var fs = require('fs'),
    IonicAppLib = module.exports,
    path = require('path');

var camelCase = function camelCase(input) {
    return input.toLowerCase().replace(/-(.)/g, function(match, group1) {
        return group1.toUpperCase();
    });
};

//
// Setup all modules as lazy-loaded getters.
//
fs.readdirSync(path.join(__dirname, 'lib')).forEach(function (file) {
  file = file.replace('.js', '');
  var command;

  if (file.indexOf('-') > 0) {
    // console.log('file', file);
    command = camelCase(file);
  } else {
    command = file;
  }

  IonicAppLib.__defineGetter__(command, function () {
    return require('./lib/' + file);
  });
});

IonicAppLib.__defineGetter__('semver', function () {
  return require('semver');
});

which wraps the accessor for the variable assigned to the require call:

var IonicAppLib = require('ionic-app-lib');

References

  • Lazy loading your node modules - Josh Bavari's Ramblings

  • nodejs module.require and require

  • Auto require a directory in Node.js (Example)

  • Modules: Exports Shortcut | Node.js v7.10.0 Documentation

  • node/module.js at master · nodejs/node · GitHub



回答3:

There are two ways to optionally require something:

Option 1 - Inside a block / if statement

In your code, this will work:

let theLibary;
if (typeof window === undefined){
   theLibrary = require('A');
} else {
   theLibrary = require('B');
}
// Now use theLibrary and only the one you want will be included

The other code may get packaged on the client, but it will never be executed.

Option 2 - Create a library that uses Lazy require ninjary

Create a new library called library_of_libraries.js that does:

thisLibrary = module.exports;

// Lazy load only on usage
thisLibrary.__defineGetter__("A", function () {
    return require("A");
});

thisLibrary.__defineGetter__("B", function () {
    return require("B");
});

Now, in your other code, when you want the library, it will be loaded on demand.

const LibraryOfLibraries = require("library_of_libraries");

LibraryofLibraries.A.someFunc();  // Library B is never loaded

Credit goes to @Paul Sweatte's answer for leading me down the right path.



回答4:

Remember that nodeJs is interpreted. So he just does whatever is inside the code in order not depending what it is!... It does not matter where you do your "require(something)", it will execute ok and no error should be thrown unless you have a sintax error or you have not installed the library you are requiring.

So you can definetly do as you want!, You can require packages inside If statements. What you have to consider is that you follow the right way after those IF because if you try to use a library that was never imported you will get a RunTime error.

Cheers!