本文来自StackOverFlow上How do I “think in AngularJS” if I have a jQuery background?一题中得票最高的回答。该回答得票超过3000次,回答者Josh David Miller是活跃于开源社区的开发者,也是Emergenesis公司的联合创始人。该答案最初由数云架构师韩铮翻译并发布在自己的博客上,在征得Josh同意后由韩铮本人推荐给 InfoQ进行分享,并在经过InfoQ社区编辑崔康审校后发布在此。 1. 不要先设计页面,然后再使用DOM操作来改变它的展现 在jQuery中,你通常会设计一个页面,然后再给它动态效果。这是因为jQuery的设计就是为了扩充DOM并在这个简单的前提下疯狂的生长的。 但是在AngularJS里,必须从头开始就在头脑中思考架构。必须从你想要完成的功能开始,然后设计应用程序,最后来设计视图,而非“我有这么一个DOM片段,我想让他可以实现XXX效果”。 2. 不要用AngularJS来加强jQuery 类似的,不要以这样的思维开始:用jQuery来做X,Y和Z,然后只需要把AngularJS的models和controllers加在这上面。这在刚开始的时候显得非常诱人,这也是为什么我总是建议AngularJS的新手完全不使用jQuery,至少不要在习惯使用“Angular Way”开发之前这么做。 我在邮件列表里看到很多开发者使用150或200行代码的jQuery插件创造出这些复杂的解决方案,然后使用一堆callback函数以及$apply把它粘合到AngularJS里,看起来复杂难懂;但是他们最终还是把它搞定了!问题是在大多数情况下这些jQuery插件可以使用很少的AngularJS代码重写,而且所有的一切都很简单直接容易理解。 这里的底线是:当你选择解决方案时,首先“think in AngularJS”;如果想不出一个解决方案,去社区求助;如果还是没有简单的解决方案,再考虑使用jQuery。但是不要让jQuery成为你的拐杖,导致你永远无法真正掌握AngularJS。 3. 总是以架构的角度思考 首先要知道Single-page应用是应用,不是网页。所以我们除了像一个客户端开发者般思考外,还需要像一个服务器端开发者一样思考。我们必须考虑如何把我们的应用分割成独立的,可扩展且可测试的组件。 那么如何做到呢?如何“think in AngularJS”?这里有一些基本原则,对比jQuery。 视图是“Official Record” 在jQuery里,我们编程改变视图。我们会将一个下拉菜单定义为一个ul :
OK,现在可以写一个测试: it('should add "active" when the route changes', inject(function () { var elm = $compile('Hello')($scope); $location.path('/not-matching'); expect(elm.hasClass('active')).toBeFalsey(); $location.path('/hello'); expect(elm.hasClass('active')).toBeTruthy(); }));
执行这个测试来确认它是失败的。然后我们可以开始写这个directive了: .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'); } }); } }; });
测试现在通过了,然后我们的menu按照请求的方式执行。开发过程既是迭代的也是测试驱动的。太酷了。 5. 概念上,Directives并不是打包的jQuery 你经常会听到“只在directive里做DOM操作”。这是必需的。请给它应有的尊重! 但让我们再深入一点…… 一些directive仅仅装饰了视图中已经存在的东西(想想ngClass)并且因此有时候仅仅直接做完DOM操作然后就完事了。但是如果一个 directive像一个“widget”并且有一个模版,那么它也要做到关注点分离。也就是说,模版本身也应该很大程度上与其link和 controller实现保持独立。 AngularJS拥有一整套工具使这个过程非常简单;有了ngClass我们可以动态地更新class;ngBind使得我们可以做双向数据绑定。ngShow和ngHide可编程地展示和隐藏一个元素;以及更多地 —— 包括那些我们自己写的。换句话说,我们可以做到任何DOM操作能实现的特性。DOM操作越少,directive就越容易测试,也越容易给它们添加样式,在未来也越容易拥抱变化,并且更加的可复用和发布。 我见过很多AngularJS新手,把一堆jQuery扔到directive里。换句话说,他们认为“因为不能在controller里做DOM操作,就把那些代码弄到directive里好了”。虽然这么做确实好一些,但是依然是错误的。 回想一下我们在第3节里写的那个logger。即使要把它放在一个directive里,我们依然希望用“Angular Way”来做。它依然没有任何DOM操作!有很多时候DOM操作是必要的,但其实比你想的要少得多!在应用里的任何地方做DOM操作之前,问问你自己是不是真的需要这么做。有可能有更好的方式。 这里有一个示例,展示出了我见过最多的一种模式。我们想做一个可以toggle的按钮。(注意:这个例子有一点牵强、啰嗦,这是为了表达出使用同样方式处理问题的更复杂的情况。) .directive('myDirective', function () { return { template: 'Toggle me!', link: function (scope, element, attrs) { var on = false; $(element).click(function () { if (on) { $(element).removeClass('active'); } else { $(element).addClass('active'); } on = !on; }); } }; });