Javascript 让闭包编译器和Node.js发挥作用

Javascript 让闭包编译器和Node.js发挥作用,javascript,node.js,require,google-closure-compiler,Javascript,Node.js,Require,Google Closure Compiler,有没有同时使用node.js和closure编译器(简称CC)的项目 CC的官方建议是将应用程序的所有代码一起编译,但当我编译一些简单的node.js代码时,其中包含一个require(“./MyLib.js”),这一行直接放在输出中,但在这种上下文中没有任何意义 我看到一些选择: 将整个应用程序编码为单个文件。这样可以避免问题,但不利于维护 假设所有文件在执行之前都将被连接。同样,这避免了问题,但使实现未编译的调试模式变得更加困难 我想让CC“理解”node.js require()函数,但如

有没有同时使用node.js和closure编译器(简称CC)的项目

CC的官方建议是将应用程序的所有代码一起编译,但当我编译一些简单的node.js代码时,其中包含一个
require(“./MyLib.js”)
,这一行直接放在输出中,但在这种上下文中没有任何意义

我看到一些选择:

  • 将整个应用程序编码为单个文件。这样可以避免问题,但不利于维护
  • 假设所有文件在执行之前都将被连接。同样,这避免了问题,但使实现未编译的调试模式变得更加困难
  • 我想让CC“理解”node.js require()函数,但如果不编辑编译器本身,这可能无法实现,不是吗

  • 选项4:不要使用闭包编译器

    节点社区中的人不倾向于使用它。你不需要缩减node.js源代码,这很愚蠢

    缩小没有什么用处

    至于闭包对性能的好处,我个人怀疑它是否真的能让你的程序更快


    当然也有成本,调试编译好的JavaScript是一场噩梦

    我一直在为一个尚未发布的项目使用带有节点的闭包编译器。它需要一些工具,但它有助于捕获许多错误,并且具有相当短的编辑-重新启动测试周期

    首先,我使用(这是我创建和维护的一个项目),以便将闭包编译器、库和模板一起使用。我以闭包库的样式编写节点代码,因此每个文件定义自己的类或实用程序集合(如
    goog.array

    下一步是为要使用的节点函数创建一组外部文件。我在以下网站上公开发布了其中一些内容:

    虽然最终,我认为这应该是一个更受社区驱动的事情,因为有很多功能需要记录。(这也很烦人,因为有些节点函数有可选的中间参数,而不是最后一个参数,这使得类型注释变得复杂。)我自己还没有开始这项工作,因为我们可以使用闭包编译器做一些工作,以减少这项工作的麻烦(见下文)

    假设您已经为节点名称空间
    http
    创建了externs文件。在我的系统中,我决定在任何时候需要
    http
    ,我都会通过以下方式将其包括在内:

    var http = require('http');
    
    虽然我的代码中没有包含
    require()
    调用。相反,我使用闭包编译器的
    output wrapper
    功能,在文件的开头预先添加所有的
    require()
    s,在plovr中声明时,在我当前的项目中如下所示:

    "output-wrapper": [
      // Because the server code depends on goog.net.Cookies, which references the
      // global variable "document" when instantiating goog.net.cookies, we must
      // supply a dummy global object for document.
      "var document = {};\n",
    
      "var bee = require('beeline');\n",
      "var crypto = require('crypto');\n",
      "var fs = require('fs');\n",
      "var http = require('http');\n",
      "var https = require('https');\n",
      "var mongodb = require('mongodb');\n",
      "var nodePath = require('path');\n",
      "var nodeUrl = require('url');\n",
      "var querystring = require('querystring');\n",
      "var SocketIo = require('socket.io');\n",
      "%output%"
    ],
    
    这样,我的库代码从不调用节点的
    require()
    ,但编译器允许在我的代码中使用
    http
    之类的东西,因为编译器将它们识别为外部。因为他们不是真正的外人,所以必须像我描述的那样做好准备

    最后,在讨论了这一点之后,我认为更好的解决方案是为名称空间提供一个新的类型注释,该注释类似于:

    goog.scope(function() {
    
        /** @type {~NodeHttpNamesapce} */
        var http = require('http');
    
        // Use http throughout.
    
    });
    
    在这种情况下,externs文件将定义
    NodeHttpNamespace
    ,以便闭包编译器能够使用externs文件对其上的属性进行类型检查。这里的区别在于,您可以根据需要命名
    require()
    的返回值,因为
    http
    的类型就是这种特殊的名称空间类型。(为
    $
    标识一个“jQuery名称空间”也是一个类似的问题。)这种方法将消除为节点名称空间统一命名局部变量的需要,并将消除在plovr配置中使用巨型
    输出包装器的需要

    但这是一个离题……一旦我按照上述方式设置好了内容,我就有了一个shell脚本:

  • 使用plovr在
    RAW
    模式下构建所有内容
  • 在plovr生成的文件上运行
    节点

  • 使用
    RAW
    模式会导致所有文件的大量串联(尽管它还负责将Soy模板甚至CoffeeScript转换为JavaScript)。诚然,这让调试变得很痛苦,因为行号毫无意义,但到目前为止对我来说已经足够好了。闭包编译器执行的所有检查都是值得的。

    闭包编译器的svn负责人似乎已经用一种更简单的方法取代了我的旧方法:

    新方法

    • 不需要()调用我自己的应用程序代码,仅针对节点模块
    • 我需要将服务器代码连接到单个文件,然后才能运行或编译它
    • 连接和编译是使用
    有趣的是,我甚至不必为
    require()
    调用添加外部程序。谷歌闭包编译器自动理解这一点。我不得不这么做

    旧方法

    根据OP的要求,我将详细介绍使用Google Closure编译器编译node.js代码的方法

    我的灵感来自于bolinfest解决问题的方式,我的解决方案使用了相同的原则。不同之处在于,我制作了一个node.js脚本,它可以完成所有事情,包括内联模块(bolinfest的解决方案让GCC负责)。这使得它更加自动化,但也更加脆弱

    我只是在编译服务器代码的每个步骤中添加了代码注释。请参阅此提交:

    总结如下:

  • 我从我的主模块开始,当我想运行它时,我会传递给节点的JS文件。
    在我的例子中,这个文件是
  • 在这个文件中,我使用正则表达式检测所有
    require()
    调用,包括赋值部分。
    在start.js中,这与一个require调用相匹配:
    var Server=require('./l)