“在AngularJS中思考”如果我有一个jQuery背景

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

  • 我如何构建和设计不同的客户端Web应用程序?最大的区别是什么?
  • 我应该停止做什么/使用什么; 我应该开始做什么/使用什么?
  • 有没有服务器端考虑/限制?

我不是在寻找jQuery和之间的详细比较AngularJS

答案


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

在jQuery中,您设计一个页面,然后使其变为动态的。这是因为jQuery是为增强而设计的,并且已经从这个简单的前提变得难以置信。

但是在AngularJS中,你必须从头开始考虑你的架构。不是从思考“我有这块DOM,而是想让它做X”开始,你必须从你想完成的任务开始,然后去设计你的应用程序,然后开始设计你的视图。

2.不要用AngularJS扩充jQuery

同样,不要从jQuery的X,Y和Z这个概念开始,所以我只需在模型和控制器上添加AngularJS。这是真的很诱人,当你刚刚起步的,这就是为什么我总是建议新AngularJS开发商不使用jQuery可言,至少直到他们习惯做的事情“角之路”。

我在这里和邮件列表上看到许多开发人员用150或200行代码的jQuery插件创建了这些精心设计的解决方案,然后他们通过一系列回调和$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意味着您可以非常自由地声明组件,然后从任何其他组件中请求它的一个实例并授予它。您不必知道加载顺序或文件位置或类似的东西。权力可能不会立即显现,但我只提供一个(常见)示例:测试。

比方说,在我们的应用程序中,我们需要一个通过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' );
                }
            });
        }
    };
});

我们的测试现在通过我们的菜单按要求执行。我们的发展既是迭代式的,也是测试驱动的。妖兽爽。

从概念上讲,指令包装jQuery

你会经常听到“只在指令中做DOM操作”。这是必要的。妥善处理!

但让我们深入一点…

一些指令只是装饰视图中已经存在的内容(思考ngClass),因此有时会立即执行DOM操作,然后基本完成。但是,如果一个指令就像一个“小部件”并且有一个模板,它应该尊重问题的分离。也就是说,模板应该在很大程度上独立于链接和控制器功能的实现。

AngularJS带有一套完整的工具,使其变得非常简单; 与ngClass我们可以动态更新类; ngModel允许双向数据绑定; ngShow并以ngHide编程方式显示或隐藏一个元素; 还有更多 – 包括我们自己写的。换句话说,我们可以在没有 DOM操作的情况下做各种精彩的事情。DOM操作越少,指令越容易测试,风格越容易,将来更容易更改,以及它们的可重用性和可分配性越高。

我看到许多新的AngularJS开发人员使用指令作为引发一堆jQuery的地方。换句话说,他们认为“因为我不能在控制器中进行DOM操作,所以我会将这些代码放入指令中”。虽然这当然好得多,但它通常仍然是错误的

想想我们在第3节中编写的记录器。即使我们把它放在指令中,我们仍然想要做到“角度的方式”。它仍然不需要任何DOM操作!有很多时候DOM操作是必要的,但比你想象的少得多!在你的应用程序的任何地方进行DOM操作之前,问问你自己是否真的需要。可能有更好的方法。

下面是一个显示我最常看到的模式的简单示例。我们想要一个可切换的按钮。(注意:这个例子是有点设计的,而skosh冗长来表示更复杂的情况,它们的解决方式完全相同。)

.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. 第三,即使假设jQuery的需要这种指令工作,jqLit​​e( angular.element)将始终使用jQuery,如果它是装的!所以我们不需要使用$– 我们可以使用angular.element
  4. 第四,与第三个密切相关的是,jqLit​​e元素不需要被包裹$– element传递给link函数的元素已经是 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来解决它,为你做更多的工作。

添加评论

友情链接:蝴蝶教程