有jQuery背景的我,该如何用AngularJS编程思想?

导读:本文由机器human 编译自 Mark Rajcok 在 StackOverflow 的同名问答题《How do I “think in Angular.js” if I have a jQuery background?》。下面是 Mark Rajcok 的提问:php

“我能够熟练使用jQuery进行客户端应用的开发,可是如今我但愿开始使用Angular.js。哪位能描述一下这个过程当中必要的模式变化吗?但愿您的答案可以围绕下面这些具体的问题:html

1. 我如何对客户端web应用进行不一样方式的架构和设计?它们之间最大的区别是什么?(译者注:指jQuery和Angular.js)java

2. 有什么是我不应作或者不应使用的;而又有什么是我应该作或者应该使用的呢?web

3. 有没有一些服务端的考量/约束呢?ajax

我在寻找的就是一个关于jQuery和Angular.js之间的详细的比较。”编程

下面是来自 Josh David Miller 的最佳回答:json

1. 毫不要先设计你的页面,而后用DOM操做去改变它服务器

在jQuery中,你会先设计一个页面,而后让它变得动态化。这是由于jQuery是为了扩展而设计的,并在这个前提下变得愈来愈臃肿。架构

可是在Angular.js中, 你必须从一开始就在脑子里挂着架构的弦。不要一开始就想着“我有这样一个DOM,我想让它作X”, 你必须从你要完成的目标开始思考,而后设计你的应用, 最后才是设计你的视图。app

2. 不要用 Angular.js 扩展 jQuery

相似地,不要一开始就带着这样的想法:jQuery能够完成X,Y,Z,因此我只要在其上为模型和控制器添加Angular.js就好了。在起步阶段这确实很容易勾引你,这也是为何我老是推荐Angular.js新手根本不要使用jQuery,至少要在他们习惯了“angular 方式”以后。

我在这里(译者注:指stackoverflow)和邮件列表上看到过不少开发者,他们用150或者200行代码的jQuery插件,而后利用一堆让人困惑的复杂的回调和$apply与Angular.js粘合起来创建这些详尽的解决方案;最终确实能够跑起来! 可是其实这个问题在大多数状况下,咱们能够用一小段Angular.js代码来重写jQuery插件便可,而这种方式会让一切刹那间简单明了可理解。

我以为这类问题的底线是:当你在解决问题时,首先利用“Angular.js思想”去作;若是你不能想出一个方案,那么就在社区里询问;若是仍是没有简单的解决方法,那么再请随意使用jQuery吧。可是注意,千万别让jqeury成为你的拐杖,否则你将永远没法真正精通Angular.js。

3. 永远根据架构去思考

首先你要知道,单页面结构也是应用。它不是网页。因此咱们须要有服务端开发者思想加上客户端开发者思想。 咱们必须考虑如何将咱们的应用拆分为独立,可扩展,可测试的组件。

那么你要怎么作呢?你如何作到利用“angualrjs思想”呢?这里有一些广泛的原则,与jQuery做为比照。

视图是“正式记录”

在jQuery中,咱们经过编程方式来改变视图。咱们能够像下面这样经过ul标签来定义一个下拉菜单:

  1. <ul class="main-menu"
  2.     <li class="active"
  3.         <href="#/home">Home</a
  4.     </li
  5.     <li
  6.         <href="#/menu1">Menu 1</a
  7.         <ul
  8.             <li><href="#/sm1">Submenu 1</a></li
  9.             <li><href="#/sm2">Submenu 2</a></li
  10.             <li><href="#/sm3">Submenu 3</a></li
  11.         </ul
  12.     </li
  13.     <li
  14.         <href="#/home">Menu 2</a
  15.     </li
  16. </ul

在jQuery中,根据咱们应用的逻辑,能够用相似下面的语句来激活它。

  1. $('.main-menu').dropdownMenu(); 

当咱们只是看着视图的时候,不会马上看出它的功能。对于小应用而言,这样是没问题的。可是对于大型的应用,状况就一会儿变得使人困惑而且难以维护。

可是在Angular.js中,视图是基于视图的功能的正式记录。咱们的ul是像下面这样声明的:

  1. <ul class="main-menu" dropdown-menu
  2.     ...  
  3. </ul

这二者其实作了一样的事情,可是在Angular.js的版本中,任何看到这个模板的人都知道将要发生什么。不论什么时候,开发团队里有任何新的开发人员加入,她能够一眼看出有一个叫作dropdownMenu的指令做用在视图上;她根本不须要凭直觉猜想或者研究下代码才找到正确的答案。视图自己就告诉咱们将会发生什么了。清晰多了。

angualrjs的新手常常会问这样一个问题: 我如何找到某一类全部的连接而且给它们添加一个指令呢?当看到咱们回复的时候小伙伴都震惊了:压根别去这样作。可是劝你不要这样作的缘由是,这样作就像是一半jQuery,一半angulrjs,而这真心很糟。这里的问题是,开发者想在angualrjs的情境中使用jQuery方式。而这绝对不会玩得转。视图是正式记录。超出指令的范围(这点下文会谈论更多),你毫不要去改变DOM。并且指令是应用在视图中的,目的天然也一目了然。

记住:不要先设计再修饰。你必须先进行架构,而后再考虑设计。

数据绑定

这是Angular.js目前最酷的特性之一,而且秒杀我前文提到的各类须要的DOM操做。不须要你本身动手,Angular.js将自动更新你的视图有木有!

在jQuery里, 咱们响应事件并更新内容,大概是这个样子:

  1. $.ajax({  
  2.   url: '/myEndpoint.json',  
  3.   success: function ( data, status ) {  
  4.     $('ul#log').append('<li>Data Received!</li>');  
  5.   }  
  6. }); 

视图则看上去是这样的:

  1. <ul class="messages" id="log"
  2. </ul

除了关注点混合的问题,这里一样有以前提到的表征目的的问题。更重要的是,咱们不得不手动引用并更新dom节点。而且若是咱们想要删除一个日志,咱们不得再也不次对dom编程操做。咱们怎样才能抛开dom来测试逻辑呢?还有,若是咱们但愿改变展示呢?

 

真是让人凌乱。。。

可是在Angular.js中,咱们能够这样作:

  1. $http( '/myEndpoint.json' ).then( function ( response ) {  
  2.     $scope.log.push( { msg: 'Data Received!' } );  
  3. }); 

咱们的视图看上去是这样的:

  1. <ul class="messages"
  2.     <li ng-repeat="entry in log">{{ entry.msg }}</li
  3. </ul

可是考虑到刚才提到的问题,咱们的视图看上去能够是这样的:

  1. <div class="messages"
  2.     <div class="alert" ng-repeat="entry in log"
  3.         {{ entry.msg }}  
  4.     </div
  5. </div

如今,替换掉了无序列表,咱们使用Bootstrap警告框。同时咱们根本不须要改变控制器代码!更重要的是,不论日志什么时候或者如何更新,视图也会跟着改变。自动的!漂亮!

虽然我没有在这里演示出来,可是数据绑定是双向的。因此这些日志信息一样能够在视图中被编辑,就像这样:

  1. <input ng-model="entry.msg" /> 

是否是更开心了?

不一样的模型层

在jQuery中,dom有点像模型。可是在angualrjs中,咱们有一个分离的模型层, 而这个模型层可让咱们用任何方式管理,彻底独立于视图。这对于上面说的数据绑定颇有帮助, 还能够维护关注点分离,而且引入更多的可测试性。其它的答案提到了这点,因此我这里就再也不赘述了。

关注点分离

以上全部的这些把咱们带入了这样的主题:保持你的关注点分离。你的视图表现的像记录什么会发生(大部分状况)的正式记录;你的模型表现你的数据;你有一个服务层来执行可重用的任务;你执行dom操做并经过指令扩展你的视图;而且你用控制器来组合这些。这些一样已经在其它答案中提到,我在这里惟一还要提出的一个事情就是可测试性,我会在下文的另外一节里专门讨论。

依赖注入

依赖注入是让咱们实现关注点分离的方法。若是你是一个服务器端的开发者(从java到php),你可能对这个概念已经很是熟悉了,可是若是你是一个来自jQuery的客户端的朋友,那么你可能会认为这个概念是傻浅挫。可是它可不是:)

从一个更广的观点来看, 依赖注入意味着你能够很是自由的声明组件,而后你能够经过任意其它组件,呼叫一个它的实例,而后受权。

你不须要知道载入顺序,或者文件位置,或者其它相似的东西。这种强大的力量可能不会马上显现,可是我这里会提供一个(一般)的例子:测试。

好比在咱们的应用中,须要一个经过REST API,同时也依赖于应用状态,本地存储实现了服务器端存储的服务。当在咱们的控制器上跑测试的时候,咱们不但愿与服务器端通信-毕竟咱们在测试控制器。咱们可以仅仅添加一个与咱们原始组件同名的mock服务,注入器将确保咱们的控制器自动获取伪造对象–咱们的控制器不会也不须要知道它们的区别。

那么既然提到测试……

4. 保持测试驱动的开发

这个实际上是关于架构的第三节的一部分,可是这个主题很是重要,因此我须要将其提出来做为自成体系的部分。

那些你看过,用过,写过的全部jQuery插件,它们中有多少有相应的测试包?不是不少吧? 由于jQuery可不是很遵照这个规矩。可是angualrjs是。

在jQuery中, 测试的惟一方法是用示例页面来独立地建立组件,针对该页面咱们的测试能够实施dom操做。因而咱们就不得不分离地开发一个组件而后将其集成进咱们的应用。多么不方便啊!

那么多时间啊,当咱们使用jQuery开发时,咱们选择使用迭代开发代替测试驱动的开发。能够谁又能怪咱们呢?

可是由于咱们有关注点分离,咱们可以在Angular.js里反复使用测试驱动开发。举个例子,咱们想要一个超简单的指令来指示在菜单中咱们目前的路径是什么。咱们能够在视图中这样声明咱们想获取的:

  1. <href="/hello" when-active>Hello</a

好了,咱们如今能够写个测试:

  1. it( 'should add "active" when the route changes', inject(function() {  
  2.     var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );  
  3.    
  4.     $location.path('/not-matching');  
  5.     expect( elm.hasClass('active') ).toBeFalsey();  
  6.    
  7.     $location.path( '/hello' );  
  8.     expect( elm.hasClass('active') ).toBeTruthy();  
  9. })); 

咱们运行测试,并确认它是失败的。那么咱们来写下咱们的指令:

  1. .directive( 'whenActive', function ( $location ) {  
  2.     return {  
  3.         scope: true,  
  4.         link: function ( scope, element, attrs ) {  
  5.             scope.$on( '$routeChangeSuccess', function () {  
  6.                 if ( $location.path() == element.attr( 'href' ) ) {  
  7.                     element.addClass( 'active' );  
  8.                 }  
  9.                 else {  
  10.                     element.removeClass( 'active' );  
  11.                 }  
  12.             });  
  13.         }  
  14.     };  
  15. }); 

如今咱们的测试经过了,而且咱们的菜单按照请求运行。咱们的开发是迭代而且测试驱动的,太酷了哦!

 

5. 从概念上来看,指令不是打包的jQuery

你会常常听到“只在指令里作dom操做”。这是必要的。请对它表示出尊重!

可是让咱们谈点更深刻的。。。

一些指令只是装饰那些在视图里的已有的(想一想ngClass),所以有时候就直接进行dom操做,基本上都能搞定。可是若是一个指令像个“widget”并有一个模板,

那它一样要遵照关注点分离原则。也就是说,这个模板也应该与它在连接和控制器函数里的实现保持最大的独立。

Angular.js自带着一套工具让这件事变得简单; 使用ngClass咱们可以动态的更新类;ngBind容许双向的数据绑定;ngShow和ngHide以编程的方式显示或隐藏一个元素;还有更多–包括咱们本身写的那些。换句话说, 咱们能够不用DOM操做来实现全部的酷炫的事儿。 dom操做越少,指令越容易测试,它们也更容易样式化,在将来它们也更容易改变,而且也变得更加可重用和可分发。

我看到不少Angular.js开发新手将指令当作放置一堆jQuery的地方。换句话说, 他们认为:“既然我不能在控制器里作dom操纵,那么我就把这段代码放到指令里”。固然这看上去好多了,可是一般这仍然是错的。

想一下在第三节里咱们编写的日志记录。即便咱们将其放到一个指令里,咱们仍然但愿用“anggualjs方式”来作这件事情。这仍然没有作任何 dom操做!在不少状况下dom操做是必须的,可是这种状况其实比你想的少得多!在你在你的应用中处处使用dmo操做以前,问问你本身,这真的是必须的吗。可能有更好的方法呢。

这里用一个简单的例子来展现咱们常常会看到的一个模式。咱们须要一个切换按钮。(注意:这个例子有那么一点人为设计的技巧而且有点啰嗦,可是不少更复杂的状况其实也彻底能够根据这个例子的方式来解决。)

  1. .directive( 'myDirective', function () {  
  2.     return {  
  3.         template: '<class="btn">Toggle me!</a>',  
  4.         link: function ( scope, element, attrs ) {  
  5.             var on = false;  
  6.    
  7.             $(element).click( function () {  
  8.                 if ( on ) {  
  9.                     $(element).removeClass( 'active' );  
  10.                 }  
  11.                 else {  
  12.                     $(element).addClass( 'active' );  
  13.                 }  
  14.    
  15.                 on = !on;  
  16.             });  
  17.         }  
  18.     };  
  19. }); 

这里有一些错误。第一,jQuery不是必须的。咱们这里作的一切都不须要jQuery!第二, 即便是咱们的页面上已经有jQuery,也没有理由必定要在这里使用它;咱们能够简单的使用angular.element,并且就算在一个没有 jQuery的项目中咱们的组件仍然能够工做。第三,即便咱们假设为了让这个指令工做,jQuery是必须的,若是jqury被加载,那么 jqLite(angler.element)必定会使用jQuery。因此咱们不须要使用$(译者注:jQuery的一个标识符号,是jQuery函数的别名)–咱们可使用angular.element。第四, 紧接第三点,jqLite元素不须要被包裹在$里–传递给link函数的element已是一个jQuery元素!第五, 我上一节已经提过的,为何咱们要将模板混合进咱们的逻辑呢?

这个指令能够更精简的重写(即时在更复杂的例子里也同样):

  1. .directive( 'myDirective', function () {  
  2.     return {  
  3.         scope: true,  
  4.         template: '<class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',  
  5.         link: function ( scope, element, attrs ) {  
  6.             scope.on = false;  
  7.    
  8.             scope.toggle = function () {  
  9.                 scope.on = !$scope.on;  
  10.             };  
  11.         }  
  12.     };  
  13. }); 

再次强调,模板的那些代码都是在模板里的,因此你或者你的使用者可以很方便的将其换成一个符合任何须要的样式,同时逻辑不被改变。这就是重用性-赞!

固然,还有其它不少好处–好比测试 – 这很容易! 不论什么在模板中,指令的内部API毫不会被接触,因此重构就变得容易。你能够在不接触指令的状况下随意改变你的模板。而且不论你怎么变,你的测试仍能够经过。

耶!

因此若是指令不仅是一组jQuery风格的函数,那么它们是什么呢?指令其实就是html的扩展。若是html不能完成你但愿它作的一些事情,你就写一个指令来作,而且当它是html的一部分来使用。

换句话说, 若是Angular.js一会儿没法完成手头的工做,想一想看你的团队是否能用ngClick、ngClass等来搞定它。

总结

别用jQuery。甚至都别引用它。它会阻碍你。当你有个问题已经知道怎么用jQuery来解决的时候,在你将手伸向$的时候,试试能不能在Angular.js的规范内解决。若是你不知道,就问!

十有八九最佳的方法是不须要jQuery的,而当你选择用jQuery的时候反而会致使更多的工做呢。

相关文章
相关标签/搜索