浏览器内 JavaScript 需要节点样式? [关闭]
Posted
技术标签:
【中文标题】浏览器内 JavaScript 需要节点样式? [关闭]【英文标题】:Node-style require for in-browser javascript? [closed] 【发布时间】:2011-10-21 17:51:21 【问题描述】:是否有任何浏览器内 javascript 库提供与 Node 的 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 年更新:Modules 现在是 ES6 的标准配置,截至 2020 年中期,most browsers 原生支持。模块支持同步和异步(使用 Promise)加载。我目前的建议是大多数新项目应该使用 ES6 模块,并使用转译器为旧版浏览器回退到单个 JS 文件。
作为一般原则,今天的带宽通常也比我最初提出这个问题时宽得多。因此,在实践中,您可能会合理地选择始终使用带有 ES6 模块的转译器,并将精力集中在代码效率而不是网络上。
以前的编辑(或者如果你不喜欢 ES6 模块): 自从写这篇文章以来,我已经广泛使用了RequireJS(现在有更清晰的文档)。在我看来,RequireJS 确实是正确的选择。我想澄清一下这个系统是如何为像我一样困惑的人工作的:
您可以在日常开发中使用require
。模块可以是函数返回的任何东西(通常是对象或函数),并且作用域为参数。您还可以使用r.js
将您的项目编译为单个文件进行部署(实际上这几乎总是更快,即使require
可以并行加载脚本)。
RequireJS 和 node-style require 之间的主要区别,比如 browserify(tjameson 推荐的一个很酷的项目)使用的模块是设计和需要的方式:
RequireJS 使用 AMD(异步模块定义)。在 AMD 中,require
采用要加载的模块列表(javascript 文件)和回调函数。当它加载了每个模块时,它会调用回调,并将每个模块作为回调的参数。因此它是真正异步的,因此非常适合网络。
节点使用 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)。
【问题讨论】:
注意 RequireJS 实际上支持模块化并且可以作用域。自从询问以来,我已经更广泛地使用了它。在我看来,它功能丰富,但需要大量阅读文档才能有效使用,并且它需要某种形式的一流同步加载才能完美。 异步的区别有意义吗?每当我需要代码时,我基本上无法继续,因为它定义了功能,所以我需要做任何事情...... 这个问题的答案应该拿出来作为实际答案输入。见Can I answer my own question? 【参考方案1】:我意识到可能有初学者希望组织他们的代码。现在是 2021,如果您正在考虑模块化 JS 应用程序,您应该立即开始使用 npm 和 Webpack。
以下是一些简单的入门步骤:
-
在您的项目根目录中,运行
npm init -y
以初始化 npm 项目
下载 Webpack 模块打包器:npm install webpack webpack-cli
创建 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文件,你不会直接修改它(继续阅读)。
-
创建一个
<project-root>/app.js
,您将在其中导入其他模块:
const printHello = require('./print-hello');
printHello();
-
创建示例
print-hello.js
模块:
module.exports = function()
console.log('Hello World!');
-
创建
<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 将在最后看到的内容。
-
打开您的
package.json
,并将scripts
替换为以下命令:
"scripts":
"start": "webpack --mode production -w"
,
-
最后运行脚本 watch
app.js
并通过运行:npm start
生成_bundle.js
文件。
享受编码!
【讨论】:
这对于 webpack 项目来说是一个非常好的开始!谢谢你:)【参考方案2】:我写了一个小脚本,它允许异步和同步加载 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 中所知道的那样。完整的文档和代码可以在on GitHub找到。
【讨论】:
如何使用 main.js 中的函数?例如 main.js 有简单的myFunction
到alert("hello")
。我打电话给main.myFunction()
吗?这不会发出警报吗?
您必须使用Tarp.require( expose: true );
才能使用它吗?就像你的测试一样?
不一定。仅当您想在主模块之外使用 require()
时,才需要选项 expose: true
。例如直接在 HTML 文件中。但在您的情况下,将myFunction()
添加到全局窗口对象可能会更好。您可以在主模块内将其定义为window.myFunction = function() ...
。这样myFunction()
就可以从任何地方访问。【参考方案3】:
查看ender。它做了很多这样的事情。
另外,browserify 也不错。我用过require-kiss¹,它可以工作。可能还有其他人。
我不确定 RequireJS。它与节点的不同。您可能会遇到从其他位置加载的问题,但它可能会起作用。只要有提供方法或可以调用的东西。
TL;DR- 我推荐 browserify 或 require-kiss。
更新:
1: require-kiss 现已失效,作者已将其删除。从那以后我一直在使用 RequireJS 没有问题。 require-kiss 的作者写了pakmanager 和pakman。完全披露,我与开发人员合作。
我个人更喜欢 RequireJS。它更容易调试(您可以在开发中使用单独的文件,在生产中使用单个部署文件)并且构建在可靠的“标准”之上。
【讨论】:
require-kiss 的链接似乎已失效。简单的(重新)搜索并没有带来任何结果——它去了哪里? @JoelPurra - require-kiss 已被删除并被 pakmanager 取代。我现在推荐 require-js。我已经更新了答案。 这里的答案很好,伙计:),介意看看我刚刚做的这个问题与这个问题相似(但同时不同)? ***.com/questions/43237875/…【参考方案4】:Require-stub — 在浏览器中提供符合节点的require
,解析模块和相对路径。使用类似于 TKRequire (XMLHttpRequest) 的技术。
生成的代码完全可浏览,因为require-stub
可以替代watchify
。
【讨论】:
【参考方案5】: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
【讨论】:
【参考方案6】:(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() 是一个阻塞调用很好)。
【讨论】:
不应该是“exports:”一个“module”的属性?并且调用是 (R, module.exports, module) @LucioM.Tato 我不确定,在Modules 1.1 standard 中看不到任何关于 module.exports 的提及。您可以随时调用 require(module.id) 来获取导出 是的。你说得对,我在考虑 node.js 模块的实现。我已经在您非常好的答案中添加了一些对代码的修改,以使其与 Chrome 开发工具(我将其用作调试时间 IDE)一起使用。我会将代码发布为这个问题的另一个答案,以防对其他人有用。【参考方案7】:Webmake 将 Node 样式的模块捆绑到浏览器中,试试看。
【讨论】:
以上是关于浏览器内 JavaScript 需要节点样式? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章