有没有适用于浏览器端JavaScript的库,其提供与Node.js的require
相同的灵活性、模块化和易用性?
更详细地说,之所以require
如此出色是因为它:
- 允许从其他位置动态加载代码(在我看来样式更好,而不是将所有代码链接到HTML中)
- 提供了构建模块的一致接口
- 易于模块依赖其他模块(因此我可以编写一个需要jQuery的API,以便使用
jQuery.ajax()
) - 加载的JavaScript是封闭作用域的,这意味着我可以使用
var dsp = require("dsp.js");
进行加载,并且我能够访问dsp.FFT
,这不会干扰我的本地变量var FFT
我尚未找到有效实现这一点的库。 我经常使用的解决方法是:
coffeescript-concat -- 很容易要求其他js,但你必须编译它,这意味着它不太适合快速开发(例如在测试中构建API)
RequireJS -- 它很受欢迎,简单明了,并解决了1-3的问题,但缺乏作用域是一个真正的破坏者(我相信head.js也类似于缺少作用域,虽然我从未有过任何使用它的场合。同样,LABjs可以加载和
.wait()
可以缓解依赖问题,但仍然没有做到作用域)
据我所知,似乎有许多动态和/或异步加载javascript的解决方案,但它们往往具有与从HTML加载js相同的作用域问题。最重要的是,我希望有一种加载javascript的方式,完全不污染全局命名空间,但仍允许我加载和使用库(就像node的require一样)。
2020年更新:模块现在是ES6的标准,在2020年中期,大多数浏览器已经原生支持了。模块支持同步和异步(使用Promise)加载。我目前的建议是,大多数新项目应该使用ES6模块,并使用转换器回退到单个JS文件以适应旧版浏览器。
总体原则是,今天的带宽通常比我最初提出这个问题时要宽得多。因此,实际上,您可能会合理地选择始终使用ES6模块的转换器,并将精力集中在代码效率而不是网络上。
以前的编辑(或者如果您不喜欢ES6模块):自从写下这篇文章以来,我广泛使用了RequireJS(现在有更清晰的文档)。在我看来,RequireJS确实是正确的选择。我想澄清一下系统如何工作,以便像我一样困惑的人能够理解:
您可以在日常开发中使用require
。模块可以是由函数返回的任何内容(通常是对象或函数),并且作为参数进行作用域限制。您还可以使用r.js
将项目编译为单个文件以进行部署(实际上,这几乎总是更快的,即使require
可以并行加载脚本)。
RequireJS和类似browserify(tjameson建议的很酷的项目)使用的node-style require之间的主要区别在于模块的设计和要求方式:
- RequireJS 使用 AMD(异步模块定义)。在 AMD 中,
require
接受需要加载的模块(JavaScript 文件)列表和回调函数。当它加载完每个模块后,它会将每个模块作为参数传递给回调函数。因此,它是真正的异步,并且非常适合于 Web。 - Node 使用 CommonJS。在 CommonJS 中,
require
是一个阻塞调用,它会加载一个模块并将其作为对象返回。这对 Node 来说很好用,因为文件可以快速从文件系统中读取,但在 Web 上效果不佳,因为同步加载文件可能需要更长的时间。
实际上,许多开发人员在看到 AMD 之前就已经使用了 Node(因此使用了 CommonJS)。此外,许多库/模块是针对 CommonJS 编写的(通过向 exports
对象添加内容),而不是针对 AMD 编写的(通过从 define
函数返回模块)。因此,许多从 Node 转到 Web 的开发人员希望在 Web 上使用 CommonJS 库。这是可能的,因为从 <script>
标签加载是阻塞的。像 browserify 这样的解决方案可以将 CommonJS(Node)模块包装起来,以便您可以使用 script 标签包含它们。