浏览器内JavaScript是否需要节点样式?


83

浏览器内的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的方法,它完全不会污染全局名称空间,但是仍然允许我加载和使用库(就像节点的require一样)。

2020年更新: 模块现已成为ES6中的标准组件,并且自2020年中期开始,大多数浏览器都原生支持这些模块。模块支持同步和异步(使用Promise)加载。我当前的建议是,大多数新项目应使用ES6模块,并使用转译器回退到旧版浏览器的单个JS文件。

作为一般原则,今天的带宽通常也比我最初提出这个问题时要宽得多。因此,在实践中,您可能会合理地选择始终将转译器与ES6模块一起使用,并将精力集中在代码效率而不是网络上。

以前的编辑(或者,如果您不喜欢ES6模块):自编写此文档以来,我已经广泛使用RequireJS(现在它的文档更加清晰)。在我看来,RequireJS确实是正确的选择。我想澄清一下该系统如何为像我一样困惑的人们工作:

您可以require在日常开发中使用。模块可以是函数(通常是对象或函数)返回的任何东西,范围可以作为参数。您还可以将项目编译为单个文件以进行部署r.js(实际上,尽管require可以并行加载脚本,但这样做几乎总是更快)。

RequireJS和节点样式的require之间的主要区别在于,例如browserify(tjameson建议一个很酷的项目)使用的是模块的设计和需求方式:

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

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

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


1
注意RequireJS实际上确实支持模块化并且可以作用域。自询问以来,我已对其进行了更广泛的使用。在我看来,它是功能强大的,但是要有效地使用它需要阅读大量文档,并且在完善之前需要某种形式的一流同步加载。
亚历克斯·丘吉尔

1
异步的区别有意义吗?每当我需要代码时,由于它定义了函数,因此我基本上无法继续运行,因此我需要做任何事情……
迈克尔

Answers:


17

检出ender。它做了很多。

另外,browserify 还不错。我已经使用了require- kiss¹,它可以正常工作。可能还有其他人。

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

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


更新:

1:require-kiss现在已死,作者已将其删除。从那以后,我一直在使用RequireJS而不出现问题。require-kiss的作者写了pakmanagerpakman。全面披露,我与开发商合作。

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


太酷了,还没有尝试使用browserify,但是看起来确实正是我所需要的。
亚历克斯·丘吉尔

到require-kiss的链接似乎已死。简单的(重新)搜索没有成功-到哪里去了?
乔尔·普拉

@JoelPurra-require-kiss已被删除,由pakmanager取代。我现在建议require-js。我已经更新了答案。
beatgammit,2012年

很好的回答这里的人:),介意检查一下我刚才做的与该问题类似的问题(但同时不同)吗?stackoverflow.com/questions/43237875/…–
Webeng

16

我写了一个小脚本,它允许异步和同步加载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上找到


1
真是太酷了。感谢您撰写本文!
OldTimeGuitarGuy

谢谢@OldTimeGuitarGuy :)
Torben

10

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

谢谢卢西奥!我一直在寻找这样的最小解决方案。我扩展了它以支持相对路径。见下文。
Trausti Kristjansson 2014年

8

我意识到可能有些初学者希望组织他们的代码。这是2020年,如果您正在考虑使用模块化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点:

  • 条目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. 享受编码!

1
在2020年,应将其标记为正确答案
p13rnd

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()是一个阻塞调用就可以了)。


不应是“导出:{}”的“模块”属性?呼叫为(R,module.exports,module)
Lucio M. Tato

我不确定@ LucioM.Tato,看不到Modules 1.1 standard中对module.exports的任何提及。您可以随时致电require(module.id)获取出口
Ilya Kharlamov

是。没错,我在考虑node.js模块的实现。为了使它在Chrome开发工具中发挥出色的作用,我在您的非常好的答案中对代码进行了一些修改(我将其用作“调试时间” IDE)。如果对其他人有用,我将代码发布为该问题的另一个答案。
Lucio M. Tato 2013年


1

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


0

这是Lucio M. Tato出色答案的扩展,它允许递归加载具有相对路径的模块。

这是一个包含解决方案github项目,以及如何使用该解决方案的示例:

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

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

<script type =“ text / javascript” src =“ ./ TKRequire.js”> </ script>

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

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


谢谢特劳斯蒂。如果您使用的是JavaScript,则应检查github.com/luciotato/LiteScript(测试版)。PD:您现在在玩CC Saga的哪个级别?:P
Lucio M. Tato 2014年

链接已失效,我们如何抓取并查看您的代码?
PA。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.