浏览器内的javascript需要节点式吗?

时间:2011-08-07 08:20:33

标签: javascript load require scoping

是否存在任何用于浏览器内javascript的库,它们提供与Node require相同的灵活性/模块性/易用性?

提供更多详细信息:require非常好的原因是:

  1. 允许从其他位置动态加载代码(在我看来,这样做比在HTML中链接所有代码更好)
  2. 它为构建模块提供了一致的界面
  3. 模块很容易依赖其他模块(所以我可以编写一个需要jQuery的API,所以我可以使用jQuery.ajax()
  4. 加载的javascript是作用域,这意味着我可以使用var dsp = require("dsp.js");加载,我可以访问dsp.FFT,这不会干扰我的本地{{1} }
  5. 我还没有找到一个能够有效完成这项工作的库。我倾向于使用的解决方法是:

    • coffeescript-concat - 很容易需要其他js,但你必须编译它,这意味着它不太适合快速开发(例如构建API in-test)

      < / LI>
    • RequireJS - 它很受欢迎,直截了当,并解决了1-3,但缺少范围确定是一个真正的交易破坏者(我相信head.js是相似的,因为它缺乏范围,虽然我从来没有机会使用它。同样,LABjs可以加载,var FFT可以缓解依赖性问题,但它仍然没有确定范围。

    据我所知,似乎有很多动态和/或异步加载javascript的解决方案,但它们往往具有与从HTML加载js相同的范围问题。最重要的是,我想要一种加载不会污染全局命名空间的javascript的方法,但仍允许我加载和使用库(就像节点的要求一样)。

    编辑(我的答案):自写这篇文章以来,我已广泛使用RequireJS(现在有更清晰的文档)。在我看来,RequireJS确实是正确的选择。我想澄清一下这个系统如何适用于那些和我一样困惑的人:

    您可以在日常开发中使用.wait()。模块可以是函数返回的任何内容(通常是对象或函数),并且作为参数确定范围。您还可以使用require将项目编译为单个文件以进行部署(实际上,这几乎总是更快,即使{​​{1}}可以并行加载脚本)。

    RequireJS和node-style之间的主要区别就像browserify(由tjameson建议的一个很酷的项目)使用的是模块的设计和要求:

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

    在实践中,许多开发人员在看到AMD之前就使用过Node(因此也使用了CommonJS)。此外,许多库/模块是为CommonJS编写的(通过向require对象添加内容)而不是为AMD编写(通过从require函数返回模块)。因此,许多支持Node的Web开发人员希望在Web上使用CommonJS库。这是可能的,因为从exports标记加载是阻止的。像browserify这样的解决方案采用CommonJS(Node)模块并将它们包装起来,以便您可以将它们包含在脚本标记中。

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

8 个答案:

答案 0 :(得分:15)

结帐ender。它做了很多这个。

此外,browserify非常好。我使用了require-kiss并且它有效。可能还有其他人。

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

TL; DR - 我建议使用browserify或require-kiss。

<强>更新

require-kiss现已死亡,作者已将其删除。我一直在使用RequireJS没有问题。 require-kiss的作者写了pakmanagerpakman。完全披露,我与开发人员合作。

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

答案 1 :(得分:12)

我写了一个小脚本,允许异步和同步加载Javascript文件,这可能在这里有用。它没有依赖关系,并且与Node.js兼容。 CommonJS的。您甚至可以将多个模块捆绑在一个文件中,以减少生产服务器上的HTTP请求。使用非常简单:

<script type="text/javascript" src="require.js"></script>
<script type="text/javascript">
    var ModuleA = require('modulea') // Sync loading of a script in the module directory
    ModuleA.funcA();

    require('./path/moduleb.js', callbackB); // Async loading of a script anywhere else
    function callbackB(ModuleB) {
        ModuleB.funcB();
    }
</script>

更多细节和代码可以在我的博客中找到:http://pixelsvsbytes.com/2013/02/js-require-for-browsers-better-faster-stronger/ 代码也在GitHub上:https://github.com/letorbi/smoothie/blob/master/standalone/require.js

答案 2 :(得分:9)

我意识到可能有一些初学者希望组织他们的代码。这是 2020 ,如果您正在考虑使用模块化JS应用,则应该立即开始使用 npm Webpack

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

  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点:

  • 输入app.js是编写JS代码的地方。它将导入其他模块,如上所示。
  • 输出_bundle.js是webpack生成的最后一个包。这就是您的html末尾将看到的内容。

-7。打开您的package.js,然后用以下命令替换scripts

  "scripts": {
    "start": "webpack --mode production -w"
  },
  1. 最后运行脚本监视app.js并通过运行_bundle.js来生成npm start文件。
  2. 享受编码!

答案 3 :(得分:7)

Ilya Kharlamov great answer的变体,有一些代码可以让它与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

答案 4 :(得分: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()是一个阻塞调用很好)。

答案 5 :(得分:1)

Webmake将节点式模块捆绑到浏览器中,试一试。

答案 6 :(得分:1)

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

答案 7 :(得分:0)

以下是Lucio M. Tato的一个很棒的答案,它允许递归加载具有相对路径的模块。

以下是github project to house the solution以及如何使用它的示例:

https://github.com/trausti/TKRequire.js

要使用TKRequire.js,请在标题中包含以下行

  

&lt; script type =“text / javascript”src =“./ TKRequire.js”&gt;&lt; / script&gt;

然后像在node.js中一样加载模块:

  

var MyModule = require(“./ relative / path / to / MyModule.js”);