Javascript 让闭包编译器和Node.js发挥作用
有没有同时使用node.js和closure编译器(简称CC)的项目 CC的官方建议是将应用程序的所有代码一起编译,但当我编译一些简单的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()函数,但如
require(“./MyLib.js”)
,这一行直接放在输出中,但在这种上下文中没有任何意义
我看到一些选择:
选项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)