让closure-compiler和Node.js发挥得很好

buk*_*zor 26 javascript require google-closure-compiler node.js

是否有任何项目一起使用node.js和closure-compiler(简称CC)?

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

我看到几个选项:

  1. 将整个应用程序编码为单个文件.这通过避免它来解决问题,但是对于维护是不利的.
  2. 假设在执行之前将连接所有文件.这再次避免了这个问题,但却使实现未编译的调试模式变得更加困难.
  3. 我想让CC"理解"node.js require()函数,但如果不编辑编译器本身就可能无法完成,可以吗?

bol*_*est 50

我一直在使用Closure Compiler with Node来完成我尚未发布的项目.它采用了一些工具,但它有助于捕获许多错误,并且具有非常短的编辑 - 重启 - 测试周期.

首先,我使用plovr(这是我创建和维护的项目),以便一起使用Closure Compiler,Library和Templates.我以Closure Library的样式编写Node代码,因此每个文件都定义了自己的类或实用程序集合(如goog.array).

下一步是为要使用的Node函数创建一组externs文件.我公开发表了一些这些内容:

https://github.com/bolinfest/node-google-closure-latitude-experiment/tree/master/externs/node/v0.4.8

虽然最终,我认为这应该是一个更加社区驱动的东西,因为有很多功能需要记录.(这也很烦人,因为一些Node函数有可选的中间参数而不是最后一个参数,使得类型注释变得复杂.)我自己没有开始这个动作,因为我们可以用Closure Complier做一些工作来减少它的尴尬(见下文).

假设您已为Node命名空间创建了externs文件http.在我的系统中,我已经决定,只要我需要http,我将通过以下方式包含它:

var http = require('http');
Run Code Online (Sandbox Code Playgroud)

虽然require()我的代码中没有包含该调用.相反,我使用output-wrapperClosure Compiler 的功能,在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%"
],
Run Code Online (Sandbox Code Playgroud)

通过这种方式,我的库代码从不调用Node require(),但编译器可以容忍http我的代码中使用的东西,因为编译器将它们识别为externs.由于它们不是真正的外部因素,因此必须按照我的描述进行.

最后,在讨论列表中讨论了这个之后,我认为更好的解决方案是为命名空间创建一个类型的新类型注释:

goog.scope(function() {

    /** @type {~NodeHttpNamesapce} */
    var http = require('http');

    // Use http throughout.

});
Run Code Online (Sandbox Code Playgroud)

在这种情况下,externs文件将定义NodeHttpNamespaceClosure Compiler能够使用externs文件对其进行类型检查.这里的区别在于您可以为require()任何您想要的返回值命名,因为类型http将是这种特殊的命名空间类型.(识别"jQuery命名空间" $是一个类似的问题.)这种方法将不再需要一致地命名Node命名空间的局部变量,并且不需要output-wrapperplovr配置中的那个巨人.

但这是一个题外话......一旦我按照上面所述设置了东西,我就有了一个shell脚本:

  1. 使用plovr以RAW模式构建所有内容.
  2. 运行node由plovr生成的文件.

使用RAW模式会导致所有文件的大量连接(尽管它还负责将Soy模板甚至CoffeeScript转换为JavaScript).不可否认,这使得调试变得很痛苦,因为行号是无稽之谈,但到目前为止我一直运作良好.Closure Compiler执行的所有检查都使它值得.


Jau*_*uco 6

闭包编译器的svn HEAD似乎支持AMD


Ray*_*nos -29

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

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

缩小根本没有什么好处。

至于闭包的性能优势,我个人怀疑它实际上会让你的程序更快。

当然这是有代价的,调试已编译的 JavaScript 是一场噩梦

  • @Raynos,我不敢苟同。闭包不仅仅是为了缩小。肯定有性能优势。OP 询问如何才能做到这一点,而不是首先询问他是否应该这样做。节点社区中没有人使用它并不意味着它不应该被使用。 (25认同)
  • 我还想指出,“选项4”并不能解决问题。“不”不能回答“如何?” (13认同)
  • 仅供参考:我想使用优化和静态分析方面,而不仅仅是缩小。 (7认同)
  • @StephenChung 这实际上听起来相当有用 (4认同)
  • @StephenChung 加速的好处很小而且微不足道。如果性能很重要,请用 C++ 重写它,而不是通过 CC 运行代码。 (3认同)
  • @StephenChung 我将其修复为更正确。我认为性能方面的好处并不值得。在浏览器代码上使用 CC 是明智的,但在服务器端代码上则不然。 (2认同)