假设我熟悉在jQuery中开发客户端应用程序,但现在我想开始使用AngularJS。你能描述一下必要的范式转变吗?以下几个问题可能有助于您确定答案:
如何以不同的方式构建和设计客户端web应用程序?最大的区别是什么?我应该停止做什么/使用什么;我应该开始做什么?是否有服务器端的考虑/限制?
我不想详细比较jQuery和AngularJS。
假设我熟悉在jQuery中开发客户端应用程序,但现在我想开始使用AngularJS。你能描述一下必要的范式转变吗?以下几个问题可能有助于您确定答案:
如何以不同的方式构建和设计客户端web应用程序?最大的区别是什么?我应该停止做什么/使用什么;我应该开始做什么?是否有服务器端的考虑/限制?
我不想详细比较jQuery和AngularJS。
迫切的→ 声明的
在jQuery中,选择器用于查找DOM元素,然后将事件处理程序绑定/注册到这些元素。当事件触发时,执行该(命令性)代码以更新/更改DOM。
在AngularJS中,您需要考虑视图而不是DOM元素。视图是包含AngularJS指令的(声明性)HTML。指令在幕后为我们设置事件处理程序,并为我们提供动态数据绑定。选择器很少使用,因此对ID(以及某些类型的类)的需求大大减少。视图与模型绑定(通过范围)。视图是模型的投影。事件会更改模型(即数据、范围财产),并且投影这些模型的视图会“自动”更新
在AngularJS中,考虑模型,而不是jQuery选择的保存数据的DOM元素。将视图视为这些模型的投影,而不是注册回调来操纵用户所看到的内容。
关注点分离
jQuery采用了不引人注目的JavaScript——行为(JavaScript)与结构(HTML)分离。
AngularJS使用控制器和指令(每一个都可以有自己的控制器,和/或编译和链接函数)从视图/结构(HTML)中删除行为。Angular还提供服务和过滤器来帮助分离/组织应用程序。
另请参见https://stackoverflow.com/a/14346528/215945
应用程序设计
设计AngularJS应用程序的一种方法:
想想你的模型。为这些模型创建服务或您自己的JavaScript对象。想想你想如何展示你的模型——你的观点。使用必要的指令为每个视图创建HTML模板以获得动态数据绑定。将控制器连接到每个视图(使用ng视图和布线或ng控制器)。让控制器仅查找/获取视图执行其任务所需的任何模型数据。使控制器尽可能薄。
原型继承
在不了解JavaScript原型继承的工作原理的情况下,您可以使用jQuery做很多工作。在开发AngularJS应用程序时,如果您对JavaScript继承有很好的理解,您将避免一些常见的陷阱。推荐阅读:AngularJS中范围原型/原型继承的细微差别是什么?
你能描述一下必要的范式转变吗?
命令与声明
使用jQuery,您可以一步一步地告诉DOM需要发生什么。使用AngularJS,您可以描述您想要的结果,但不描述如何实现。还有,看看马克·拉乔克的答案。
如何以不同的方式构建和设计客户端web应用程序?
AngularJS是一个使用MVC模式的完整客户端框架(查看它们的图形表示)。它非常注重关注点的分离。
最大的区别是什么?我应该停止做什么/使用什么;我应该开始做什么?
jQuery是一个库
AngularJS是一个漂亮的客户端框架,高度可测试,它结合了许多很酷的东西,如MVC、依赖注入、数据绑定等等。
它专注于关注点和测试的分离(单元测试和端到端测试),这有助于测试驱动开发。
最好的开始方式是完成他们很棒的教程。你可以在几个小时内完成这些步骤;然而,如果你想掌握幕后的概念,它们包括大量的参考资料以供进一步阅读。
是否有服务器端的考虑/限制?
您可以在已经使用纯jQuery的现有应用程序上使用它。然而,如果您想充分利用AngularJS特性,可以考虑使用RESTful方法对服务器端进行编码。
这样做将允许您利用他们的资源工厂,这将创建服务器端RESTful API的抽象,并使服务器端调用(获取、保存、删除等)变得异常简单。
1.不要设计页面,然后使用DOM操作更改页面
在jQuery中,您设计一个页面,然后使其成为动态的。这是因为jQuery是为扩充而设计的,并且从这个简单的前提下得到了难以置信的发展。
但在AngularJS中,您必须从一开始就考虑到您的架构。与其一开始就想着“我有这一块DOM,我想让它做X”,不如从你想完成的事情开始,然后开始设计你的应用程序,最后开始设计视图。
2.不要用AngularJS扩充jQuery
类似地,不要从jQuery执行X、Y和Z的想法开始,所以我将在模型和控制器的基础上添加AngularJS。当你刚开始时,这真的很吸引人,这就是为什么我总是建议新的AngularJS开发人员不要使用jQuery,至少在他们习惯了“Angular方式”之前。
我在这里和邮件列表上看到过许多开发人员使用jQuery插件创建了这些复杂的解决方案,这些插件包含150或200行代码,然后通过一系列令人困惑和费解的回调和$apply粘贴到AngularJS中;但他们最终成功了!问题是,在大多数情况下,jQuery插件可以在AngularJS中用一小部分代码重写,突然间一切都变得简单明了。
底线是:在解决问题时,首先“用AngularJS思考”;如果你想不出解决办法,那就问问社区;如果所有这些都没有简单的解决方案,那么就可以使用jQuery。但不要让jQuery成为拐杖,否则你永远不会掌握AngularJS。
3.始终从架构角度思考
首先要知道单页应用程序是应用程序。它们不是网页。因此,除了像客户端开发人员一样思考之外,我们还需要像服务器端开发人员那样思考。我们必须考虑如何将应用程序划分为单独的、可扩展的、可测试的组件。
那么你是怎么做到的?你如何“用AngularJS思考”?与jQuery相比,以下是一些一般原则。
该视图是“官方记录”
在jQuery中,我们以编程方式更改视图。我们可以将下拉菜单定义为ul,如下所示:
<ul class="main-menu">
<li class="active">
<a href="#/home">Home</a>
</li>
<li>
<a href="#/menu1">Menu 1</a>
<ul>
<li><a href="#/sm1">Submenu 1</a></li>
<li><a href="#/sm2">Submenu 2</a></li>
<li><a href="#/sm3">Submenu 3</a></li>
</ul>
</li>
<li>
<a href="#/home">Menu 2</a>
</li>
</ul>
在jQuery中,在我们的应用程序逻辑中,我们将使用类似以下内容激活它:
$('.main-menu').dropdownMenu();
当我们仅仅查看视图时,并不能立即看出这里有任何功能。对于小型应用程序,这很好。但对于非平凡的应用程序,事情很快就会变得混乱,难以维护。
然而,在AngularJS中,视图是基于视图的功能的官方记录。我们的ul声明应该是这样的:
<ul class="main-menu" dropdown-menu>
...
</ul>
这两种方法相同,但在AngularJS版本中,任何查看模板的人都知道应该发生什么。每当开发团队的新成员加入时,她都可以看到这个,然后知道上面有一个叫做dropdownMenu的指令;她不需要凭直觉找到正确的答案或筛选任何代码。景色告诉我们应该发生什么。干净多了。
刚接触AngularJS的开发人员经常会问这样一个问题:我如何找到所有特定类型的链接并向它们添加指令。当我们回答:“你没有”时,开发人员总是大吃一惊。但你不这么做的原因是,这就像一半的jQuery,一半的AngularJS,没有好处。这里的问题是,开发人员试图在AngularJS的上下文中“执行jQuery”。这永远不会奏效。这是官方记录。在指令之外(下面将详细介绍),您永远不会更改DOM。并且在视图中应用了指令,所以意图很明确。
记住:不要设计,然后标记。你必须先设计,然后再设计。
数据绑定
这是迄今为止AngularJS最令人敬畏的特性之一,它省去了我在上一节中提到的许多DOM操作。AngularJS将自动更新您的视图,因此您不必这样做!在jQuery中,我们响应事件,然后更新内容。类似于:
$.ajax({
url: '/myEndpoint.json',
success: function ( data, status ) {
$('ul#log').append('<li>Data Received!</li>');
}
});
对于这样的视图:
<ul class="messages" id="log">
</ul>
除了混合关注之外,我们还存在我之前提到的表示意图的相同问题。但更重要的是,我们必须手动引用和更新DOM节点。如果我们想删除日志条目,我们也必须针对DOM进行编码。除了DOM之外,我们如何测试逻辑?如果我们想更改演示文稿呢?
这有点凌乱,有点脆弱。但在AngularJS中,我们可以这样做:
$http( '/myEndpoint.json' ).then( function ( response ) {
$scope.log.push( { msg: 'Data Received!' } );
});
我们的观点如下:
<ul class="messages">
<li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>
但就这一点而言,我们的观点可能是这样的:
<div class="messages">
<div class="alert" ng-repeat="entry in log">
{{ entry.msg }}
</div>
</div>
现在,我们不再使用无序列表,而是使用Bootstrap警报框。而且我们从不需要更改控制器代码!但更重要的是,无论日志在何处或如何更新,视图也会发生变化。自动地整洁的
虽然我没有在这里展示,但数据绑定是双向的。因此,只需执行以下操作,这些日志消息也可以在视图中编辑:<input ng model=“entry.msg”/>。人们非常高兴。
不同的模型层
在jQuery中,DOM有点像模型。但是在AngularJS中,我们有一个单独的模型层,可以用我们想要的任何方式来管理,完全独立于视图。这有助于实现上述数据绑定,保持关注点的分离,并引入更大的可测试性。其他答案都提到了这一点,所以我就到此为止。
关注点分离
以上所有这些都与这个总的主题相联系:将你的关注点分开。你的观点充当了(大部分情况下)应该发生的事情的官方记录;你的模型代表你的数据;您有一个服务层来执行可重用的任务;您可以进行DOM操作,并使用指令扩充视图;然后用控制器把它们粘在一起。其他答案中也提到了这一点,我唯一要补充的是可测试性,我将在下面的另一节中讨论。
依赖注入
依赖注入(DI)可以帮助我们分离关注点。如果你来自服务器端语言(从Java到PHP),你可能已经熟悉了这个概念,但如果你是来自jQuery的客户端人员,这个概念对时髦人士来说可能会显得从愚蠢到多余。但事实并非如此。:-)
从广泛的角度来看,DI意味着您可以非常自由地声明组件,然后从任何其他组件中,只需请求它的一个实例,它就会被授予。您不必知道加载顺序、文件位置或诸如此类的信息。功率可能不会立即显现,但我只提供一个(常见的)示例:测试。
假设在我们的应用程序中,我们需要一个通过REST API实现服务器端存储的服务,根据应用程序状态,还需要本地存储。在控制器上运行测试时,我们不想与服务器通信——毕竟我们在测试控制器。我们只需添加一个与原始组件同名的模拟服务,注入器将确保我们的控制器自动获得假的服务-我们的控制器不需要知道区别。
说到测试。。。
4.测试驱动开发-始终
这确实是关于架构的第3节的一部分,但它非常重要,我将其作为自己的顶级部分。
在您见过、使用过或编写过的众多jQuery插件中,有多少插件附带了测试套件?不太多,因为jQuery不太适合这样。但AngularJS是。
在jQuery中,测试的唯一方法通常是使用示例/演示页面独立创建组件,我们的测试可以对其执行DOM操作。因此,我们必须单独开发一个组件,然后将其集成到应用程序中。多么不方便!很多时候,在使用jQuery开发时,我们选择迭代开发,而不是测试驱动开发。谁能责怪我们呢?
但是因为我们有关注点分离,所以我们可以在AngularJS中迭代地进行测试驱动开发!例如,假设我们想要一个超级简单的指令来在菜单中指明当前的路由。我们可以在应用程序的视图中声明我们想要的:
<a href="/hello" when-active>Hello</a>
好了,现在我们可以为不存在的when-active指令编写测试:
it( 'should add "active" when the route changes', inject(function() {
var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );
$location.path('/not-matching');
expect( elm.hasClass('active') ).toBeFalsey();
$location.path( '/hello' );
expect( elm.hasClass('active') ).toBeTruthy();
}));
当我们运行测试时,我们可以确认它失败了。只有现在我们才能创建指令:
.directive( 'whenActive', function ( $location ) {
return {
scope: true,
link: function ( scope, element, attrs ) {
scope.$on( '$routeChangeSuccess', function () {
if ( $location.path() == element.attr( 'href' ) ) {
element.addClass( 'active' );
}
else {
element.removeClass( 'active' );
}
});
}
};
});
我们的测试现在通过了,菜单按照要求运行。我们的开发是迭代和测试驱动的。酷毙了。
5.概念上,指令不是打包的jQuery
您经常会听到“只在指令中执行DOM操作”。这是必要的。以应有的尊重对待它!
但让我们再深入一点。。。
有些指令只是修饰视图中已经存在的内容(想想ngClass),因此有时直接进行DOM操作,然后基本上就完成了。但是,如果一个指令像一个“小部件”,并且有一个模板,那么它也应该尊重关注点的分离。也就是说,模板也应该在很大程度上独立于其在链接和控制器功能中的实现。
AngularJS附带了一整套工具,让这一切变得非常简单;使用ngClass,我们可以动态更新类;ngModel允许双向数据绑定;ngShow和ngHide以编程方式显示或隐藏元素;还有很多——包括我们自己写的那些。换句话说,我们可以在没有DOM操作的情况下实现各种令人惊叹的功能。DOM操作越少,指令就越容易测试,它们就越容易样式化,将来就越容易更改,并且它们就越易于重用和分发。
我看到很多刚接触AngularJS的开发人员使用指令作为抛出一堆jQuery的地方。换句话说,他们认为“由于我不能在控制器中进行DOM操作,所以我将把代码放在指令中”。虽然这当然好得多,但往往还是错误的。
想想我们在第3节中编程的记录器。即使我们把它放在一个指令中,我们仍然希望以“角度方式”来做。它仍然不需要任何DOM操作!有很多时候DOM操作是必要的,但这比你想象的要难得多!在应用程序的任何地方进行DOM操作之前,问问自己是否真的需要。也许有更好的方法。
下面是一个快速示例,显示了我最常看到的模式。我们想要一个可切换的按钮。(注意:这个例子有点做作,有点冗长,以表示以完全相同的方式解决的更复杂的情况。)
.directive( 'myDirective', function () {
return {
template: '<a class="btn">Toggle me!</a>',
link: function ( scope, element, attrs ) {
var on = false;
$(element).click( function () {
on = !on;
$(element).toggleClass('active', on);
});
}
};
});
这有一些问题:
首先,jQuery从来都不是必需的。我们在这里做的一切都不需要jQuery!第二,即使我们的页面上已经有jQuery,也没有理由在这里使用它;我们可以简单地使用angular.element,当我们的组件被放到没有jQuery的项目中时,它仍然可以工作。第三,即使假设该指令需要jQuery才能工作,jqLite(angular.element)在加载jQuery时也将始终使用它!所以我们不需要使用$,我们可以只使用angular.element。第四,与第三个密切相关的是,jqLite元素不需要包装在$中——传递给链接函数的元素已经是jQuery元素了!第五,我们在前面的章节中已经提到过,为什么我们要将模板内容混合到逻辑中?
这个指令可以被重写(甚至对于非常复杂的情况!),非常简单,如下所示:
.directive( 'myDirective', function () {
return {
scope: true,
template: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
link: function ( scope, element, attrs ) {
scope.on = false;
scope.toggle = function () {
scope.on = !scope.on;
};
}
};
});
同样,模板内容在模板中,因此您(或您的用户)可以很容易地将其替换为符合任何必要样式的模板,并且逻辑永远不必修改。可重用性-繁荣!
还有其他所有的好处,比如测试——这很简单!无论模板中有什么,指令的内部API都不会被触及,因此重构很容易。您可以根据需要更改模板,而无需触摸指令。不管你做了什么改变,你的考试还是通过的。
w00t!
那么,如果指令不仅仅是jQuery类函数的集合,它们是什么呢?指令实际上是HTML的扩展。如果HTML不做你需要做的事情,你可以编写一个指令来为你做,然后就像它是HTML的一部分一样使用它。
换言之,如果AngularJS没有做一些开箱即用的事情,那么考虑一下团队将如何完成它,以适应ngClick、ngClass等。
总结
甚至不要使用jQuery。甚至不要包括它。它会拖你后腿。当你遇到一个你认为你已经知道如何在jQuery中解决的问题时,在你拿到$之前,试着想想如何在AngularJS的范围内解决它。如果你不知道,问问!20次中有19次,最好的解决方法不需要jQuery,并且尝试用jQuery解决它会为您带来更多的工作。
它们是苹果和橘子。你不想比较它们。它们是两种不同的东西。AngularJs已经内置了jQuery lite,它允许您执行基本的DOM操作,甚至不包括完整的jQuery版本。
jQuery完全是关于DOM操作的。它解决了所有跨浏览器的问题,否则您将不得不处理,但它不是一个允许您将应用程序划分为AngularJS等组件的框架。
AngularJs的一个优点是它允许您在指令中分离/隔离DOM操作。有内置指令可供您使用,例如ng-click。您可以创建自己的自定义指令,这些指令将包含所有视图逻辑或DOM操作,这样您就不会在应该处理业务逻辑的控制器或服务中混合DOM操作代码。
Angular将应用程序分解为-控制器-服务-视图-等等。
还有一件事,那就是指令。它是一个可以附加到任何DOM元素的属性,您可以在其中使用jQuery,而不必担心jQuery会与AngularJs组件冲突或破坏其架构。
我从我参加的一次会议上听到,Angular的创始人之一表示,他们非常努力地将DOM操作分离出来,所以不要试图将它们重新纳入其中。
我觉得这个问题很有趣,因为我第一次认真接触JavaScript编程是Node.js和AngularJS。我从未学习过jQuery,我想这是一件好事,因为我不必忘记任何东西。事实上,我积极避免使用jQuery解决我的问题,而是只寻找一种“AngularJS方法”来解决它们。因此,我想我对这个问题的回答基本上可以归结为,“像从未学习过jQuery的人一样思考”,并避免任何直接合并jQuery的诱惑(显然AngularJS在某种程度上在幕后使用了它)。
jQuery是一个DOM操作库。
AngularJS是一个MV*框架。
事实上,AngularJS是为数不多的JavaScript MV*框架之一(许多JavaScript MVC工具仍然属于类别库)。
作为一个框架,它托管您的代码,并决定何时调用什么!
AngularJS本身包含一个jQuery-lite版本。因此,对于一些基本的DOM选择/操作,您真的不必包含jQuery库(它节省了许多在网络上运行的字节)
AngularJS为DOM操作和设计可重用UI组件提供了“指令”的概念,因此当您觉得需要做与DOM操作相关的事情时,应该使用它(指令是在使用AngularJS时编写jQuery代码的唯一地方)。
AngularJS涉及一些学习曲线(不仅仅是jQuery:-)。
-->对于任何来自jQuery背景的开发人员,我的第一个建议是“在跳到像AngularJS这样的富框架之前,将JavaScript作为第一类语言学习!”我很难了解到上述事实。
祝你好运
jQuery:您对DOM元素的“QUERYing the DOM”进行了很多思考,并做了一些事情。
AngularJS:模型是事实,你总是从这个角度思考。
例如,在jQuery中,当您从THE服务器获取要以某种格式显示在DOM中的数据时,需要“1”。FIND”在DOM中要放置此数据的位置,即“2”。通过创建新节点或仅设置其innerHTML,UPDATE/APPEND”。然后,当您要更新此视图时,请选择“3”。查找位置和“4”。更新”。在AngularJS中,在从服务器获取和格式化数据的同一上下文中完成的查找和更新循环已经消失。
有了AngularJS,你就有了你的模型(你已经习惯了JavaScript对象),模型的值告诉了你关于模型(显然)和视图的信息,模型上的操作会自动传播到视图,所以你不必考虑它。你会发现自己在Angular JS中不再在DOM中找到东西。
换言之,在jQuery中,您需要考虑CSS选择器,也就是说,具有类或属性等的div或td在哪里,这样我就可以获得它们的HTML或颜色或值,但在AngularJS中,您会发现自己这样想:我在处理什么模型,我会将模型的值设置为true。您不必担心反映该值的视图是复选框还是驻留在td元素中(在jQuery中通常需要考虑的细节)。
通过AngularJS中的DOM操作,您发现自己添加了指令和过滤器,可以将其视为有效的HTML扩展。
在AngularJS中,你会体验到另一件事:在jQuery中,你经常调用jQuery函数,在AngularJS中,AngularJS会调用你的函数,所以AngularJS将“告诉你怎么做”,但好处是值得的,所以学习AngularJS通常意味着学习AngularJS想要什么,或者AngularJS要求你呈现函数的方式,它会相应地调用它。这是AngularJS成为框架而不是库的原因之一。
请收听播客JavaScript Jabber:第32集,其中介绍了AngularJS的原创创作者:Misko Hevery&Igor Minar。他们谈论了很多来自其他JavaScript背景,特别是jQuery的AngularJS的感觉。
播客中的一点让我对你的问题有了很多看法:
MISKO:[…]在Angular中,我们很难想到的一件事是,我们如何提供大量逃生舱口,以便您能够逃出去,并基本上找到解决问题的方法。对我们来说,答案是“指令”。有了指令,你就基本上变成了一个普通的小jQueryJavaScript,你可以做任何你想做的事情。IGOR:所以把指令想象成编译器的指令,当你在模板中遇到这个特定的元素或这个CSS时,它会告诉你,你保留这类代码,而这类代码负责DOM树中元素及其下面的所有内容。
可以通过上面提供的链接获得整个事件的记录。
所以,直接回答你的问题:AngularJS非常固执己见,是一个真正的MV*框架。然而,您仍然可以在指令中使用jQuery完成所有您知道和喜爱的非常酷的事情。这不是“我如何做我在jQuery中曾经做过的事情?”的问题,而是“我如何用我在jQuery中曾经做的所有事情来补充AngularJS?”
这是两种截然不同的心态。
jQuery
jQuery使getElementByHerpDerp等长得离谱的JavaScript命令变得更短,并且跨浏览器。
角度JS
AngularJS允许您制作自己的HTML标记/属性,这些标记/属性可以很好地处理动态web应用程序(因为HTML是为静态页面设计的)。
编辑:
说“我有jQuery背景,我在AngularJS中如何思考?”就像说“我拥有HTML背景,我如何在JavaScript中思考?”你问这个问题的事实表明你很可能不理解这两个资源的基本目的。这就是为什么我选择通过简单地指出根本区别来回答这个问题,而不是通过列表来回答“AngularJS使用指令,而jQuery使用CSS选择器来创建一个jQuery对象,该对象执行这和那等操作……”。这个问题不需要冗长的回答。
jQuery是一种使浏览器中的JavaScript编程更容易的方法。更短的跨浏览器命令等。
AngularJS扩展了HTML,因此您不必为了创建应用程序而到处放置<div>。它使HTML实际上适用于应用程序,而不是它的设计目的,即静态的教育网页。它使用JavaScript以迂回的方式实现了这一点,但基本上它是HTML的扩展,而不是JavaScript。
为了描述“范式转变”,我认为一个简短的答案就足够了。
AngularJS改变了查找元素的方式
在jQuery中,通常使用选择器来查找元素,然后将它们连接起来:$('#id.class').click(doStuff);
在AngularJS中,您使用指令直接标记元素,将它们连接起来:<a ng click=“doStuff()”>
AngularJS不需要(也不希望)使用选择器查找元素——AngularJS的jqLite与完整的jQuery之间的主要区别是jqLite不支持选择器。
因此,当人们说“根本不包括jQuery”时,主要是因为他们不希望您使用选择器;他们希望你学会使用指令。直接,而不是选择!
AngularJS与jQuery
AngularJS和jQuery采用了截然不同的意识形态。如果您来自jQuery,您可能会发现其中的一些差异令人惊讶。Angular可能会让你生气。
这很正常,你应该撑过去。Angular值得。
最大差异(TLDR)
jQuery为您提供了一个工具包,用于选择DOM的任意位并对其进行特殊更改。你可以一件一件地做任何你喜欢的事。
AngularJS为您提供了一个编译器。
这意味着AngularJS从上到下读取整个DOM,并将其视为代码,即编译器的指令。当它遍历DOM时,它会查找特定的指令(编译器指令),这些指令告诉AngularJS编译器如何行为和做什么。指令是充满JavaScript的小对象,可以与属性、标记、类甚至注释相匹配。
当Angular编译器确定DOM的一部分与特定指令匹配时,它调用指令函数,将DOM元素、任何属性、当前的$scope(它是一个本地变量存储)和一些其他有用的位传递给它。这些属性可能包含指令可以解释的表达式,这些表达式告诉指令如何渲染,以及何时重新绘制自己。
然后,指令可以依次引入额外的Angular组件,如控制器、服务等。编译器的底部是一个完整的web应用程序,已连接好并准备就绪。
这意味着Angular是模板驱动的。您的模板驱动JavaScript,而不是相反。这是一个角色的彻底逆转,与我们过去10年来一直在编写的不引人注目的JavaScript完全相反。这可能需要一些时间来适应。
如果这听起来像是过度规范和限制,那么没有什么比这更接近事实了。因为AngularJS将HTML视为代码,所以您可以在web应用程序中获得HTML级别的粒度。一切皆有可能,一旦你在概念上有了一些飞跃,大多数事情都会变得异常容易。
让我们进入实质。
首先,Angular不能取代jQuery
Angular和jQuery做不同的事情。AngularJS为您提供了一套生成web应用程序的工具。jQuery主要为您提供修改DOM的工具。如果jQuery出现在页面上,AngularJS将自动使用它。如果不是,AngularJS附带jQueryLite,它是jQuery的精简版,但仍然完全可用。
Misko喜欢jQuery,并不反对你使用它。但是,随着你的进步,你会发现,你可以使用范围、模板和指令的组合完成几乎所有的工作,你应该尽可能喜欢这个工作流,因为你的代码将更离散、更可配置、更具角度。
如果你使用jQuery,你不应该把它洒得到处都是。AngularJS中DOM操作的正确位置在指令中。稍后再详细介绍。
带有选择器的非独占JavaScript与声明性模板
jQuery通常不引人注目地应用。您的JavaScript代码链接在页眉(或页脚)中,这是唯一提到它的地方。我们使用选择器来选择页面的部分,并编写插件来修改这些部分。
JavaScript处于控制状态。HTML具有完全独立的存在。即使没有JavaScript,HTML仍然保持语义。点击属性是非常糟糕的做法。
关于AngularJS,您首先会注意到的一点是,自定义属性无处不在。你的HTML将充斥着ng属性,这些属性本质上是类固醇上的onClick属性。这些是指令(编译器指令),是模板与模型挂钩的主要方式之一。
当你第一次看到这一点时,你可能会想把AngularJS写成老式的侵入式JavaScript(就像我一开始做的那样)。事实上,AngularJS并不遵守这些规则。在AngularJS中,HTML5是一个模板。它由AngularJS编译以生成您的网页。
这是第一个巨大的区别。对于jQuery,您的网页是一个要操作的DOM。对于AngularJS,HTML是要编译的代码。AngularJS读取整个网页,并使用内置编译器将其编译成新网页。
你的模板应该是声明性的;通过阅读它,它的含义应该很清楚。我们使用带有有意义名称的自定义属性。我们用有意义的名称组成新的HTML元素。一个拥有最少HTML知识且没有编码技能的设计师可以阅读您的AngularJS模板并了解它在做什么。他或她可以进行修改。这是角度方式。
模板在驾驶座上。
在启动AngularJS并浏览教程时,我问自己的第一个问题是“我的代码在哪里?”。我没有编写JavaScript,但我有所有这些行为。答案很明显。由于AngularJS编译DOM,AngularJS将您的HTML视为代码。对于许多简单的情况,只需编写一个模板并让AngularJS将其编译为应用程序就足够了。
模板驱动应用程序。它被视为DSL。您编写AngularJS组件,AngularJS将负责根据模板的结构在适当的时间将其拉入并使其可用。这与标准MVC模式非常不同,其中模板仅用于输出。
例如,它比RubyonRails更类似于XSLT。
这是一个彻底的控制反转,需要一些人来适应。
停止尝试从JavaScript驱动应用程序。让模板驱动应用程序,让AngularJS负责将组件连接在一起。这也是角度方式。
语义HTML与语义模型
使用jQuery,HTML页面应该包含有语义意义的内容。如果用户或搜索引擎关闭了JavaScript,您的内容仍然可以访问。
因为AngularJS将HTML页面视为模板。模板不应该是语义的,因为您的内容通常存储在最终来自API的模型中。AngularJS使用模型编译DOM以生成语义网页。
您的HTML源不再是语义的,相反,您的API和编译的DOM是语义的。
在AngularJS中,意味着生活在模型中,HTML只是一个模板,仅供显示。
在这一点上,你可能有关于SEO和可访问性的各种问题,这是正确的。这里有一些公开的问题。大多数屏幕阅读器现在将解析JavaScript。搜索引擎还可以为AJAX内容编制索引。然而,您需要确保您使用的是pushstate URL,并且您有一个像样的站点地图。有关此问题的讨论,请参见此处:https://stackoverflow.com/a/23245379/687677
关注点分离(SOC)与MVC
关注点分离(SOC)是在多年的web开发中形成的一种模式,原因多种多样,包括SEO、可访问性和浏览器不兼容。它看起来像这样:
HTML-语义。HTML应该是独立的。CSS-样式化,没有CSS,页面仍然可读。JavaScript-行为,没有脚本,内容仍然存在。
同样,AngularJS不遵守规则。总之,AngularJS摒弃了十年来公认的智慧,转而实现了一种MVC模式,其中模板不再是语义的,甚至一点都没有。
它看起来像这样:
模型-模型包含语义数据。模型通常是JSON对象。模型作为名为$scope的对象的属性存在。您还可以在$scope上存储方便的实用程序函数,然后模板可以访问这些函数。视图-您的视图是用HTML编写的。视图通常不是语义的,因为数据存在于模型中。控制器-您的控制器是一个JavaScript函数,它将视图挂钩到模型。其功能是初始化$scope。根据您的应用程序,您可能需要也可能不需要创建控制器。一个页面上可以有多个控制器。
MVC和SOC不在同一尺度的两端,它们在完全不同的轴上。SOC在AngularJS上下文中没有意义。你必须忘掉它,继续前进。
如果像我一样,你经历了浏览器大战,你可能会觉得这个想法很令人反感。忘掉它,这是值得的,我保证。
插件与指令
插件扩展jQuery。AngularJS指令扩展了浏览器的功能。
在jQuery中,我们通过向jQuery.prototype中添加函数来定义插件。然后通过选择元素并在结果上调用插件,将这些函数挂接到DOM中。其目的是扩展jQuery的功能。
例如,如果您希望页面上有一个旋转木马,您可以定义一个无序的数字列表,也许用nav元素包装。然后,您可以编写一些jQuery来选择页面上的列表,并将其重新设置为具有超时的库,以进行滑动动画。
在AngularJS中,我们定义指令。指令是一个返回JSON对象的函数。这个对象告诉AngularJS要查找哪些DOM元素,以及要对它们进行哪些更改。指令使用您发明的属性或元素连接到模板中。其想法是用新的属性和元素扩展HTML的功能。
AngularJS的方法是扩展本地HTML的功能。您应该编写类似HTML的HTML,并使用自定义属性和元素进行扩展。
如果您需要旋转木马,只需使用<carousel/>元素,然后定义一个指令来拉入模板,并使该吸盘工作。
大量的小指令与带有配置开关的大插件
jQuery的趋势是编写像lightbox这样的大型插件,然后通过传递大量值和选项来配置这些插件。
这是AngularJS中的一个错误。
以下拉列表为例。在编写下拉插件时,您可能会尝试在单击处理程序中编写代码,或者添加一个向上或向下的V形的函数,或者更改展开元素的类,显示隐藏菜单,所有有用的东西。
直到你想做一个小小的改变。
假设你有一个菜单要在悬停时展开。现在我们有一个问题。我们的插件已经为我们连接了点击处理程序,我们需要添加一个配置选项,以使它在这种特定情况下的行为有所不同。
在AngularJS中,我们编写较小的指令。我们的下拉指令会小得离谱。它可能会保持折叠状态,并提供fold()、unfold(()或toggle()方法。这些方法只需更新$scope.menu.visible,它是一个保存状态的布尔值。
现在,在我们的模板中,我们可以将其连接起来:
<a ng-click="toggle()">Menu</a>
<ul ng-show="menu.visible">
...
</ul>
需要在鼠标悬停时更新?
<a ng-mouseenter="unfold()" ng-mouseleave="fold()">Menu</a>
<ul ng-show="menu.visible">
...
</ul>
模板驱动应用程序,因此我们可以获得HTML级别的粒度。如果我们想逐个例外,那么模板可以让这变得很容易。
结束与$scope
JQuery插件是在闭包中创建的。在该关闭期间保持隐私。这取决于您在该闭包内维护范围链。您实际上只能访问jQuery传递给插件的DOM节点集,以及闭包中定义的任何局部变量和您定义的任何全局变量。这意味着插件是完全独立的。这是一件好事,但在创建整个应用程序时可能会受到限制。试图在动态页面的各个部分之间传递数据成为了一件麻烦事。
AngularJS具有$scope对象。这些是由AngularJS创建和维护的特殊对象,用于存储模型。某些指令将生成一个新的$scope,默认情况下,它使用JavaScript原型继承从其包装$scope继承。$scope对象可在控制器和视图中访问。
这是聪明的部分。因为$scope继承的结构大致遵循DOM的结构,所以元素可以无缝地访问自己的作用域和任何包含的作用域,一直到全局$scope(与全局作用域不同)。
这使得传递数据和在适当级别存储数据变得更加容易。如果展开了下拉列表,则只有下拉列表$scope需要知道它。如果用户更新了他们的首选项,您可能需要更新全局$scope,并且任何侦听用户首选项的嵌套作用域都会自动收到警报。
这听起来可能很复杂,事实上,一旦你放松下来,这就像飞行一样。您不需要创建$scope对象,AngularJS会根据您的模板层次结构正确地为您实例化和配置它。AngularJS然后使用依赖注入的魔力将其提供给您的组件(稍后将详细介绍)。
手动DOM更改与数据绑定
在jQuery中,您可以手动进行所有DOM更改。您可以用编程方式构造新的DOM元素。如果您有一个JSON数组,并且希望将其放入DOM中,则必须编写一个函数来生成HTML并将其插入。
在AngularJS中,您也可以这样做,但我们鼓励您使用数据绑定。更改模型,因为DOM通过模板绑定到它,所以DOM将自动更新,无需干预。
因为数据绑定是从模板中完成的,使用属性或大括号语法,所以这非常容易。与此相关的认知开销很少,所以您会发现自己一直在做。
<input ng-model="user.name" />
将输入元素绑定到$scope.user.name。更新输入将更新当前范围中的值,反之亦然。
同样:
<p>
{{user.name}}
</p>
将在段落中输出用户名。这是一个实时绑定,因此如果更新了$scope.user.name值,模板也会更新。
一直以来都是Ajax
在jQuery中,进行Ajax调用非常简单,但这仍然是一个需要三思的问题。需要考虑的是额外的复杂性,需要维护大量的脚本。
在AngularJS中,Ajax是您的默认解决方案,它总是发生,几乎没有您注意到。可以使用ng include包含模板。您可以使用最简单的自定义指令应用模板。您可以将Ajax调用包装在服务中,并创建自己的GitHub服务或Flickr服务,您可以非常轻松地访问这些服务。
服务对象与助手函数
在jQuery中,如果我们想完成一个与dom无关的小任务,比如从API中提取提要,我们可以在闭包中编写一个小函数来完成。这是一个有效的解决方案,但如果我们想经常访问该提要呢?如果我们想在另一个应用程序中重用这些代码呢?
AngularJS为我们提供了服务对象。
服务是包含函数和数据的简单对象。他们总是单身,这意味着永远不会有超过一个人。假设我们想访问StackOverflow API,我们可以编写一个StackOverflowService,它定义了这样做的方法。
假设我们有一辆购物车。我们可以定义一个ShoppingCartService,它维护我们的购物车,并包含添加和删除项目的方法。因为该服务是一个单一的,并且由所有其他组件共享,所以任何需要的对象都可以写入购物车并从中提取数据。
服务对象是自包含的AngularJS组件,我们可以根据需要使用和重用这些组件。它们是包含函数和数据的简单JSON对象。它们始终是单个的,因此如果您将服务上的数据存储在一个地方,那么只需请求相同的服务,就可以将数据从其他地方取出。
依赖性注射(DI)与分期付款(即去意大利化)
AngularJS为您管理依赖关系。如果您想要一个对象,只需引用它,AngularJS就会为您获取它。
在你开始使用它之前,很难解释这是一个多么大的时间优势。jQuery中没有像AngularJS DI这样的东西。
DI意味着,您不用编写应用程序并将其连接在一起,而是定义一个组件库,每个组件由一个字符串标识。
假设我有一个名为“FlickrService”的组件,它定义了从Flickr提取JSON提要的方法。现在,如果我想编写一个可以访问Flickr的控制器,我只需要在声明控制器时按名称引用“FlickrService”。AngularJS将负责实例化组件并使其可用于我的控制器。
例如,这里我定义了一个服务:
myApp.service('FlickrService', function() {
return {
getFeed: function() { // do something here }
}
});
现在,当我想使用该服务时,我只需按如下名称引用它:
myApp.controller('myController', ['FlickrService', function(FlickrService) {
FlickrService.getFeed()
}]);
AngularJS将认识到需要FlickrService对象来实例化控制器,并将为我们提供一个。
这使得连接起来非常容易,并且几乎消除了任何spagetting的倾向。我们有一个简单的组件列表,当我们需要时,AngularJS会将它们一个一个地交给我们。
模块化服务架构
jQuery很少提到如何组织代码。AngularJS有意见。
AngularJS为您提供了可以放置代码的模块。例如,如果您正在编写一个与Flickr对话的脚本,您可能需要创建一个Flickr模块来包装所有与Flickr相关的功能。模块可以包括其他模块(DI)。您的主要应用程序通常是一个模块,这应该包括您的应用程序所依赖的所有其他模块。
如果你想编写另一个基于Flickr的应用程序,你只需要包含Flickr模块,你就可以在新的应用程序中访问所有与Flickr相关的功能。
模块包含AngularJS组件。当我们包含一个模块时,该模块中的所有组件将作为一个简单的列表提供给我们,该列表由其唯一的字符串标识。然后,我们可以使用AngularJS的依赖注入机制将这些组件相互注入。
综上所述
AngularJS和jQuery不是敌人。在AngularJS中很好地使用jQuery是可能的。如果你很好地使用AngularJS(模板、数据绑定、$scope、指令等),你会发现你需要的jQuery比你可能需要的少得多。
要意识到的主要事情是模板驱动应用程序。停止尝试编写什么都能做的大插件。相反,编写一些只做一件事的指令,然后编写一个简单的模板将它们连接在一起。
少考虑不引人注目的JavaScript,而是考虑HTML扩展。
我的小书
我对AngularJS非常兴奋,我写了一本关于它的短书,欢迎你在线阅读http://nicholasjohnson.com/angular-book/.我希望这有帮助。
这些都是很好但很长的答案。
总结我的经验:
控制器和提供者(服务、工厂等)用于修改数据模型,而不是HTML。HTML和指令定义了模型的布局和绑定。如果需要在控制器之间共享数据,请创建一个服务或工厂-它们是在应用程序中共享的单体。如果您需要HTML小部件,请创建一个指令。如果您有一些数据并且正在尝试更新HTML。。。停下!更新模型,并确保HTML绑定到模型。
作为一个JavaScriptMV*初学者,纯粹专注于应用程序架构(而不是服务器/客户端的问题),我当然会推荐以下资源(我很惊讶还没有提到):AddyOsmani的JavaScript设计模式,作为不同JavaScript设计模式的介绍。本答案中使用的术语取自上述链接文档。我不打算重复在公认的答案中措辞很好的内容。相反,这个答案与支持AngularJS(和其他库)的理论背景联系起来。
像我一样,你会很快意识到AngularJS(或Ember.js、Durandal和其他MV*框架)是一个复杂的框架,它集合了许多不同的JavaScript设计模式。
我还发现,在深入到一个全局框架之前,分别测试(1)本机JavaScript代码和(2)这些模式中的每一个的较小库更容易。这让我更好地理解了框架解决了哪些关键问题(因为你个人面临着这个问题)。
例如:
JavaScript面向对象编程(这是一个谷歌搜索链接)。它不是一个库,但肯定是任何应用程序编程的先决条件。它教会了我原型、构造函数、单例和装饰器模式的本地实现facade模式的jQueryUndercore(如用于操作DOM的WYSIWYG)原型/构造函数/混合模式的Prototype.js模块模式/AMD需要js/Coll.js可观察的发布/订阅模式的KnockoutJS
注:此列表不完整,也不是“最佳库”;它们正好是我用过的图书馆。这些库还包括更多的模式,这些模式只是它们的主要焦点或初衷。如果你觉得这个列表中缺少一些东西,请在评论中提及,我很乐意补充。
实际上,如果您使用的是AngularJS,那么就不再需要jQuery了。AngularJS本身具有绑定和指令,这是一个非常好的“替代品”,可以替代jQuery中的大多数功能。
我通常使用AngularJS和Cordova开发移动应用程序。jQuery中我唯一需要的就是选择器。
通过谷歌搜索,我发现有一个独立的jQuery选择器模块。这是Sizzle。
我决定制作一个小代码片段,帮助我利用jQuery Selector(使用Sizzle)的强大功能快速启动一个使用AngularJS的网站。
我在这里分享了我的代码:https://github.com/huytd/Sizzular
AngularJS和jQuery:
除了JQLite功能之外,AngularJs和JQuery在每个级别上都完全不同,一旦开始学习AngularJ的核心功能,您就会看到它(我将在下面解释)。
AngularJs是一个客户端框架,用于构建独立的客户端应用程序。JQuery是一个围绕DOM运行的客户端库。
AngularJs酷原则-如果您想对UI进行一些更改,请从模型数据更改的角度考虑。更改数据,UI将重新呈现自身。除非几乎不需要DOM,否则不需要每次都使用DOM,这也应该通过Angular Directive来处理。
为了回答这个问题,我想与AngularJS分享我在第一个企业应用程序上的经验。这些是Angular提供的最棒的功能,我们开始改变jQuery的思维方式,我们得到的Angular就像一个框架,而不是库。
双向数据绑定令人惊叹:我有一个具有UPDATE、DELTE和INSERT功能的网格。我有一个使用ng repeat绑定网格模型的数据对象。您只需编写一行简单的JavaScript代码即可进行删除和插入,就可以了。随着网格模型的即时变化,网格会自动更新。更新功能是实时的,没有代码。你感觉太棒了!!!
可重用指令是超级的:在一个地方编写指令并在整个应用程序中使用。天啊!!!我将这些指令用于分页、正则表达式、验证等。这真的很酷!
路由强大:如何使用它取决于您的实现,但它需要很少的代码行来将请求路由到指定HTML和控制器(JavaScript)
控制器很棒:控制器负责自己的HTML,但这种分离对公共功能也很有效。如果您想在主HTML上单击按钮时调用相同的函数,只需在每个控制器中写入相同的函数名并编写单独的代码。
插件:还有许多其他类似的功能,如在应用程序中显示覆盖。您不需要为它编写代码,只需使用wc覆盖插件,这将自动处理所有XMLHttpRequest(XHR)请求。
RESTful体系结构的理想选择:作为一个完整的框架,AngularJS非常适合使用RESTful架构。调用REST CRUD API非常简单
服务:使用服务编写公共代码,在控制器中编写更少的代码。服务可用于在控制器之间共享公共功能。
可扩展性:Angular使用Angular指令扩展了HTML指令。在html中编写表达式,并在运行时对其求值。创建自己的指令和服务,并在另一个项目中使用它们,而无需任何额外的工作。