【问题标题】:Lazy loading of modules in NodejsNodejs中模块的延迟加载
【发布时间】:2015-08-07 06:59:11
【问题描述】:

我的第一个问题是:谁负责处理 Nodejs 应用程序中的require 语句?是节点本身吗?还是 CommonJS ?还是 RequireJS ? CommonJS 是否包含在 Node 中? RequireJS 呢?

现在我的第二个问题:

我有一个 if-else 语句,它决定我们是在服务器端还是客户端呈现。我想在客户端或服务器端渲染时加载不同的库。是否可以在运行时加载模块?正是在需要它的那一刻?

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

看起来 Node 在启动应用程序之前加载了所需的一切。因此,如果您在代码顶部或 if-else 块中需要它并不重要。

【问题讨论】:

  • #1:Node.js。 Node 不使用 RequireJS。它们的相关性仅在于两者都有一个名为require() 的函数,但具有类似于Java 和JavaScript 的相似性——实际上只是在名称上。而且,CommonJS 是一组规范,包括require() 函数应该 的行为方式。 Node.js 将是该规范的实现。

标签: node.js requirejs commonjs


【解决方案1】:

在 Node.js 中,Node 本身处理 require。你错了——require 在程序的评估达到它之前不会被评估。如果你有这个代码:

var mod;

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

...您可以确定两件事:1. 模块 b 永远不会被加载,2. 模块 a 直到五秒后才会加载。

在浏览器中,require 仅在您使用定义它的库时才定义,例如 RequireJS、Browserify 或 Webpack。一般来说,这些工具与 Node 的行为很接近:虽然浏览器可能会一次下载所有代码(特别是如果您有一个将所有模块放入单个文件的构建步骤),它们会将每个模块包装在一个函数中,因此在required 之前不会对其进行实际评估。

如果您想根据您的代码是在客户端还是在服务器上运行来加载不同的模块,我建议您在构建步骤中执行此操作 - 大多数构建工具(如上面提到的那些)都具有此功能,或者它可以作为一个插件——而不仅仅是一个if 语句,因为使用if 语句你仍然在让浏览器下载它永远不会使用的代码。

【讨论】:

    【解决方案2】:

    记住 nodeJs 是被解释的。所以他只是做代码中的任何事情,而不是取决于它是什么!......无论你在哪里做你的“require(something)”,它都会执行正常并且除非你有一个sintax,否则不会抛出错误错误或者你没有安装你需要的库。

    所以你可以随心所欲地做!你可以在 If 语句中要求包。您必须考虑的是,在这些 IF 之后遵循正确的方法,因为如果您尝试使用从未导入的库,您将收到运行时错误。

    干杯!

    【讨论】:

    • 假设我在客户端运行此代码(因此它不应该需要 A)。在 B 中有一个自调用函数,它唯一要做的就是抛出一个错误。我们期望的是不会收到错误。但我收到一个错误。
    • 我认为你正在混合两种不同的东西。为什么你会有相同的代码,在服务器上运行的东西和在客户端运行的东西?我猜当您说客户端时,您的意思是浏览器对吗?如果是这种情况,浏览器不运行 nodejs,因此在客户端执行“要求”将不起作用,浏览器只运行简单的 javascript(仅供参考)..
    【解决方案3】:

    覆盖.js 文件扩展名以从目录循环中隐藏.js 文件,这在默认情况下会在调用require 时发生,并创建以编程方式按需调用require 的自定义方法:

    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');
    });
    

    包装分配给require调用的变量的访问器:

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

    参考文献

    【讨论】:

      【解决方案4】:

      有两种方法可以选择性地要求某些东西:

      选项 1 - 在块/if 语句内

      在您的代码中,这将起作用:

      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
      

      其他代码可能会在客户端打包,但永远不会执行。

      选项 2 - 创建一个使用 Lazy require ninjary 的库

      创建一个名为 library_of_libraries.js 的新库:

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

      现在,在您的其他代码中,当您需要该库时,它将按需加载。

      const LibraryOfLibraries = require("library_of_libraries");
      
      LibraryofLibraries.A.someFunc();  // Library B is never loaded
      

      感谢@Paul Sweatte 的回答,他带领我走上了正确的道路。

      【讨论】:

        猜你喜欢
        • 2018-05-22
        • 2017-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多