我正在用JavaScript客户端(在浏览器中运行)和Node.js服务器创建一个小应用程序,使用WebSocket通信。
我想在客户机和服务器之间共享代码。我才刚刚开始使用Node.js,至少可以说,我对现代JavaScript的知识有点生疏。因此,我仍然对CommonJS的require()函数感到困惑。如果我通过使用“export”对象创建我的包,那么我无法看到我如何在浏览器中使用相同的JavaScript文件。
我想创建一组在两端使用的方法和类,以方便编码和解码消息以及其他镜像任务。然而,Node.js/CommonJS打包系统似乎阻止了我创建可以在双方使用的JavaScript文件。
我还尝试使用JS.Class来获得更紧密的OO模型,但我放弃了,因为我不知道如何让提供的JavaScript文件与require()一起工作。我是不是遗漏了什么?
之前的解决方案都没有将CommonJS模块系统引入浏览器。
正如在其他答案中提到的,有资产管理器/打包器解决方案,如Browserify或Piler,还有RPC解决方案,如dnode或nowjs。
但是我找不到浏览器的CommonJS实现(包括require()函数和exports /模块)。导出对象等)。所以我自己写了,后来才发现别人写得比我好:https://github.com/weepy/brequire。它被称为Brequire (Browser require的缩写)。
从受欢迎程度来看,资产管理公司符合大多数开发商的需求。然而,如果你需要CommonJS的浏览器实现,Brequire可能会符合要求。
2015年更新:我不再使用Brequire(它已经几年没有更新了)。如果我只是在编写一个小型的开源模块,并且我希望任何人都能轻松使用,那么我将遵循与Caolan的回答类似的模式(上面)——我在几年前写过一篇关于它的博客文章。
然而,如果我写的模块是为私人使用的,或者是为一个在CommonJS上标准化的社区(比如Ampersand社区)写的,那么我就用CommonJS的格式写它们,并使用Browserify。
我写了一个简单的模块,可以导入(在Node中使用require,或者在浏览器中使用脚本标记),您可以使用它从客户端和服务器加载模块。
示例使用
1. 定义模块
将以下文件放在你的静态web文件文件夹中的log2.js文件中:
let exports = {};
exports.log2 = function(x) {
if ( (typeof stdlib) !== 'undefined' )
return stdlib.math.log(x) / stdlib.math.log(2);
return Math.log(x) / Math.log(2);
};
return exports;
就这么简单!
2. 使用模块
由于它是一个双边模块加载器,我们可以从双方(客户端和服务器)加载它。因此,你可以做以下事情,但你不需要同时做这两件事(更不用说以特定的顺序):
在节点
在Node中,它很简单:
var loader = require('./mloader.js');
loader.setRoot('./web');
var logModule = loader.importModuleSync('log2.js');
console.log(logModule.log2(4));
这个应该返回2。
如果文件不在Node的当前目录中,请确保调用loader。seroot的路径到你的静态web文件文件夹(或任何你的模块)。
在浏览器中:
首先,定义网页:
<html>
<header>
<meta charset="utf-8" />
<title>Module Loader Availability Test</title>
<script src="mloader.js"></script>
</header>
<body>
<h1>Result</h1>
<p id="result"><span style="color: #000088">Testing...</span></p>
<script>
let mod = loader.importModuleSync('./log2.js', 'log2');
if ( mod.log2(8) === 3 && loader.importModuleSync('./log2.js', 'log2') === mod )
document.getElementById('result').innerHTML = "Your browser supports bilateral modules!";
else
document.getElementById('result').innerHTML = "Your browser doesn't support bilateral modules.";
</script>
</body>
</html>
确保你没有直接在浏览器中打开文件;因为它使用AJAX,我建议你看看Python 3的http。服务器模块(或任何你的超快速,命令行,文件夹web服务器部署解决方案)。
如果一切顺利,将会出现:
如果你使用模块捆绑器(如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源文件发送到客户端(浏览器),但诀窍是客户端必须提供一个迷你的“导出”环境,然后才能执行代码并将其存储为模块。
创建这样一个环境的一个简单方法是使用闭包。例如,假设您的服务器通过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导出的代码。