"在AngularJS中思考"如果我有jQuery的背景?

假设我熟悉用jQuery开发客户端应用程序,但现在我想开始使用AngularJS。你能描述一下这种模式的转变是必要的吗?这里有几个问题可以帮助你确定答案。

  • 我如何以不同的方式架构和设计客户端网络应用?最大的区别是什么?
  • 我应该停止做/使用什么;我应该开始做/使用什么?
  • 是否有任何服务器端的考虑/限制?

我不是在寻找jQueryAngularJS之间的详细比较。

解决办法

1.不要设计你的页面,然后用DOM的操作来改变它

在jQuery中,你设计了一个页面,然后你让它变得动态。这是因为jQuery是为增强功能而设计的,并且从这个简单的前提下得到了惊人的发展。 但是在AngularJS中,你必须从头开始考虑你的架构。而不是一开始就想 "我有这块DOM,我想让它做X",你必须从你想完成的事情开始,然后去设计你的应用程序,最后再去设计你的视图。

2.不要用AngularJS增强jQuery的功能

同样的,不要一开始就认为jQuery能做X、Y和Z,所以我就在上面添加AngularJS来做模型和控制器。这在你刚开始的时候是非常*诱人的,这就是为什么我总是建议新的AngularJS开发者完全不使用jQuery,至少在他们习惯于用 "Angular方式 "做事之前。 我在这里和邮件列表中看到许多开发者用jQuery插件创建了150或200行代码的复杂解决方案,然后他们用一系列的回调和$applys粘在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)。如果你来自服务器端语言(从JavaPHP),你可能已经熟悉了这个概念,但如果你是一个来自jQuery的客户端人员,这个概念可能看起来很傻,很多余,很时髦。但事实并非如此:-) 从广义的角度来看,DI意味着你可以非常自由地声明组件,然后从任何其他的组件中,只要要求它的一个实例,它就会被授予。你不需要知道加载顺序,或者文件位置,或者类似的东西。这种力量可能不会立即显现出来,但我只提供一个(常见的)例子:测试。 假设在我们的应用程序中,我们需要一个通过RESTAPI实现服务器端存储的服务,并且,根据应用程序的状态,也需要本地存储。当在我们的控制器上运行测试时,我们不希望与服务器通信,毕竟我们是在测试*控制器。我们可以添加一个与原始组件同名的模拟服务,注入器将确保我们的控制器自动获得假的服务--我们的控制器不知道也不需要知道其中的区别。 说到测试...

4.测试驱动的开发--总是

这其实是第三部分架构的一部分,但它非常重要,所以我把它作为自己的顶级部分。 在你所见过的、使用过的或编写过的所有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允许双向数据绑定;ngShowngHide以编程方式显示或隐藏一个元素;还有更多--包括我们自己编写的工具。换句话说,我们可以做各种了不起的事情,而不需要DOM操作。对DOM的操作越少,指令就越容易测试,越容易形成风格,越容易在将来进行修改,也越容易重复使用和发布。 我看到很多刚接触AngularJS的开发者把指令当作扔一堆jQuery的地方。换句话说,他们认为 "既然我不能在控制器中进行DOM操作,我就把这些代码放在指令中"。虽然这样做肯定要好得多,但往往还是错的。 想想我们在第3节编写的记录器。即使我们把它放在一个指令中,我们仍然想用 "Angular方式 "来做。它仍然不需要任何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);
            });
        }
    };
});

这里面有几个问题。 1.首先,jQuery从来不是必要的。我们在这里做的任何事情都不需要jQuery! 2.第二,即使我们的页面上已经有了jQuery,也没有理由在这里使用它;我们可以简单地使用angular.element,当我们的组件被放到一个没有jQuery的项目中时,仍然可以工作。 3.3.第三,即使假设jQuery是这个指令的必要条件,jqLite(angular.element)也会在加载jQuery的情况下一直使用!所以我们不需要使用jQuery。所以我们不需要使用$ - 我们可以直接使用angular.element。 4.第四,与第三条密切相关的是,jqLite元素不需要用$来包装--传递给link函数的element已经是一个jQuery元素了! 5.第五,我们在前面的章节中提到过,为什么我们要把模板的东西混入我们的逻辑? 这个指令可以更简单地改写(即使是非常复杂的情况!),就像这样。

.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没有做一些开箱即用的事情,想想团队会如何完成它,以适应ngClickngClass等。

总结

甚至不要使用jQuery。甚至不要包括它。它将会阻碍你的发展。当你遇到一个你认为你已经知道如何用jQuery解决的问题时,在你伸手去拿"$"之前,试着想想如何在AngularJS的范围内完成它。如果你不知道,就问吧!20次中有19次,最好的方法是不需要jQuery的,而且试图用jQuery来解决这个问题的结果是给你带来更多的工作。

评论(22)

祈使句 → 声明句

在jQuery中,选择器被用来寻找DOM元素,然后将事件处理程序绑定/注册到它们。当一个事件被触发时,该(命令式)代码就会执行,以更新/改变DOM。

在AngularJS中,你要考虑的是视图而不是DOM元素。视图是包含AngularJS 指令的(声明性)HTML。指令在幕后为我们设置了事件处理程序,并为我们提供了动态数据绑定。选择器很少被使用,所以对ID(和某些类型的类)的需求大大减少了。视图与模型联系在一起(通过作用域)。视图是模型的一个投影。事件改变了模型(即数据、作用域属性),投射这些模型的视图会 "自动 "更新。

在AngularJS中,考虑模型,而不是jQuery选择的DOM元素,这些元素持有你的数据。把视图看成是这些模型的投影,而不是注册回调来操纵用户看到的东西。

关注点的分离

jQuery采用unobtrusive JavaScript - 行为(JavaScript)与结构(HTML)分离。

AngularJS使用控制器和指令(每个指令都可以有自己的控制器,和/或编译和链接功能)来从视图/结构(HTML)中移除行为。 Angular也有服务过滤器来帮助分离/组织你的应用程序。

另见https://stackoverflow.com/a/14346528/215945

应用程序设计

设计AngularJS应用程序的一种方法。

1.想一想你的模型。为这些模型创建服务或你自己的JavaScript对象。 2.2.考虑你想如何展示你的模型--你的视图。为每个视图创建HTML模板,使用必要的指令来获得动态数据绑定。 3.3.为每个视图附加一个控制器(使用ng-view和路由,或ng-controller)。让控制器只查找/获取视图所需的任何模型数据来完成其工作。使控制器尽可能的薄。

原型化的继承

你可以用jQuery做很多事情,却不知道JavaScript原型继承是如何工作的。在开发AngularJS应用程序时,如果你对JavaScript继承有很好的了解,你会避免一些常见的陷阱。推荐阅读:https://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs

评论(7)

你能描述一下有必要进行的范式转变吗?

默许式与声明式

jQuery你告诉DOM需要发生什么,一步一步来。用AngularJS你描述了你想要的结果,但没有描述如何去做。更多关于这方面的内容这里。另外,请看Mark Rajcok的回答。

我如何以不同的方式架构和设计客户端的Web应用?

AngularJS是一个使用MVC模式的整个客户端框架(查看他们的图形表示)。它非常注重关注点的分离。

最大的区别是什么?我应该停止做/使用什么;我应该开始做/使用什么?

jQuery是一个库

AngularJS是一个漂亮的客户端框架,高度可测试,它结合了大量很酷的东西,如MVC、依赖注入、数据绑定等。

它专注于关注点分离和测试(单元测试和端到端测试),这有利于测试驱动的开发。

开始的最好方法是通过他们令人敬畏的教程。你可以在几个小时内完成这些步骤;然而,如果你想掌握幕后的概念,他们包括无数的参考资料供进一步阅读。

是否有任何服务器端的考虑/限制?

你可以在已经使用纯jQuery的现有应用程序中使用它。然而,如果你想充分利用AngularJS的功能,你可以考虑使用RESTful的方法对服务器端进行编码。

这样做将允许你利用他们的资源工厂,它为你的服务器端RESTfulAPI创建一个抽象,使服务器端的调用(获取、保存、删除等)变得非常容易。

评论(4)