95

是否有任何浏览器内 javascript 库提供与 Node 相同的灵活性/模块化/易用性require

提供更多细节:之所以require这么好,是因为它:

  1. 允许从其他位置动态加载代码(在我看来,这在风格上比在 HTML 中链接所有代码更好)
  2. 它为构建模块提供了一致的接口
  3. 模块很容易依赖于其他模块(例如,我可以编写一个需要 jQuery 的 API,这样我就可以使用jQuery.ajax()
  4. 加载的 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 和 node-style require (如 browserify (tjameson 推荐的一个很酷的项目))使用的主要区别在于模块的设计和需求方式:

  • RequireJS 使用 AMD(异步模块定义)。在 AMD 中,require获取要加载的模块列表(javascript 文件)和回调函数。当它加载了每个模块时,它会调用回调,并将每个模块作为回调的参数。因此它是真正异步的,因此非常适合网络。
  • Node 使用 CommonJS。在 CommonJS 中,require是一个加载模块并将其作为对象返回的阻塞调用。这对 Node 来说很好,因为文件是从文件系统中读取的,这足够快,但在 Web 上效果不佳,因为同步加载文件可能需要更长的时间。

在实践中,许多开发人员在看到 AMD 之前就已经使用过 Node(以及 CommonJS)。此外,许多库/模块是为 CommonJS(通过向exports对象添加内容)而不是为 AMD(通过从define函数返回模块)编写的。因此,许多从 Node 转向 Web 的开发人员都希望在 Web 上使用 CommonJS 库。这是可能的,因为从<script>标签加载是阻塞的。browserify 之类的解决方案采用 CommonJS (Node) 模块并将它们包装起来,以便您可以将它们包含在脚本标签中。

因此,如果你正在为 Web 开发自己的多文件项目,我强烈推荐 RequireJS,因为它确实是一个用于 Web 的模块系统(尽管公平地披露,我发现 AMD 比 CommonJS 更自然)。最近,这种区别变得不那么重要了,因为 RequireJS 现在允许您基本上使用 CommonJS 语法。此外,RequireJS 可用于在 Node 中加载 AMD 模块(尽管我更喜欢node-amd-loader)。

4

7 回答 7

24

我意识到可能有初学者希望组织他们的代码。这是2022 年,如果您正在考虑模块化 JS 应用程序,您应该立即开始使用npmWebpack

以下是一些简单的入门步骤:

  1. 在您的项目根目录中,运行npm init -y以初始化一个 npm 项目
  2. 下载 Webpack 模块捆绑器:npm install webpack webpack-cli
  3. 创建一个 index.html 文件:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>App</title>
</head>
<body>
    
    <script src="_bundle.js"></script>
</body>
</html>

特别注意_bundle.js文件 - 这将是 webpack 生成的最终 JS 文件,您不会直接修改它(继续阅读)。

  1. 创建一个<project-root>/app.js您将在其中导入其他模块:
const printHello = require('./print-hello');

printHello();
  1. 创建一个示例print-hello.js模块:
module.exports = function() {
    console.log('Hello World!');
}
  1. 创建<project-root>/webpack.config.js并复制粘贴以下内容:
var path = require('path');

module.exports = {
  entry: './app.js',
  output: {
    path: path.resolve(__dirname),
    filename: '_bundle.js'
  }
};

在上面的代码中,有2点:

  • entryapp.js是你编写 JS 代码的地方。如上所示,它将导入其他模块。
  • output_bundle.js是 webpack 生成的最终包。这是您的 html 将在最后看到的内容。
  1. 打开您的package.json, 并替换scripts为以下命令:
  "scripts": {
    "start": "webpack --mode production -w"
  },
  1. 最后运行脚本 watchapp.js并通过运行生成_bundle.js文件:npm start.
  2. 享受编码!
于 2020-02-01T19:32:54.900 回答
21

检查ender。它做了很多这样的事情。

另外,browserify 也不错。我使用过require-kiss ¹ 并且它有效。可能还有其他人。

我不确定RequireJS。它与节点的不同。您可能会遇到从其他位置加载的问题,但它可能会起作用。只要有提供方法或可以调用的东西。

TL;DR - 我推荐 browserify 或 require-kiss。


更新:

1:require-kiss现在已经死了,作者已经把它去掉了。从那以后我一直在使用 RequireJS 没有问题。require-kiss 的作者写了pakmanagerpakman。完全披露,我与开发人员合作。

我个人更喜欢RequireJS。它更容易调试(您可以在开发中拥有单独的文件,在生产中拥有单个部署的文件)并且建立在可靠的“标准”之上。

于 2011-08-07T08:26:26.220 回答
17

我写了一个小脚本,它允许异步和同步加载 Javascript 文件,在这里可能会有一些用处。它没有依赖关系,并且兼容 Node.js 和 CommonJS。安装非常简单:

$ npm install --save @tarp/require

然后只需将以下行添加到您的 HTML 以加载主模块:

<script src="/node_modules/@tarp/require/require.min.js"></script>
<script>Tarp.require({main: "./scripts/main"});</script>

在您的主模块(当然还有任何子模块)中,您可以require()按照您从 CommonJS/NodeJS 中了解的方式使用它。完整的文档和代码可以在 GitHub 上找到

于 2012-07-21T00:40:34.847 回答
12

Ilya Kharlamov 的一个变体很好的答案,有一些代码可以让它与 chrome 开发者工具配合得很好。

//
///- REQUIRE FN
// equivalent to require from node.js
function require(url){
    if (url.toLowerCase().substr(-3)!=='.js') url+='.js'; // to allow loading without js suffix;
    if (!require.cache) require.cache=[]; //init cache
    var exports=require.cache[url]; //get from cache
    if (!exports) { //not cached
            try {
                exports={};
                var X=new XMLHttpRequest();
                X.open("GET", url, 0); // sync
                X.send();
                if (X.status && X.status !== 200)  throw new Error(X.statusText);
                var source = X.responseText;
                // fix (if saved form for Chrome Dev Tools)
                if (source.substr(0,10)==="(function("){ 
                    var moduleStart = source.indexOf('{');
                    var moduleEnd = source.lastIndexOf('})');
                    var CDTcomment = source.indexOf('//@ ');
                    if (CDTcomment>-1 && CDTcomment<moduleStart+6) moduleStart = source.indexOf('\n',CDTcomment);
                    source = source.slice(moduleStart+1,moduleEnd-1); 
                } 
                // fix, add comment to show source on Chrome Dev Tools
                source="//@ sourceURL="+window.location.origin+url+"\n" + source;
                //------
                var module = { id: url, uri: url, exports:exports }; //according to node.js modules 
                var anonFn = new Function("require", "exports", "module", source); //create a Fn with module code, and 3 params: require, exports & module
                anonFn(require, exports, module); // call the Fn, Execute the module
                require.cache[url]  = exports = module.exports; //cache obj exported by module
            } catch (err) {
                throw new Error("Error loading module "+url+": "+err);
            }
    }
    return exports; //require returns object exported by module
}
///- END REQUIRE FN
于 2013-10-28T00:04:56.667 回答
5
(function () {
    // c is cache, the rest are the constants
    var c = {},s="status",t="Text",e="exports",E="Error",r="require",m="module",S=" ",w=window;
    w[r]=function R(url) {
        url+=/.js$/i.test(url) ? "" : ".js";// to allow loading without js suffix;
        var X=new XMLHttpRequest(),module = { id: url, uri: url }; //according to the modules 1.1 standard
        if (!c[url])
            try {
                X.open("GET", url, 0); // sync
                X.send();
                if (X[s] && X[s] != 200) 
                    throw X[s+t];
                Function(r, e, m, X['response'+t])(R, c[url]={}, module); // Execute the module
                module[e] && (c[url]=module[e]);
            } catch (x) {
                throw w[E](E+" in "+r+": Can't load "+m+S+url+":"+S+x);
            }
        return c[url];
    }
})();

由于阻塞,最好不要在生产中使用。(在 node.js 中, require() 是一个阻塞调用很好)。

于 2013-08-05T01:29:06.230 回答
1

Require-stub — 在浏览器中提供节点兼容require,解析模块和相对路径。使用类似于 TKRequire (XMLHttpRequest) 的技术。生成的代码完全可浏览,因为它require-stub可以替代watchify.

于 2015-01-11T07:15:58.767 回答
0

Webmake 将Node 样式的模块捆绑到浏览器,试一试。

于 2012-07-26T18:58:49.270 回答