浏览器内 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 应用程序,您应该立即开始使用 npmWebpack

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

    在您的项目根目录中,运行 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文件,你不会直接修改它(继续阅读)。

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

printHello();
    创建示例print-hello.js 模块:
module.exports = function() 
    console.log('Hello World!');

    创建&lt;project-root&gt;/webpack.config.js 并复制粘贴以下内容:
var path = require('path');

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

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

entry app.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 有简单的myFunctionalert("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 需要节点样式? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

前端乱煮之javascript学习笔记三

关于javascript的一些知识以及循环

JavaScript学习笔记

JavaScript---1

JQ基础样式篇

全栈JavaScript之路( 二十五 )訪问元素的样式