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

我想在客户机和服务器之间共享代码。我才刚刚开始使用Node.js,至少可以说,我对现代JavaScript的知识有点生疏。因此,我仍然对CommonJS的require()函数感到困惑。如果我通过使用“export”对象创建我的包,那么我无法看到我如何在浏览器中使用相同的JavaScript文件。

我想创建一组在两端使用的方法和类,以方便编码和解码消息以及其他镜像任务。然而,Node.js/CommonJS打包系统似乎阻止了我创建可以在双方使用的JavaScript文件。

我还尝试使用JS.Class来获得更紧密的OO模型,但我放弃了,因为我不知道如何让提供的JavaScript文件与require()一起工作。我是不是遗漏了什么?


当前回答

现在,js也值得一看。它允许您从客户端调用服务器端,从服务器端调用客户端函数

其他回答

如果你使用模块捆绑器(如webpack)来捆绑JavaScript文件以便在浏览器中使用,你可以简单地重用你的Node.js模块用于在浏览器中运行的前端。换句话说,你的Node.js模块可以在Node.js和浏览器之间共享。

例如,你有下面的代码sum.js:

普通Node.js模块:sum.js

const sum = (a, b) => {
    return a + b
}

module.exports = sum

使用Node.js中的模块

const sum = require('path-to-sum.js')
console.log('Sum of 2 and 5: ', sum(2, 5)) // Sum of 2 and 5:  7

在前端重用它

import sum from 'path-to-sum.js'
console.log('Sum of 2 and 5: ', sum(2, 5)) // Sum of 2 and 5:  7

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

另一种方法是使用构建系统,将公共代码放在单独的文件中,然后将它们包含在服务器和客户端脚本中。我通过WebSockets将这种方法用于一个简单的客户端/服务器游戏,其中服务器和客户端都运行本质上相同的游戏循环,客户端每一次都与服务器同步,以确保没有人作弊。

我的游戏构建系统是一个简单的Bash脚本,它通过C预处理器运行文件,然后通过sed清理cpp留下的一些垃圾,所以我可以使用所有普通的预处理器,如#include, #define, #ifdef等。

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

创建这样一个环境的一个简单方法是使用闭包。例如,假设您的服务器通过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);
  }
});

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

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

将代码编写为RequireJS模块,将测试编写为Jasmine测试。

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

下面是一个工作示例。