假设我熟悉在jQuery中开发客户端应用程序,但现在我想开始使用AngularJS。你能描述一下必要的范式转变吗?以下是一些问题,可能有助于你构建答案:
我不是在寻找jQuery和AngularJS之间的详细比较。
jQuery
AngularJS
在jQuery中,选择器用于查找DOM元素,然后将事件处理程序绑定/注册到它们。当事件触发时,该(命令式)代码会执行以更新/更改DOM。
在AngularJS中,你想考虑视图而不是DOM元素。视图是包含AngularJS指令的(声明性)超文本标记语言。指令为我们在幕后设置事件处理程序,并为我们提供动态数据绑定。选择器很少使用,因此对ID(和某些类型的类)的需求大大减少。视图与模型绑定(通过作用域)。视图是模型的投影。事件更改模型(即数据、作用域属性),投影这些模型的视图“自动”更新。
在AngularJS中,考虑模型,而不是保存数据的jQuery选择的DOM元素。将视图视为这些模型的投影,而不是注册回调来操纵用户看到的内容。
jQuery采用不显眼的JavaScript-行为(JavaScript)与结构(超文本标记语言)分离。
AngularJS使用控制器和指令(每个指令都可以有自己的控制器,和/或编译和链接函数)来从视图/结构中删除行为(超文本标记语言)。Angular还有服务和过滤器来帮助分离/组织你的应用程序。
另见https://stackoverflow.com/a/14346528/215945
设计AngularJS应用程序的一种方法:
你可以用jQuery做很多事情,而不需要知道JavaScript原型继承是如何工作的。在开发AngularJS应用程序时,如果你对JavaScript继承有很好的理解,你将避免一些常见的陷阱。推荐阅读:AngularJS中范围原型/原型继承的细微差别是什么?
你能描述一下必要的范式转变吗?
命令式vs宣告式
使用jQuery,您可以一步一步地告诉DOM需要发生什么。使用AngularJS,您可以描述您想要的结果,而不是如何做到这一点。更多关于这个这里。此外,请查看Mark Rajcok的答案。
如何以不同的方式构建和设计客户端Web应用程序?
AngularJS是一个完整的客户端框架,它使用MVC模式(查看它们的图形表示)。它非常关注关注点的分离。
最大的区别是什么?我应该停止做/使用什么;我应该开始做/使用什么?
jQuery是图书馆
AngularJS是一个漂亮的客户端框架,高度可测试,它结合了大量很酷的东西,如MVC、依赖注入、数据绑定等等。
它侧重于关注点分离和测试(单元测试和端到端测试),这有助于测试驱动开发。
最好的开始方式是通过他们很棒的教程。你可以在几个小时内完成这些步骤;然而,如果你想掌握幕后的概念,它们包括无数的参考资料供进一步阅读。
是否有任何服务器端注意事项/限制?
您可以在已经使用纯jQuery的现有应用程序上使用它。但是,如果您想充分利用AngularJS功能,您可以考虑使用RESTful方法对服务器端进行编码。
这样做将允许您利用它们的资源工厂,它创建了服务器端RESTfulapi的抽象,并使服务器端调用(获取、保存、删除等)变得非常容易。
在jQuery中,你设计一个页面,然后让它变得动态。这是因为jQuery是为增强而设计的,并且从这个简单的前提下发展得令人难以置信。
但是在AngularJS中,你必须从头开始考虑你的架构。而不是从思考“我有这块DOM,我想让它做X”开始,你必须从你想完成的事情开始,然后开始设计你的应用程序,最后开始设计你的视图。
同样,不要从jQuery做X、Y和Z的想法开始,所以我只会在模型和控制器的基础上添加AngularJS。当你刚刚开始时,这是真的的诱惑,这就是为什么我总是建议新的AngularJS开发人员根本不要使用jQuery,至少在他们习惯用“Angular Way”做事之前。
我在这里和邮件列表上看到许多开发人员使用150或200行代码的jQuery插件创建了这些精心设计的解决方案,然后他们使用一组回调和$apply粘合到AngularJS中,这些回调和$apply令人困惑和复杂;但他们最终让它工作!问题是,在大多数的情况下,jQuery插件可以在AngularJS中用一小部分代码重写,突然间一切都变得易于理解和直截了当。
$apply
底线是这样的:在解决问题时,首先“用AngularJS思考”;如果你想不出解决方案,问问社区;如果毕竟没有简单的解决方案,然后随时可以使用jQuery。但不要让jQuery成为拐杖,否则你永远不会掌握AngularJS。
首先要知道单页应用是4.4应用项目。它们是没有网页。所以我们需要像服务器端开发人员此外一样思考,像客户端开发人员一样思考。我们必须考虑如何将我们的应用程序划分为单独的、可扩展的、可测试的组件。
那么如何你会这样做吗?你如何“在AngularJS中思考”?这里有一些与jQuery对比的一般原则。
在jQuery中,我们以编程方式更改视图。我们可以将下拉框定义为ul,如下所示:
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的指令在上面操作;她不需要凭直觉得出正确的答案或筛选任何代码。视图告诉我们应该发生什么。干净多了。
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" />。
<input ng-model="entry.msg" />
在jQuery中,DOM有点像模型。但是在AngularJS中,我们有一个单独的模型层,我们可以以任何我们想要的方式管理,完全独立于视图。这有助于上述数据绑定,维护关注点分离,并引入更大的可测试性。其他答案提到了这一点,所以我就把它留在那里。
以上所有内容都与这个首要主题联系在一起:将你的关注点分开。你的视图充当了应该发生的事情的官方记录(在大多数情况下);你的模型代表了你的数据;你有一个服务层来执行可重用的任务;你进行DOM操作并使用指令增强你的视图;然后你用控制器将它们粘合在一起。这在其他答案中也提到了,我唯一想添加的是可测试性,我将在下面的另一节中讨论。
帮助我们分离关注点的是依赖注入(DI)。如果你来自服务器端语言(从Java到php),你可能已经熟悉这个概念了,但是如果你是来自jQuery的客户端人员,这个概念看起来从愚蠢到多余到时髦。但事实并非如此。:-)
从更广泛的角度来看,DI意味着你可以非常自由地声明组件,然后从任何其他组件,只需请求它的一个实例,它就会被授予。你不必知道加载顺序、文件位置或类似的东西。这种力量可能不会立即可见,但我将只提供一个(常见的)例子:测试。
假设在我们的应用程序中,我们需要一个服务通过REST API实现服务器端存储,根据应用程序状态,还需要一个本地存储。在我们的控制器上运行测试时,我们不希望必须与服务器通信——毕竟我们正在测试控制器。我们可以添加一个与原始组件同名的模拟服务,注入器将确保我们的控制器自动获得假控制器——我们的控制器不知道也不需要知道区别。
说到测试…
这实际上是关于架构的第3节的一部分,但它非常重要,我把它作为自己的顶级部分。
在你看到、使用或编写的所有jQuery插件中,有多少有附带的测试套件?不是很多,因为jQuery不太适合。但AngularJS是。
在jQuery中,测试的唯一方法通常是使用我们的测试可以执行DOM操作的示例/演示页面独立创建组件。所以我们必须单独开发一个组件,并然后将其集成到我们的应用程序中。多么不方便啊!
但是因为我们有关注点分离,我们可以在AngularJS中迭代地做测试驱动开发!例如,假设我们想要一个超简单的指令来在菜单中指示我们当前的路由是什么。我们可以在应用程序的视图中声明我们想要的:
<a href="/hello" when-active>Hello</a>
好的,现在我们可以为不存在的when-active指令编写一个测试:
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' );}});}};});
我们的测试现在通过了和我们的菜单按要求执行。我们的开发是两者迭代和测试驱动。酷极了。
您经常会听到“只在指令中进行DOM操作”。这是必要的。适当尊重它!
但是让我们再深入一点…
有些指令只是装饰视图中已经存在的内容(想想ngClass),因此有时会立即进行DOM操作,然后基本上就完成了。但是如果一个指令像一个“小部件”并且有一个模板,它应该还尊重关注点分离。也就是说,模板也应该在很大程度上独立于其在链接和控制器函数中的实现。
ngClass
AngularJS附带了一整套工具来使这变得非常容易;使用ngClass我们可以动态更新类;ngModel允许双向数据绑定;ngShow和ngHide以编程方式显示或隐藏元素;还有更多-包括我们自己编写的那些。换句话说,我们可以做各种令人敬畏的没有 DOM操作。DOM操作越少,指令就越容易测试,它们越容易样式化,它们在未来越容易更改,它们就越可重用和可分发。
ngModel
ngShow
ngHide
我看到很多AngularJS的新开发人员使用指令作为抛出一堆jQuery的地方。换句话说,他们认为“既然我不能在控制器中进行DOM操作,我就把这段代码放在指令中”。虽然这肯定要好得多,但通常是仍然错误。
想想我们在第3节中编写的记录器。即使我们把它放在指令中,我们仍然也想用“Angular Way”来做。它仍然不需要任何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);});}};});
有几件事是错误的:
angular.element
$
link
element
这个指令可以重写(即使是非常复杂的情况!)更简单地像这样:
.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类函数的集合,它们是什么?指令实际上是超文本标记语言的扩展。如果超文本标记语言不能做你需要它做的事情,你写一个指令为你做,然后就像它是超文本标记语言的一部分一样使用它。
换句话说,如果AngularJS没有做一些开箱即用的事情,想想团队将如何完成它以适应ngClick、ngClass等。
ngClick
甚至不要使用jQuery。甚至不要包含它。它会阻碍你。当你遇到一个你认为你已经知道如何用jQuery解决的问题时,在你达到$之前,试着在AngularJS的范围内思考如何解决它。如果你不知道,问!20次中有19次,最好的方法不需要jQuery,并尝试用jQuery结果为你解决它。
它们是苹果和橘子。你不想比较它们。它们是两回事。AngularJs已经内置了jQuery lite,它允许你执行基本的DOM操作,甚至不包括完整的jQuery版本。
jQuery是关于DOM操作的。它解决了所有跨浏览器的痛苦,否则您将不得不处理,但它不是一个允许您将应用程序划分为AngularJS等组件的框架。
AngularJs的一个好处是它允许你在指令中分离/隔离DOM操作。有内置指令供你使用,例如ng单击。你可以创建自己的自定义指令,其中包含你所有的视图逻辑或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”DOM元素并做一些事情。
AngularJS:模型就是真理,你总是从这个角度思考。
例如,当你从服务器获取你打算在DOM中以某种格式显示的数据时,在jQuery中,你需要“1. FIND”在DOM中你想要放置这些数据的位置,“2. UPDATE/APPEND”通过创建一个新节点或只是设置它的innerHTML来将其放置在那里。然后当你想更新这个视图时,你需要“3. FIND”位置和“4. UPDATE”。在AngularJS中,这种查找和更新的循环都在从服务器获取和格式化数据的相同上下文中完成。
使用AngularJS,你有你的模型(你已经习惯的JavaScript对象),模型的值告诉你关于模型(显然)和视图,对模型的操作会自动传播到视图,所以你不必考虑它。你会发现自己在AngularJS中不再在DOM中找到东西。
换句话说,在jQuery中,你需要考虑CSS选择器,也就是说,div或td在哪里有类或属性等,这样我就可以得到它们的超文本标记语言或颜色或值,但在AngularJS中,你会发现自己这样想:我在处理什么模型,我将模型的值设置为true。你不必担心反映这个值的视图是复选框还是驻留在td元素中(你在jQuery中经常需要考虑的细节)。
div
td
使用AngularJS中的DOM操作,您会发现自己添加了指令和过滤器,您可以将其视为有效的超文本标记语言扩展。
还有一件事你会在AngularJS中体验到:在jQuery中你会经常调用jQuery函数,在AngularJS中,AngularJS会调用你的函数,所以AngularJS会“告诉你如何做事”,但好处是值得的,所以学习AngularJS通常意味着学习AngularJS想要什么,或者AngularJS要求你呈现函数的方式,它会相应地调用它。这是使AngularJS成为框架而不是库的原因之一。
收听播客JavaScript Jabber: Episode#32,其中包括AngularJS的原始创建者:Misko Heach和Igor Minar。他们谈论了很多来自其他JavaScript背景的AngularJS的感受,尤其是jQuery。
播客中的一个观点让我对你的问题有很多看法:
MISKO:[…]在Angular中,我们几乎没有考虑过的一件事是,我们如何提供大量的逃生舱口,以便您可以出去并基本上找到解决方案。所以对我们来说,答案是这个叫做“指令”的东西。有了指令,你基本上就变成了一个普通的小jQuery JavaScript,你可以做任何你想做的事情。IGOR:所以把指令想象成编译器的指令,当你在模板中遇到这个特定的元素或这个CSS时,它会告诉它,你保留这种代码,代码负责DOM树中元素和该元素下面的所有内容。
MISKO:[…]在Angular中,我们几乎没有考虑过的一件事是,我们如何提供大量的逃生舱口,以便您可以出去并基本上找到解决方案。所以对我们来说,答案是这个叫做“指令”的东西。有了指令,你基本上就变成了一个普通的小jQuery JavaScript,你可以做任何你想做的事情。
IGOR:所以把指令想象成编译器的指令,当你在模板中遇到这个特定的元素或这个CSS时,它会告诉它,你保留这种代码,代码负责DOM树中元素和该元素下面的所有内容。
整个事件的记录可在上面提供的链接。
所以,直接回答你的问题:AngularJS非常固执己见,是一个真正的MV*框架。然而,你仍然可以在指令中用jQuery做所有你知道和喜欢的很酷的事情。这不是“我如何在jQuery中做我以前做的事情?”的问题,而是“我如何用我以前在jQuery中做的所有事情来补充AngularJS?”
这实际上是两种截然不同的心态。
jQuery使JavaScript命令变得非常长,例如getElementByHerpDerp更短和跨浏览器。
getElementByHerpDerp
AngularJS允许您制作自己的超文本标记语言标签/属性,这些标签/属性可以很好地与动态Web应用程序一起工作(因为超文本标记语言是为静态页面设计的)。
说“我有jQuery背景,我在AngularJS中如何思考?”就像说“我有超文本标记语言背景,我在JavaScript中如何思考?”你问这个问题的事实表明你很可能不理解这两个资源的基本目的。这就是为什么我选择通过简单地指出根本区别来回答这个问题,而不是通过列表说“AngularJS使用指令,而jQuery使用CSS选择器来制作一个jQuery对象,它做这个和那个等等……”。这个问题不需要冗长的回答。
jQuery是一种使在浏览器中编程JavaScript更容易的方法。更短、跨浏览器的命令等。
AngularJS扩展了超文本标记语言,所以你不必为了制作一个应用程序而到处放<div>。它使超文本标记语言实际上适用于应用程序,而不是它的设计目的,即静态的、教育性的网页。它使用JavaScript以迂回的方式实现了这一点,但从根本上说,它是超文本标记语言的扩展,而不是JavaScript。
<div>
为了描述“范式转变”,我认为一个简短的答案就足够了。
在jQuery中,通常使用选择器查找元素,然后将它们连接起来:$('#id .class').click(doStuff);
$('#id .class').click(doStuff);
在AngularJS中,您使用指令直接标记元素,将它们连接起来:<a ng-click="doStuff()">
<a ng-click="doStuff()">
AngularJS不需要(或不希望)你使用选择器来查找元素——AngularJS的jqLite与成熟的jQuery之间的主要区别在于jqLite不支持选择器。
所以当人们说“根本不包含jQuery”时,主要是因为他们不希望你使用选择器;他们希望你学会使用指令。直接,而不是选择!
AngularJS和jQuery采用非常不同的意识形态。如果你来自jQuery,你可能会发现一些差异令人惊讶。Angular可能会让你生气。
这很正常,你应该坚持下去。Angular是值得的。
jQuery为您提供了一个工具包,用于选择DOM的任意位并对其进行临时更改。您几乎可以逐个执行任何您喜欢的操作。
AngularJS反而给你一个编译器。
这意味着AngularJS从上到下读取整个DOM并将其视为代码,字面意思是编译器的指令。当它遍历DOM时,它会寻找特定的指令(编译器指令)来告诉AngularJS编译器如何行为和做什么。指令是充满JavaScript的小对象,可以与属性、标签、类甚至注释匹配。
当Angular编译器确定DOM的一部分与特定指令匹配时,它会调用指令函数,传递DOM元素、任何属性、当前$范围(它是一个局部变量存储)和一些其他有用的位。这些属性可能包含可以被指令解释的表达式,这些表达式告诉它如何渲染,以及何时应该重绘自己。
然后指令可以反过来引入额外的Angular组件,例如控制器、服务等。编译器底部出来的是一个完全形成的Web应用程序,连接起来并准备就绪。
这意味着Angular是模板驱动的。您的模板驱动JavaScript,而不是相反。这是角色的彻底颠倒,与我们过去10年左右一直在编写的不引人注目的JavaScript完全相反。这可能需要一些时间来适应。
如果这听起来像是过度规范和限制,那么没有什么比这更偏离事实了。因为AngularJS将你的超文本标记语言视为代码,所以你得到了Web应用程序中的超文本标记语言级颗粒度。一切皆有可能,一旦你在概念上取得了一些飞跃,大多数事情都出奇地容易。
让我们回到本质上来。
Angular和jQuery做不同的事情。AngularJS为您提供了一组生成Web应用程序的工具。jQuery主要为您提供修改DOM的工具。如果您的页面上存在jQuery,AngularJS将自动使用它。如果不是,AngularJS附带jQuery Lite,这是jQuery的精简版,但仍然非常可用。
Misko喜欢jQuery并且不反对你使用它。然而,随着你的进步,你会发现你可以使用范围、模板和指令的组合来完成几乎所有的工作,你应该尽可能喜欢这种工作流,因为你的代码将更离散、更可配置、更有角度。
如果你确实使用jQuery,你不应该把它洒得到处都是。AngularJS中DOM操作的正确位置是在指令中。稍后会详细介绍这些。
jQuery通常不显眼地应用。您的JavaScript代码链接在页眉(或页脚)中,这是唯一提到它的地方。我们使用选择器从页面中挑选出一些并编写插件来修改这些部分。
JavaScript在控制。超文本标记语言有一个完全独立的存在。即使没有JavaScript,你的超文本标记语言仍然是语义的。OnClick属性是非常糟糕的做法。
你会注意到AngularJS的第一件事是自定义属性无处不在。你的超文本标记语言将充斥着ng属性,这些属性本质上是类固醇上的onClick属性。这些是指令(编译器指令),是模板与模型挂钩的主要方式之一。
当你第一次看到这个时,你可能会想把AngularJS写成老派的侵入性JavaScript(就像我一开始所做的那样)。事实上,AngularJS并不遵守这些规则。在AngularJS中,您的HTML5是一个模板。它由AngularJS编译以生成您的网页。
这是第一个大的区别。对jQuery来说,你的网页是一个要操作的DOM。对AngularJS来说,你的超文本标记语言是要编译的代码。AngularJS读取你的整个网页,并使用其内置的编译器将其编译成一个新的网页。
你的模板应该是声明性的;它的含义应该通过阅读它就清楚了。我们使用带有有意义名称的自定义属性。我们组成了新的超文本标记语言元素,同样使用有意义的名称。一个只有最少超文本标记语言知识和没有编码技能的设计师可以阅读你的AngularJS模板并理解它在做什么。他或她可以进行修改。这就是Angular的方式。
在启动AngularJS并运行教程时,我问自己的第一个问题是“我的代码在哪里?”。我没有编写过JavaScript,但我有所有这些行为。答案是显而易见的。因为AngularJS编译DOM,AngularJS将您的超文本标记语言视为代码。对于许多简单的情况,只需编写一个模板并让AngularJS为您编译成应用程序就足够了。
您的模板驱动您的应用程序。它被视为DSL。您编写AngularJS组件,AngularJS将负责提取它们并根据模板的结构在正确的时间使它们可用。这与标准MVC模式非常不同,其中模板仅用于输出。
例如,它更类似于XSLT而不是ruby on rails。
这是一个激进的控制反转,需要一些时间来适应。
停止尝试从JavaScript驱动您的应用程序。让模板驱动应用程序,让AngularJS负责将组件连接在一起。这也是Angular的方式。
使用jQuery,您的超文本标记语言页面应该包含语义有意义的内容。如果JavaScript被关闭(由用户或搜索引擎),您的内容仍然可以访问。
因为AngularJS将您的超文本标记语言页面视为模板。模板不应该是语义的,因为您的内容通常存储在最终来自您的API的模型中。AngularJS使用模型编译您的DOM以生成语义网页。
您的超文本标记语言源不再是语义的,相反,您的API和编译的DOM是语义的。
在AngularJS中,意味着存在于模型中,超文本标记语言只是一个模板,仅用于显示。
在这一点上,你可能有各种各样关于seo和可访问性的问题,这是正确的。这里有一些悬而未决的问题。大多数屏幕阅读器现在将解析JavaScript。搜索引擎也可以索引AJAXed内容。尽管如此,你还是要确保你使用的是pushstate URL,并且你有一个不错的站点地图。请参阅此处了解有关问题的讨论:https://stackoverflow.com/a/23245379/687677
关注点分离(SOC)是一种模式,它在多年的Web开发中成长起来,原因有很多,包括SEO、可访问性和浏览器不兼容。它看起来像这样:
同样,AngularJS不遵守他们的规则。在一个笔画中,AngularJS废除了十年来公认的智慧而是实现了一个MVC模式,其中模板不再是语义的,甚至一点也不是。
它看起来像这样:
MVC和SOC不在同一个尺度的两端,它们在完全不同的轴上。SOC在AngularJS上下文中没有意义。你必须忘记它并继续前进。
如果你像我一样经历过浏览器大战,你可能会发现这个想法非常令人反感。克服它,这是值得的,我保证。
插件扩展了jQuery。AngularJS指令扩展了浏览器的功能。
在jQuery中,我们通过向jQuery.prototype.添加函数来定义插件,然后通过选择元素并在结果上调用插件将它们挂钩到DOM中。这个想法是扩展jQuery的功能。
例如,如果您想在页面上使用轮播,您可以定义一个无序的图形列表,可能包含在nav元素中。然后您可以编写一些jQuery来选择页面上的列表,并将其重新设计为带有超时的库以执行滑动动画。
在AngularJS中,我们定义了指令。指令是一个返回JSON对象的函数。该对象告诉AngularJS要查找哪些DOM元素,以及要对它们进行哪些更改。指令使用您发明的属性或元素与模板挂钩。这个想法是用新的属性和元素扩展超文本标记语言的功能。
AngularJS的方式是扩展本机超文本标记语言的功能。你应该编写超文本标记语言,看起来像超文本标记语言,扩展自定义属性和元素。
如果您想要一个轮播,只需使用<carousel />元素,然后定义一个指令来拉取模板,并使该吸盘工作。
<carousel />
jQuery的趋势是编写像Lightbox这样的大型插件,然后通过传递大量值和选项来配置。
这是AngularJS中的一个错误。
以下拉列表为例,在编写下拉列表插件时,您可能会想在单击处理程序中编写代码,也许是在向上或向下的人字形中添加一个函数,也许是更改展开元素的类,显示隐藏菜单,所有有用的东西。
直到你想做一个小小的改变。
假设您有一个要在悬停上展开的菜单。现在我们有一个问题。我们的插件已经为我们连接了单击处理程序,我们需要添加一个配置选项,使其在这个特定情况下的行为不同。
在AngularJS中,我们编写了更小的指令。我们的下拉指令小得离谱。它可能会保持折叠状态,并提供折叠()、展开()或切换()的方法。这些方法只会更新$scope.menu.visible,这是一个保存状态的布尔值。
现在在我们的模板我们可以连接起来:
<a ng-click="toggle()">Menu</a><ul ng-show="menu.visible">...</ul>
需要更新mouseover吗?
<a ng-mouseenter="unfold()" ng-mouseleave="fold()">Menu</a><ul ng-show="menu.visible">...</ul>
模板驱动应用程序,因此我们获得超文本标记语言级别的颗粒度。
JQuery插件是在闭包中创建的。隐私在该闭包中得到维护。在该闭包中维护你的作用域链取决于你。你只能真正访问jQuery传递给插件的DOM节点集,加上闭包中定义的任何局部变量和你定义的任何全局变量。这意味着插件是相当自包含的。这是一件好事,但在创建整个应用程序时可能会受到限制。试图在动态页面的各个部分之间传递数据变得很麻烦。
AngularJS有$ope对象。这些是由AngularJS创建和维护的特殊对象,您可以在其中存储模型。某些指令将生成一个新的$ope,默认情况下,它使用JavaScript原型继承从其包装的$ope继承。$ope对象可以在控制器和视图中访问。
这是聪明的部分。因为$scope继承的结构大致遵循DOM的结构,元素可以无缝访问它们自己的范围和任何包含范围的范围,一直到全局$范围(这与全局范围不同)。
这使得传递数据和在适当的级别存储数据变得更加容易。如果展开下拉列表,只有下拉列表$范围需要知道它。如果用户更新他们的首选项,您可能希望更新全局$范围,并且任何监听用户首选项的嵌套范围都会自动收到警报。
这听起来可能很复杂,事实上,一旦你放松下来,它就像飞行一样。你不需要创建$scope对象,AngularJS会根据你的模板层次结构正确适当地实例化和配置它。然后,AngularJS使用依赖注入的魔力使它可用于你的组件(稍后会详细介绍)。
在jQuery中,您可以手动进行所有DOM更改。您可以通过编程方式构建新的DOM元素。如果您有一个JSON数组并且要将其放入DOM,则必须编写一个函数来生成超文本标记语言并插入它。
在AngularJS中,你也可以这样做,但鼓励你使用数据绑定。更改你的模型,因为DOM通过模板绑定到它,你的DOM将自动更新,不需要干预。
因为数据绑定是从模板完成的,使用属性或花括号语法,所以非常容易做到。与之相关的认知开销很小,所以您会发现自己一直在做这件事。
<input ng-model="user.name" />
将输入元素绑定到$scope.user.name。更新输入将更新当前范围中的值,反之亦然。
$scope.user.name
同样:
<p>\{\{user.name}}</p>
将在段落中输出用户名。这是一个实时绑定,因此如果$scope.user.name值被更新,模板也会更新。
在jQuery中,进行Ajax调用相当简单,但它仍然是您可能会三思而后行的事情。有额外的复杂性需要考虑,还有相当多的脚本需要维护。
在AngularJS中,Ajax是您默认的首选解决方案,它一直在发生,几乎没有您注意到。您可以使用ng-包括包含模板。您可以使用最简单的自定义指令应用模板。您可以将Ajax调用包装在服务中并为自己创建github服务或flickr服务,您可以非常轻松地访问它们。
在jQuery中,如果我们想完成一个与dom无关的小任务,例如从API提取提要,我们可能会在闭包中编写一个小函数来做到这一点。这是一个有效的解决方案,但是如果我们想经常访问该提要怎么办?如果我们想在另一个应用程序中重用该代码怎么办?
AngularJS为我们提供了服务对象。
服务是包含函数和数据的简单对象。它们始终是单例,这意味着它们中永远不会有一个以上。假设我们想要访问Stack Overflow API,我们可能会编写一个StackOverflowService来定义这样做的方法。
StackOverflowService
假设我们有一个购物车。我们可以定义一个ShoppingCartService来维护我们的购物车并包含添加和删除商品的方法。因为该服务是单例的,并且由所有其他组件共享,所以任何需要写入购物车并从中提取数据的对象。它总是相同的购物车。
服务对象是自包含的AngularJS组件,我们可以在合适的时候使用和重用。它们是包含函数和数据的简单JSON对象。它们总是单例的,所以如果你在一个地方存储服务上的数据,你可以通过请求相同的服务从其他地方获取数据。
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对象来实例化控制器,并将为我们提供一个。
这使得将东西连接在一起变得非常容易,并且几乎消除了任何拼凑的倾向。我们有一个扁平的组件列表,AngularJS会在我们需要时逐个将它们交给我们。
jQuery很少谈到如何组织代码。AngularJS有意见。
AngularJS提供了可以放置代码的模块。例如,如果您正在编写与Flickr对话的脚本,您可能希望创建一个Flickr模块来包装所有与Flickr相关的功能。模块可以包括其他模块(DI)。您的主应用程序通常是一个模块,这应该包括您的应用程序将依赖的所有其他模块。
您可以获得简单的代码重用,如果您想编写另一个基于Flickr的应用程序,您只需包含Flickr模块,瞧,您可以在新应用程序中访问所有与Flickr相关的功能。
模块包含AngularJS组件。当我们包含一个模块时,该模块中的所有组件都可以作为一个由其唯一字符串标识的简单列表使用。然后我们可以使用AngularJS的依赖注入机制将这些组件相互注入。
AngularJS和jQuery不是敌人。在AngularJS中非常好地使用jQuery是可能的。如果你很好地使用AngularJS(模板、数据绑定、$范围、指令等),你会发现你需要的jQuery比你可能需要的少了很多。
要意识到的主要事情是您的模板驱动您的应用程序。不要试图编写做所有事情的大插件。相反,编写只做一件事的小指令,然后编写一个简单的模板将它们连接在一起。
少考虑不显眼的JavaScript,而是考虑超文本标记语言扩展。
我对AngularJS感到非常兴奋,我写了一本关于它的小书,非常欢迎您在线阅读http://nicholasjohnson.com/angular-book/。我希望它有帮助。
这些是一些非常好但冗长的答案。
总结一下我的经验:
作为一个JavaScript MV*初学者,并且纯粹专注于应用程序架构(而不是服务器/客户端问题),我肯定会推荐以下资源(我很惊讶还没有提到):JavaScript设计模式,Addy Osmani,作为对不同JavaScript设计模式的介绍。此答案中使用的术语取自上面链接的文档。我不打算重复已接受答案中措辞很好的内容。相反,此答案链接回驱动AngularJS(和其他库)的理论背景。
像我一样,你很快就会意识到AngularJS(或Ember.js、Durandal和其他MV*框架)是一个复杂的框架,组装了许多不同的JavaScript设计模式。
我发现在深入研究一个全局框架之前,测试(1)个原生JavaScript代码和(2)个较小的库也更容易分别。这使我能够更好地理解框架解决了哪些关键问题(因为你亲自面对这个问题)。
例如:
NB:这个列表并不完整,也不是“最好的库”;它们只是我使用的库。这些库还包含更多模式,提到的只是他们的主要焦点或原始意图。如果你觉得这个列表缺少什么,请在评论中提到,我很乐意添加它。
实际上,如果你使用AngularJS,你就不再需要jQuery了。AngularJS本身具有绑定和指令,这是一个非常好的“替代品”,可以用jQuery做大多数事情。
我通常使用AngularJS和科尔多瓦开发移动应用程序。我需要的jQuery中唯一的东西就是选择器。
通过谷歌搜索,我看到有一个独立的jQuery选择器模块。它是Sizzle。
我决定制作一个小代码片段,帮助我使用AngularJS快速启动一个网站,并使用jQuery Selector的强大功能(使用Sizzle)。
我在这里分享了我的代码:https://github.com/huytd/Sizzular
AngularJS和jQuery:
AngularJs和JQuery在除了JQLite功能之外的每个级别都完全不同,一旦你开始学习AngularJs的核心功能,你就会看到它(我在下面解释过)。
AngularJs是一个客户端框架,提供构建独立的客户端应用程序。JQuery是一个围绕DOM的客户端库。
AngularJs很酷的原则-如果你想对你的UI进行一些更改,请从模型数据更改的角度考虑。更改你的数据,UI将重新呈现自己。除非几乎不需要,否则你不需要每次都玩DOM,这也应该通过Angular指令来处理。
为了回答这个问题,我想分享我在使用AngularJS的第一个企业应用程序上的经验。这些是Angular提供的最棒的功能,我们开始改变我们的jQuery心态,我们把Angular当成一个框架,而不是库。
双向数据绑定是惊人的:我有一个具有所有功能UPDATE、DELTE、INSERT的网格。我有一个数据对象,它使用ng-重复绑定网格的模型。你只需要编写一行简单的JavaScript代码来删除和插入,就是这样。随着网格模型的即时变化,网格会自动更新。更新功能是实时的,没有代码。感觉太棒了!!!
可重用指令非常棒:在一个地方编写指令并在整个应用程序中使用它。天哪!!!我将这些指令用于分页、正则表达式、验证等。真的很酷!
路由强:如何使用它取决于您的实现,但它需要很少的代码行来路由请求以指定超文本标记语言和控制器(JavaScript)
控制器很棒:控制器照顾自己的超文本标记语言,但这种分离也适用于通用功能。如果您想在单击主超文本标记语言上的按钮时调用相同的函数,只需在每个控制器中编写相同的函数名称并编写单独的代码。
插件:还有许多其他类似的功能,例如在您的应用程序中显示覆盖。你不需要为此编写代码,只需使用作为wc-overlay可用的覆盖插件,这将自动处理所有XMLHttpRequest相关文档(XHR)请求。
RESTful架构的理想选择:作为一个完整的框架,AngularJS非常适合使用RESTful架构。调用REST CRUD API非常容易,
服务:使用服务编写通用代码,在控制器中编写更少的代码。服务可用于在控制器之间共享通用功能。
扩展性:Angular使用角指令扩展了超文本标记语言指令。在html中编写表达式并在运行时评估它们。创建您自己的指令和服务并在其他项目中使用它们,无需任何额外的努力。