我是Angular.js的新手,试图理解它与Backbone.js的不同之处……我们曾经在使用Backbone时使用Require.js来管理我们的包依赖关系。对Angular.js做同样的事情有意义吗?


当前回答

在AngularJS中使用RequireJS是有意义的,但前提是你要了解它们各自在依赖注入方面的工作原理,因为尽管它们都注入依赖,但它们注入的东西非常不同。

AngularJS有自己的依赖系统,它允许你将AngularJS模块注入到新创建的模块中,以便重用实现。假设你创建了一个"first"模块,它实现了AngularJS过滤器"greet":

angular
  .module('first', [])
  .filter('greet', function() {
    return function(name) {
      return 'Hello, ' + name + '!';
    }
  });

现在假设你想在另一个叫做“second”的模块中使用“greet”过滤器,这个模块实现了一个“goodbye”过滤器。你可以把"first"模块注入到"second"模块:

angular
  .module('second', ['first'])
  .filter('goodbye', function() {
    return function(name) {
      return 'Good bye, ' + name + '!';
    }
  });

问题是,为了在没有RequireJS的情况下正常工作,你必须确保在创建“第二个”AngularJS模块之前,页面上已经加载了“第一个”AngularJS模块。引用文件:

依赖于一个模块意味着需要加载所需的模块 在需要的模块加载之前。

在这个意义上,RequireJS可以帮助您,因为RequireJS提供了一种将脚本注入页面的干净方法,帮助您组织脚本之间的依赖关系。

回到“第一个”和“第二个”AngularJS模块,下面是如何使用RequireJS将模块分离到不同的文件中,以利用脚本依赖项加载:

// firstModule.js file
define(['angular'], function(angular) {
  angular
    .module('first', [])
    .filter('greet', function() {
      return function(name) {
        return 'Hello, ' + name + '!';
      }
    });
});
// secondModule.js file
define(['angular', 'firstModule'], function(angular) {
  angular
    .module('second', ['first'])
    .filter('goodbye', function() {
      return function(name) {
        return 'Good bye, ' + name + '!';
      }
    });
});

你可以看到,在RequireJS回调的内容被执行之前,我们依赖于“firstModule”文件被注入,这需要加载“第一个”AngularJS模块来创建“第二个”AngularJS模块。

边注:在“firstModule”和“secondModule”文件中注入“angular”,作为在RequireJS回调函数中使用AngularJS的依赖项,它必须在RequireJS配置中配置,以将“angular”映射到库代码中。你也可以用传统的方式(script标签)将AngularJS加载到页面中,尽管这与RequireJS的优点相违背。

更多关于AngularJS 2.0内核支持RequireJS的细节,请见我的博文。

根据我的博客文章“用AngularJS理解RequireJS”,这里有一个链接。

其他回答

重申一下我认为OP的问题真正是什么:

如果我主要用Angular 1构建应用程序。在Grunt/Gulp/Broccoli和Bower/NPM时代(隐式地)这样做,并且我可能有一些额外的库依赖,Require是否比我使用Angular不使用Require所获得的价值更清晰、更具体?

或者换句话说:

“如果我有其他处理基本脚本加载的方法,那么普通的Angular是否需要Require来有效地管理基本的Angular组件加载呢?”

我相信最基本的答案是:“除非你有别的事情要做,或者你不能使用更新、更现代的工具。”

让我们从一开始就明确一点:RequireJS是一个很棒的工具,它解决了一些非常重要的问题,并让我们走上了一条更可伸缩、更专业的Javascript应用程序的道路。重要的是,这是许多人第一次接触到模块化的概念以及将事物从全局范围中解脱出来的概念。如果你要构建一个需要伸缩的Javascript应用,Require和AMD模式都是不错的工具。

但是,Angular中有什么特别的地方让Require/AMD特别适合它吗?不。事实上,Angular为你提供了它自己的模块化和封装模式,这在很多方面使AMD的基本模块化特性变得多余。而且,把Angular模块集成到AMD模式中也不是不可能,但是有点……挑剔。您肯定会花时间将这两个模式很好地集成起来。

Angular团队本身的一些观点是这样的,来自Angular Batarang的作者,现在是Angular核心团队的一员Brian Ford:

我不推荐AngularJS使用RequireJS。虽然这当然是可能的,但我还没有看到RequireJS在实践中有任何好处的实例。

所以,关于AngularJS这个非常具体的问题:Angular和Require/AMD是正交的,在某些地方是重叠的。你可以把它们一起使用,但这与Angular本身的性质/模式没有特别的关系。

但是如何对可伸缩Javascript应用程序的内部和外部依赖关系进行基本管理呢?难道Require没有为我做一些非常重要的事情吗?

我建议你看看Bower和NPM,尤其是NPM。我并不是要开始一场关于这些工具的相对好处的圣战。我只是想说:还有其他方法来剥那只猫的皮,这些方法可能比AMD/Require更好。(它们在2015年末肯定会更受欢迎,特别是NPM,结合ES6或CommonJS模块。参见相关SO问题。)

那么惰性加载呢?

Note that lazy-loading and lazy-downloading are different. Angular's lazy-loading doesn't mean you're pulling them direct from the server. In a Yeoman-style application with javascript automation, you're concatenating and minifying the whole shebang together into a single file. They're present, but not executed/instantiated until needed. The speed and bandwidth improvements you get from doing this vastly, vastly outweigh any alleged improvements from lazy-downloading a particular 20-line controller. In fact, the wasted network latency and transmission overhead for that controller is going to be an order of magnitude greater than the size of the controller itself.

但是,假设您确实需要惰性下载,也许是应用程序中不经常使用的部分,比如管理界面。这是一个非常合理的案例。Require确实可以为你做到这一点。但也有许多其他可能更灵活的选择来完成同样的事情。Angular 2.0显然会帮我们解决这个问题,它内置在路由器中。(细节)。

但是在我的本地开发boxen上进行开发时呢?

我如何才能得到我所有的几十/数百个脚本文件加载,而不需要将它们都附加到index.html手动?

Have a look at the sub-generators in Yeoman's generator-angular, or at the automation patterns embodied in generator-gulp-angular, or at the standard Webpack automation for React. These provide you a clean, scalable way to either: automatically attach the files at the time that components are scaffolded, or to simply grab them all automatically if they are present in certain folders/match certain glob-patterns. You never again need to think about your own script-loading once you've got the latter options.

底线?

Require对于某些事情来说是一个很好的工具。但只要有可能,就顺其自然,尽可能把你的关注点分开。让Angular担心自己的模块化模式,考虑使用ES6模块或CommonJS作为通用的模块化模式。让现代自动化工具来操心脚本加载和依赖关系管理。以细粒度的方式处理异步延迟加载,而不是将其与其他两个问题纠缠在一起。

也就是说,如果你正在开发Angular应用,但由于某些原因不能在你的机器上安装Node来使用Javascript自动化工具,那么Require可能是一个很好的替代解决方案。我也见过一些非常复杂的设置,人们想动态加载Angular组件,每个组件都声明了自己的依赖项之类的。虽然我可能会尝试用另一种方式解决这个问题,但我可以看到这个想法的优点,在这种非常特殊的情况下。

但除此之外……当你从头开始创建一个新的Angular应用程序,并灵活地创建一个现代自动化环境时……你还有很多其他更灵活、更现代的选择。

(不断更新,以跟上不断发展的JS场景。)

是的,使用angular.js和require.js是有意义的,其中你可以使用require.js来模块化组件。

有一个种子项目同时使用了angular.js和require.js。

正如@ganaraj提到的,AngularJS的核心是依赖注入。在使用和不使用RequireJS构建玩具种子应用程序时,我个人发现RequireJS对于大多数用例来说可能是多余的。

That doesn't mean RequireJS is not useful for it's script loading capabilities and keeping your codebase clean during development. Combining the r.js optimizer (https://github.com/jrburke/r.js) with almond (https://github.com/jrburke/almond) can create a very slim script loading story. However since its dependency management features are not as important with angular at the core of your application, you can also evaluate other client side (HeadJS, LABjs, ...) or even server side (MVC4 Bundler, ...) script loading solutions for your particular application.

我认为这是一个主观的问题,所以我将提供我的主观意见。

Angular有一个内置的模块化机制。当你创建应用程序时,你要做的第一件事是

var app = angular.module("myApp");

然后

app.directive(...);

app.controller(...);

app.service(...);

如果你看一下angular-seed,它是angular的入门级应用,它将指令、服务、控制器等分离到不同的模块中,然后将这些模块作为依赖项加载到你的主应用中。

比如:

var app = angular.module("myApp",["Directives","Controllers","Services"];

Angular也会惰性加载这些模块(到内存中),而不是它们的脚本文件。

至于延迟加载脚本文件,坦率地说,除非你写的是非常大的东西,否则它会是一个过度的,因为angular本质上减少了你编写的代码量。在大多数其他框架中编写的典型应用,如果用angular编写,可以减少大约30-50%的LOC。

是的,专门为大型SPA服务。

在某些场景中,RequireJS是必须的。例如,我使用AngularJS开发PhoneGap应用程序,它也使用谷歌Map API。如果没有像RequireJS这样的AMD加载器,应用程序只会在离线时启动时崩溃,因为它不能来源谷歌地图API脚本。AMD加载器让我有机会向用户显示错误消息。

然而,AngularJS和RequireJS之间的集成有点棘手。我创建了angularAMD,使这个过程不那么痛苦:

http://marcoslin.github.io/angularAMD/