使用RequireJS进行Javascript命名空间,为什么这样做?

6

我目前正在面对关于JavaScript命名空间的争论,需要社区意见。

情况: 负责该项目的架构师非常热衷于RequireJS并且想要使用它。

我必须说这是一个后台管理应用程序,布局为向导形式,因此您需要在6个页面上来回跳转,并进行一些复杂的业务逻辑,最终填写某些内容,我可以在这里将其描述为流程请求。

好的,没有单页应用程序,也没有任何复杂的问题。普通的后台Web应用程序,多页面,具有非常复杂的UI,每个页面都需要向服务器请求,并且所有资源(CSS,JavaScript等)都必须在页面加载时加载。

主要问题:了解我们所谈论的应用程序类型,为什么首先要使用RequireJS?

第二个问题:为什么要尝试说服使用RequireJS作为JavaScript命名空间的最佳方法?我错过了什么吗?

我的观点: 对我来说根本没有意义。 在这里使用RequireJS很麻烦,因为不会按需加载任何资源,它们都在页面加载时加载(只是因为我们需要它们在页面加载时全部加载)。 我们需要支持至少IE8,Chrome,Firefox和Opera,并且在所有这些浏览器中已经遇到了很多资源加载问题。在Require中确保一切按预期加载已经有很多技巧了。

对于命名空间,情况甚至更糟。当然它可以工作,但对我来说似乎又麻烦又非常有限。

所以我错过了什么吗? 我需要第三个(或第100个)意见。

  • 您怎么看这个问题?
  • 您使用什么?
  • 为什么?

提前致谢


你在 C# 中使用 using 语句吗?这是同一件事。 - BentOnCoding
C#的"Using"语句和JavaScript的AMD工具完全不同。 - AlexCode
"Using" 将命名空间中包含的类型导入到立即封闭的编译单元或命名空间体中。 "require" 将指定的模块导入到当前脚本/模块中。听起来对我来说非常相似,但这只是我的观点。 - BentOnCoding
AMD代表异步模块定义,它不仅仅是快捷方式和对象命名空间,这就是C#中“using”的全部。 - AlexCode
4个回答

4

AMD加载器(例如RequireJS)解决了不同的问题:

  • 适当的模块化,关注点分离
  • 没有全局污染
  • 没有名称冲突
  • 按需加载或预部署优化
  • 根据加载器,提供插件以解决高级问题,例如本地化资源或加载时编译

我非常支持这样的加载器,并发现它们适用于除非非常小的单页应用程序之外的所有内容。


我只将该列表中的最后两个链接到 AMD 加载器。 - AlexCode
自从以前,我一直为每个页面拥有单独的文件,这样可以很好地限定关注点,但没有命名空间。随着应用程序在客户端变得更加复杂,我开始将代码封装在域函数中,以避免污染全局环境并防止名称冲突。根据我的经验,我知道RequireJS并不是“只需工作”。跨所有浏览器(特别是旧版本)的兼容性和稳定性并不是一件简单的事情,那么为什么要试图用重型武器来解决小问题呢? - AlexCode
1
@AlexCode 全局变量的缺失是 AMD 概念的一部分:http://requirejs.org/docs/whyamd.html - Christophe

3
不论是否是“单页应用程序”,RequireJS在开发客户端JavaScript时有两个方面是很难做到自我约束的,这就需要帮助:

生产环境与开发环境

现在把JavaScript应用程序拆分成逻辑上不同的文件已经是常识了。这样做更容易调试和维护。但在生产环境下,将多个未压缩的文件打包成一个文件会影响性能。因此,你需要将所有文件合并成一个单一的文件,压缩它(例如使用Google closure编译器),然后将其作为单个gzip文件发送。虽然有许多不同的命令行工具来完成这个任务(例如使用GruntJS),但没有像RequireJS这样的脚本加载器,你必须自己设置两种不同的用例(引用所有开发文件作为脚本标记或单个production.js)。RequireJS有一个NodeJS构建工具可以为你完成所有这些操作。

模块化

现在把代码放在单独的文件中是很好的。但由于JavaScript的性质和所有东西都是全局的,这意味着你仍然可能遇到奇怪的事情,比如名称冲突。这就是命名空间派上用场的地方。但更好的选择是将所有内容包装到自己的函数中(引入自己的作用域)。这就是为什么大多数JavaScript代码都以自执行匿名函数形式出现。如果此函数现在返回它想要公开的API,那么你就有了Web模块。你可以单独测试模块API,并在其他地方重复使用它。

还可以在RequireJS文档的Why Web Modules部分中阅读相关内容。


由于回复,我发布了一个回答来解释我如何实施命名空间以及原因。 你对环境相关配置确实有很强的观点。 谢谢! - AlexCode

2
在我个人看来,使用JavaScript模块系统几乎从来不是一个坏主意。Requirejs并不是唯一可用的JavaScript模块加载器(但可能是最受欢迎的)。一些替代方案包括LABjs或HeadJS。
这些加载器通常很容易使用(在开始时没有太多麻烦),但随着项目变得越来越大,它们可以帮助您大有裨益。它们避免了全局空间中的命名冲突,并且还可以通过最小化/优化您的模块来支持您在部署步骤中。最重要的事实是它们允许您编写更加模块化的JavaScript代码。
  • 您有一些实用函数?只需创建一个实用程序模块。
  • 您有一些需要在所有页面上使用的函数?将它们放入公共模块中。
  • 某些页面需要大量特定的JavaScript代码?为这些页面创建一个额外的模块,以便您不必在其他页面上加载此代码。

我在我的回答中对你的观点进行了评论。 基本上,我完全同意你的观点,只是我不喜欢为了实现一些可能非常简单和透明的东西而增加额外的开销和失去控制。 - AlexCode

0

我目前防止全局环境污染和名称冲突的解决方案是使用$.extend

文件系统树看起来像下面这样:

RootFolder
  +-> js
  global.js
  ...
   +-> views
       index.js
       page1.js
       page2.js
       ...
index.html
page1.html
page2.html

通常情况下,我们会有一个名为global.js的文件,其中包含所有应用程序共享代码,还有另一个与每个页面相关的特定文件。
对于命名空间,我喜欢使用$.extend,在每个js文件中都会有类似以下的内容:
var app = app || {};  

/* only one document ready block per view if needed */
$(document).ready(function(){
    /* initialization code */
});

/* extend the app namespace with the Page1 code */
$.extend(app, {
    page1: {
        SayHi: function SayHi(name){
            alert('Hi ' + name);
        }
    }
});

因此,您可以通过调用以下代码来访问您的代码:

app.page1.SayHi("Alex");

使用这种技术,您可以:

  • 完全控制您的代码。没有神奇的资源加载和花哨的东西,您无法完全控制。
  • 没有全局环境污染
  • 没有命名冲突
  • 命名空间树可以尽可能深,您是自己复杂性的所有者。
  • 您可以有几个js文件实际上对同一命名空间级别做出贡献。这在辅助工具上特别有用。
  • 根据您在页面上包含的javascript文件,您可以拥有更大或更小的应用程序命名空间。

结论:

Web环境真的很容易,我们不应该把它复杂化。

我并不是针对RequireJS,别误会了。它做到了它的本意(本质上是纯资源惰性加载)。其他所有东西也随之而来,但也可以以更清晰透明的方式完成。

因此,如果我不需要主要功能,那么使用它就没有意义。

干杯!


将你的JavaScript定义为模块是一种推荐的最佳实践。我不相信任何一个框架可以解决所有问题,但React是一个很好的解决方案,它提供了向后兼容性,适用于不支持模块加载的浏览器。你费尽心思避免使用你没有时间理解的东西,结果得到了一个不太理想的模式。如果你坚持不使用require,你至少应该学习一下立即调用函数表达式,以及它们如何帮助你安全地定义JavaScript中的命名空间。 - BentOnCoding
你正在评论一篇将近3年的旧文章。无论如何,我承认这不是一个好方法,但更多的是在沮丧中释放出来 :) 我从未实现过任何这样的东西,实际上,几个月后我们把所有东西都转移到了AngularJS,也没有使用RequireJS了。 总之,所有的决定都不是因为不理解工具,而恰恰相反。 - AlexCode

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接