如何在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);