节点样式的浏览器内 javascript 需求?

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

提供更多细节: require如此优秀的原因在于:

  1. 允许从其他位置动态加载代码(在我看来,这在风格上比在 HTML 中链接所有代码要好)
  2. 它为构建模块提供了一致的接口
  3. 模块很容易依赖于其他模块(例如,我可以编写一个需要 jQuery 才能使用 jQuery.ajax()的 API)
  4. 加载的 javascript 是 瞄准,这意味着我可以加载 var dsp = require("dsp.js");并且我可以访问 dsp.FFT,这不会干扰我的本地 var FFT

我还没有找到一个能有效做到这一点的图书馆。我倾向于使用的变通方法是:

  • Coffeescript-concat ——需要其他 js 很容易,但是你必须编译它,这意味着它对于快速开发(例如构建测试中的 API)来说不是很好

  • RequreJS ——它很流行,很直接,解决了1-3个问题,但是缺乏作用域是一个真正的问题(我相信 头 J是类似的,因为它缺乏作用域,尽管我从来没有机会使用它。类似地,实验室可以加载,而 .wait()确实缓解了依赖性问题,但它仍然不做范围界定)

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

2020年更新: 现在,模组已经成为 ES6的标准,到2020年中期,大多数浏览器已经开始支持 模组。模块同时支持同步和异步(使用承诺)加载。我目前的建议是,大多数新项目应该使用 ES6模块,并使用传输器回退到旧版浏览器的单个 JS 文件。

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

之前的编辑(或者如果你不喜欢 ES6模块) : 自从写这篇文章以来,我已经广泛使用了 需要(现在有了更清晰的文档)。在我看来,RequreJS 确实是正确的选择。我想澄清一下,对于像我一样困惑的人来说,这个系统是如何工作的:

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

RequreJS 和像 Browserify (tjameson 推荐的一个很酷的项目)这样的节点风格需求之间的主要区别在于模块的设计和需求方式:

  • RequreJS 使用 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 开发自己的多文件项目,我强烈推荐 RequreJS,因为它确实是一个 Web 模块系统(尽管公平地说,我发现 AMD 比 CommonJS 自然得多)。最近,这种区别变得不那么重要了,因为 RequreJS 现在允许您基本上使用 CommonJS 语法。另外,RequreJS 可以用来在 Node 中加载 AMD 模块(尽管我更喜欢 Node-amd-loader 节点加载程序)。

79903 次浏览

Check out ender. It does a lot of this.

Also, browserify is pretty good. I've used require-kiss¹ and it works. There are probably others.

I'm not sure about RequireJS. It's just not the same as node's. You may run into problems with loading from other locations, but it might work. As long as there's a provide method or something that can be called.

TL;DR- I'd recommend browserify or require-kiss.


Update:

1: require-kiss is now dead, and the author has removed it. I've since been using RequireJS without problems. The author of require-kiss wrote pakmanager and pakman. Full disclosure, I work with the developer.

Personally I like RequireJS better. It is much easier to debug (you can have separate files in development, and a single deployed file in production) and is built on a solid "standard".

I wrote a small script which allows asynchronous and synchronous loading of Javascript files, which might be of some use here. It has no dependencies and is compatible to Node.js & CommonJS. The installation is pretty easy:

$ npm install --save @tarp/require

Then just add the following lines to your HTML to load the main-module:

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

Inside your main-module (and any sub-module, of course) you can use require() as you know it from CommonJS/NodeJS. The complete docs and the code can be found on GitHub.

Webmake bundles Node-style modules to Browser, give it a try.

(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];
}
})();

Better not to be used in production because of the blocking. (In node.js, require() is a blocking call is well).

A variation of Ilya Kharlamov great answer, with some code to make it play nice with chrome developer tools.

//
///- 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

Require-stub — provides node-compliant require in browser, resolves both modules and relative paths. Uses technic similar to TKRequire (XMLHttpRequest). Resulting code is fully browserifyable, in that require-stub can serve as a replacement for watchify.

I realize there may be beginners looking to organize their code. This is 2022, and if you're considering a modular JS app, you should get started with npm and Webpack right now.

Here are a few simple steps to get started:

  1. In your project root, run npm init -y to initialize an npm project
  2. Download the Webpack module bundler: npm install webpack webpack-cli
  3. Create an index.html file:
<!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>

Pay special attention to _bundle.js file - this will be a final JS file generated by webpack, you will not modify it directly (keep reading).

  1. Create a <project-root>/app.js in which you will import other modules:
const printHello = require('./print-hello');


printHello();
  1. Create a sample print-hello.js module:
module.exports = function() {
console.log('Hello World!');
}
  1. Create a <project-root>/webpack.config.js and copy-paste the following:
var path = require('path');


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

In the code above, there are 2 points:

  • entry app.js is where you will write your JS code. It will import other modules as shown above.
  • output _bundle.js is your final bundle generated by webpack. This is what your html will see at the end.
  1. Open your package.json, and replace scripts with the following command:
  "scripts": {
"start": "webpack --mode production -w"
},
  1. And finally run the script watch app.js and generate the _bundle.js file by running: npm start.
  2. Enjoy coding!