如何在Node.js和浏览器之间共享代码?

我正在用JavaScript客户端(在浏览器中运行)和Node.js服务器创build一个小应用程序,使用WebSocket进行通信。

我想分享客户端和服务器之间的代码。 我刚刚开始使用Node.js,至less可以说,我对现代JavaScript的了解有点生疏。 所以我仍然对CommonJS的require()函数感到满意。 如果我使用“导出”对象创build我的包,那么我不能看到如何在浏览器中使用相同的JavaScript文件。

我想创build一组两端使用的方法和类,以方便编码和解码消息以及其他镜像任务。 但是,Node.js / CommonJS打包系统似乎无法创build可在两侧使用的JavaScript文件。

我也尝试使用JS.Class来获得更紧的OO模型,但是我放弃了,因为我无法弄清楚如何让提供的JavaScript文件与require()一起工作。 有什么我在这里失踪?

如果你想写一个模块,可以同时用于客户端和服务器端,我有一个简短的方法快速博客文章: 编写Node.js和浏览器 ,本质上是以下( this是一样的window ):

 (function(exports){ // Your code goes here exports.test = function(){ return 'hello world' }; })(typeof exports === 'undefined'? this['mymodule']={}: exports); 

另外还有一些项目旨在在客户端实现Node.js API,比如Marak的双子座 。

您可能还对DNode感兴趣,它允许您公开一个JavaScript函数,以便可以使用简单的基于JSON的networking协议从另一台机器调用它。

Epeli在这里http://epeli.github.com/piler/有一个很好的解决scheme,即使没有这个库,也可以把它放在一个名为share.js的文件中;

 (function(exports){ exports.test = function(){ return 'This is a function from shared module'; }; }(typeof exports === 'undefined' ? this.share = {} : exports)); 

在服务器端只使用:

 var share = require('./share.js'); share.test(); 

而在客户端只需加载js文件,然后使用

 share.test(); 

不要忘记,JavaScript函数的string表示代表该函数的源代码。 你可以简单地用封装的方式编写你的函数和构造函数,以便它们可以被toString()函数和发送给客户端。

另一种方法是使用构build系统,将通用代码放在单独的文件中,然后将其包含在服务器和客户端脚本中。 我正在通过WebSockets使用这种方法进行一个简单的客户/服务器游戏,其中服务器和客户端都运行基本相同的游戏循环,客户端每隔一个时钟周期同步服务器以确保没有人作弊。

我的游戏构build系统是一个简单的Bash脚本,它通过C预处理器运行这些文件,然后通过sed来清理掉一些垃圾cpp叶子,所以我可以使用所有正常的预处理器东西,如#include,#define,#ifdef等等

我build议查看Node.js的RequireJS适配器 。 问题是默认情况下使用的CommonJS模块模式Node.js不是asynchronous的,这会阻止Web浏览器中的加载。 RequireJS使用AMD模式,只要您使用r.js适配器,它既是asynchronous的,又兼容服务器和客户端。

检出jQuery源代码,使其在Node.js模块模式,AMD模块模式和全局浏览器中工作:

 (function(window){ var jQuery = 'blah'; if (typeof module === "object" && module && typeof module.exports === "object") { // Expose jQuery as module.exports in loaders that implement the Node // module pattern (including browserify). Do not create the global, since // the user will be storing it themselves locally, and globals are frowned // upon in the Node module world. module.exports = jQuery; } else { // Otherwise expose jQuery to the global object as usual window.jQuery = window.$ = jQuery; // Register as a named AMD module, since jQuery can be concatenated with other // files that may use define, but not via a proper concatenation script that // understands anonymous AMD modules. A named AMD is safest and most robust // way to register. Lowercase jquery is used because AMD module names are // derived from file names, and jQuery is normally delivered in a lowercase // file name. Do this after creating the global so that if an AMD module wants // to call noConflict to hide this version of jQuery, it will work. if (typeof define === "function" && define.amd) { define("jquery", [], function () { return jQuery; }); } } })(this) 

也许这不完全符合这个问题,但我想我会分享这个。

我想在String.prototype上声明一些简单的string实用程序函数,可用于节点和浏览器。 我只是将这些函数保存在一个名为utilities.js的文件中(在子文件夹中),并且可以通过我的浏览器代码中的脚本标记以及在Node.js脚本中使用require(忽略.js扩展名)轻松地引用它们:

my_node_script.js

 var utilities = require('./static/js/utilities') 

my_browser_code.html

 <script src="/static/js/utilities.js"></script> 

我希望这是对除我以外的人有用的信息。

服务器可以简单地将JavaScript源文件发送到客户端(浏览器),但诀窍是客户端必须提供一个迷你“exports”环境,然后才能exec代码并将其作为模块存储。

制造这种环境的一个简单方法是使用闭包。 例如,假设您的服务器通过HTTP提供源文件,如http://example.com/js/foo.js 。 浏览器可以通过XMLHttpRequest加载所需的文件,并像下面这样加载代码:

 ajaxRequest({ method: 'GET', url: 'http://example.com/js/foo.js', onSuccess: function(xhr) { var pre = '(function(){var exports={};' , post = ';return exports;})()'; window.fooModule = eval(pre + xhr.responseText + post); } }); 

关键是客户端可以将外部代码包装成一个匿名函数,立即运行(闭包),创build“exports”对象并将其返回,以便将其分配到您想要的位置,而不是污染全局名称空间。 在这个例子中,它被分配给窗口属性fooModule ,它将包含文件foo.js导出的代码。

以前的解决scheme都没有将CommonJS模块系统带入浏览器。

正如其他答案中提到的那样,有像Browserify或Piler这样的资产pipe理器/包装器解决scheme,还有像dnode或nowjs这样的RPC解决scheme。

但是我找不到浏览器的CommonJS实现(包括require()函数和exports / module.exports对象等)。 所以我写了自己的,只是后来发现有人写的比我更好: https : //github.com/weepy/brequire 。 它被称为Brequire(浏览器需求的缩写)。

从受欢迎程度来看,资产pipe理者可以满足大多数开发者的需求。 但是,如果您需要CommonJS的浏览器实现, Brequire可能会适合这个账单。

2015更新:我不再使用Brequire(它在几年内没有更新)。 如果我只是写一个小的,开源的模块,我希望任何人都可以轻松使用,那么我将遵循一个类似于Caolan的答案(上面) – 我写了一个博客文章几年前。

但是,如果我正在编写专用模块或CommonJS标准化的社区(例如Ampersand社区),那么我只需要以CommonJS格式编写它们并使用Browserify 。

now.js也值得一看。 它允许您从客户端调用服务器端,并从服务器端调用客户端function

如果你想用类似Node.js的风格编写你的浏览器,你可以尝试双重化 。

没有浏览器代码编译,所以你可以写你的应用程序没有限制。

把你的代码写成RequireJS模块,把你的testing写成Jasminetesting。

这样,代码就可以在RequireJS的任何地方加载,并且testing在浏览器中用jasmine-html和Node.js中的jasmine-node运行 ,而不需要修改代码或者testing。

这是一个工作的例子 。

我写了这个,如果你想把所有的variables设置为全局范围,使用起来很简单:

 (function(vars, global) { for (var i in vars) global[i] = vars[i]; })({ abc: function() { ... }, xyz: function() { ... } }, typeof exports === "undefined" ? this : exports);