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

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

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


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

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

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

    2.不要用AngularJS扩充jQuery

    同样,不要从jQuery的X,Y和Z这个概念开始,所以我只需在模型和控制器上添加AngularJS。 当你刚开始时,这真的很诱人,这就是为什么我总是建议新的AngularJS开发人员根本不使用jQuery,至少在他们习惯于以“Angular Way”做事情之前。

    我在这里和邮件列表上看到许多开发人员用150或200行代码的jQuery插件创建了这些精心设计的解决方案,然后他们通过一系列回调和$apply来将它们粘贴到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)。 如果你来自服务器端语言(从Java到PHP),那么你可能已经熟悉了这个概念,但是如果你是来自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允许双向数据绑定; ngShowngHide编程方式显示或隐藏一个元素; 还有更多 - 包括我们自己写的。 换句话说,我们可以在没有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);
                });
            }
        };
    });
    

    这有几个问题:

  • 首先,jQuery从来没有必要。 我们在这里没有任何需要jQuery的东西!
  • 其次,即使我们的页面上已经有jQuery,也没有理由在这里使用它; 我们可以简单地使用angular.element并且在放入没有jQuery的项目中时,我们的组件仍然可以工作。
  • 第三,即使假定这个指令需要jQuery工作,jqLit​​e( angular.element )也会在加载时使用jQuery! 所以我们不需要使用$ - 我们可以使用angular.element
  • 四是密切相关的第三个,就是jqLit​​e元件不必被包裹在$ -的element传递给link功能将已经是jQuery的元素!
  • 第五,我们在前面几节中已经提到过,为什么我们将模板内容混合到我们的逻辑中?
  • 这条指令可以被重写(即使对于非常复杂的情况也是如此):

    .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来解决它,为你做更多的工作。


    命令式→声明式

    在jQuery中,选择器用于查找DOM元素,然后绑定/注册事件处理程序。 当事件触发时,该(必要的)代码将执行以更新/更改DOM。

    在AngularJS中,你想要考虑视图而不是DOM元素。 视图是包含AngularJS 指令的 (声明式)HTML。 指令为我们在幕后设置事件处理程序,并为我们提供动态数据绑定。 选择器很少使用,因此对ID(以及某些类的类型)的需求大大减少。 视图与模型绑定(通过范围)。 视图是模型的投影。 事件更改模型(即数据,范围属性),以及投影这些模型的视图“自动”更新。

    在AngularJS中,考虑一下模型,而不是jQuery选择的DOM元素来保存你的数据。 将视图看作是对这些模型的预测,而不是注册回调来操纵用户看到的内容。

    关注点分离

    jQuery采用了不显眼的JavaScript - 行为(JavaScript)与结构(HTML)分离。

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

    另请参阅https://stackoverflow.com/a/14346528/215945

    应用设计

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

  • 想想你的模型。 为这些模型创建服务或您自己的JavaScript对象。
  • 想想你想如何展示你的模型 - 你的观点。 为每个视图创建HTML模板,使用必要的指令获取动态数据绑定。
  • 将控制器连接到每个视图(使用ng-view和routing或ng-controller)。 让控制器仅查找/获取视图需要完成其工作的任何模型数据。 尽可能使控制器变薄。
  • 原型继承

    不用了解JavaScript原型继承如何工作,就可以用jQuery做很多事情。 在开发AngularJS应用程序时,如果你对JavaScript继承有很好的理解,你将避免一些常见的陷阱。 推荐阅读:AngularJS中范围原型/原型继承的细微差别是什么?


    AngularJS与jQuery

    AngularJS和jQuery采用非常不同的思想。 如果您来自jQuery,您可能会发现一些令人惊讶的差异。 角度可能会让你生气。

    这是正常的,你应该通过。 Angular是值得的。

    最大差异(TLDR)

    jQuery为您提供了一个工具包,用于选择DOM的任意位并对其进行临时更改。 你可以做很多你喜欢的部分。

    AngularJS改为给你一个编译器

    这意味着AngularJS会从上到下读取整个DOM,并将其视为代码,实际上是对编译器的指示。 在遍历DOM时,它会查找特定的指令 (编译器指令),告诉AngularJS编译器如何操作以及如何操作。 指令是充满JavaScript的小对象,可以匹配属性,标签,类甚至注释。

    当Angular编译器确定一段DOM与特定指令相匹配时,它会调用指令函数,将它传递给DOM元素,任何属性,当前$ scope(这是一个局部变量存储)以及其他一些有用的位。 这些属性可能包含可以由Directive解释的表达式,以及告诉它如何渲染以及何时应该重绘自己的表达式。

    然后,指令可以依次引入额外的Angular组件,例如控制器,服务等。编译器的底部是一个完全形成的Web应用程序,已经连接并准备就绪。

    这意味着Angular是模板驱动的 。 您的模板驱动JavaScript,而不是其他方式。 这是角色的彻底逆转,与我们在过去10年左右一直在写的不引人注目的JavaScript截然相反。 这可能需要一些习惯。

    如果这听起来像是可能是过度规定和限制的话,没有比这个事实更远的了。 由于AngularJS将HTML视为代码,因此您可以在Web应用程序中获得HTML级别的粒度 。 一切都是可能的,一旦你进行了一些概念上的飞跃,大部分事情都是非常容易的。

    让我们回到本质上。

    首先,Angular不会取代jQuery

    Angular和jQuery做了不同的事情。 AngularJS为您提供了一套用于生成Web应用程序的工具。 jQuery主要为您提供修改DOM的工具。 如果jQuery存在于你的页面上,AngularJS会自动使用它。 如果不是的话,AngularJS会附带jQuery Lite,这是一个简化版,但仍然是完美可用的jQuery版本。

    Misko喜欢jQuery,并不反对你使用它。 然而,随着您的发展,您会发现使用范围,模板和指令可以完成大部分工作,而且您应该尽可能使用此工作流程,因为您的代码将更加独立,更易于配置,而且更多角。

    如果你确实使用jQuery,你不应该把它放在所有的地方。 AngularJS中DOM操作的正确位置在一个指令中。 更多关于这些。

    带有选择器和声明模板的不显眼的JavaScript

    jQuery通常不被引人注目地应用。 您的JavaScript代码链接在页眉(或页脚)中,并且这是唯一提及的地方。 我们使用选择器来挑选页面的各个部分,并编写插件来修改这些部分。

    JavaScript是在控制之中。 HTML具有完全独立的存在。 即使没有JavaScript,你的HTML仍然是语义的。 Onclick属性是非常糟糕的做法。

    你会注意到关于AngularJS的第一件事情是自定义属性无处不在 。 你的HTML将被ng属性所抛弃,这些属性本质上是类固醇上的onClick属性。 这些是指令(编译器指令),是模板挂钩到模型的主要方式之一。

    当你第一次看到这个时,你可能会试图将AngularJS写成老派入侵的JavaScript(就像我最初做的那样)。 事实上,AngularJS不会遵守这些规则。 在AngularJS中,你的HTML5是一个模板。 它由AngularJS编译生成您的网页。

    这是第一大区别。 对于jQuery,你的网页是一个DOM被操纵。 对于AngularJS,您的HTML是要编译的代码。 AngularJS读入您的整个网页,并使用其内置的编译器将其编译成新的网页。

    你的模板应该是声明式的; 它的含义应该通过阅读而清楚。 我们使用有意义名称的自定义属性。 我们用有意义的名字组成新的HTML元素。 具有最小HTML知识和无编码技能的设计人员可以阅读您的AngularJS模板,并了解它在做什么。 他或她可以进行修改。 这是角度的方式。

    模板在驾驶座位上。

    在启动AngularJS并运行教程时,我问自己的第一个问题是“我的代码在哪里?” 。 我写了没有JavaScript,但我有这一切的行为。 答案很明显。 由于AngularJS编译DOM,AngularJS将HTML视为代码。 对于很多简单的情况,只需编写一个模板并让AngularJS将它编译成一个应用程序就足够了。

    你的模板驱动你的应用程序 它被视为DSL。 您编写AngularJS组件,而AngularJS将负责根据模板的结构在合适的时间提取它们并使其可用。 这与标准MVC模式非常不同,模板仅用于输出。

    例如,它比Ruby on Rails更类似于XSLT。

    这是一个需要一些习惯的控制的彻底颠倒。

    不要试图从你的JavaScript驱动你的应用程序。 让模板驱动应用程序,让AngularJS负责将组件连接在一起。 这也是角度的方式。

    语义HTML与语义模型

    使用jQuery,你的HTML页面应该包含语义上有意义的内容。 如果JavaScript被关闭(通过用户或搜索引擎),您的内容仍然可以访问。

    因为AngularJS把你的HTML页面当作模板。 模板不应该是语义的,因为您的内容通常存储在最终来自您的API的模型中。 AngularJS使用模型编译您的DOM以生成语义网页。

    您的HTML源不再是语义的,相反,您的API和编译的DOM是语义的。

    在AngularJS中,意味着生活在模型中,HTML仅仅是一个模板,仅用于显示。

    在这一点上,你可能有各种有关搜索引擎优化和可访问性的问题,这是正确的。 这里有一些未解决的问题。 大多数屏幕阅读器现在将解析JavaScript。 搜索引擎也可以索引AJAX的内容。 不过,你需要确保你使用pushstate网址,并且你有一个体面的网站地图。 有关此问题的讨论,请参阅https://stackoverflow.com/a/23245379/687677

    关注点分离(SOC)与MVC

    问题分离(SOC)是一种由于各种原因(包括搜索引擎优化,可访问性和浏览器不兼容性)而在多年网络开发中长大的模式。 它看起来像这样:

  • HTML - 语义意义。 HTML应该独立。
  • CSS - 样式,没有CSS,页面仍然可读。
  • JavaScript - 行为,没有脚本的内容仍然存在。
  • 同样,AngularJS不会按照他们的规则玩牌。 一举中AngularJS取消了十年的收获智慧 ,而是实现了一种MVC模式,其中模板不再是语义的,甚至没有一点点。

    它看起来像这样:

  • 模型 - 您的模型包含您的语义数据。 模型通常是JSON对象。 模型作为名为$ scope的对象的属性存在。 您还可以将方便的实用程序功能存储在您的模板可以访问的$ scope中。
  • 查看 - 您的意见是用HTML编写的。 视图通常不是语义的,因为您的数据存在于模型中。
  • 控制器 - 您的控制器是一个将视图挂接到模型的JavaScript函数。 它的功能是初始化$ scope。 根据您的应用程序,您可能需要也可能不需要创建控制器。 您可以在页面上拥有多个控制器。
  • MVC和SOC不在相同尺度的两端,它们在完全不同的轴上。 SOC在AngularJS上下文中没有意义。 你必须忘记它并继续前进。

    如果像我一样,你经历了浏览器的战争,你可能会发现这个想法非常冒犯。 克服它,这是值得的,我保证。

    插件与指令

    插件扩展了jQuery。 AngularJS指令扩展了浏览器的功能。

    在jQuery中,我们通过向jQuery.prototype添加函数来定义插件。 然后我们通过选择元素并在结果上调用插件来将这些钩到DOM中。 这个想法是扩展jQuery的功能。

    例如,如果你想在你的页面上使用传送带,你可以定义一个无序的数字列表,也许包裹在一个导航元素中。 然后,您可能会编写一些jQuery来选择页面上的列表,并将其重新定义为具有超时的画廊以执行滑动动画。

    在AngularJS中,我们定义了指令。 指令是一个返回JSON对象的函数。 该对象告诉AngularJS要查找哪些DOM元素以及对它们做出什么更改。 指令使用您创建的属性或元素连接到模板。 这个想法是用新的属性和元素来扩展HTML的功能。

    AngularJS的方式是扩展原生HTML的功能。 您应该编写看起来像HTML的HTML,并使用自定义属性和元素进行扩展。

    如果你想要一个旋转木马,只需使用一个<carousel />元素,然后定义一个指令来拉入模板,并使该吸盘工作。

    很多小指令与带配置开关的大插件

    jQuery的趋势是编写像lightbox这样的大插件,然后我们通过传递许多值和选项来配置。

    这在AngularJS中是一个错误。

    以下拉菜单为例。 当编写一个下拉式插件时,你可能试图在点击处理程序中编写代码,也许这是一个函数,可以添加一个可以向上或向下的V形图,也可以改变展开元素的类,显示隐藏菜单和所有有用的东西。

    直到你想做一个小小的改变。

    假设你有一个你想展开悬停的菜单。 那么现在我们有一个问题。 我们的插件已经连接到了我们的点击处理程序,我们需要添加一个配置选项,以使其在这种特定情况下的行为不同。

    在AngularJS中,我们编写了更小的指令。 我们的下拉指令可能会很小。 它可以保持折叠状态,并提供方法fold(),unfold()或toggle()。 这些方法只会更新$ scope.menu.visible,这是一个保存状态的布尔值。

    现在在我们的模板中,我们可以连接这个:

    <a ng-click="toggle()">Menu</a>
    <ul ng-show="menu.visible">
      ...
    </ul>
    

    需要更新鼠标悬停?

    <a ng-mouseenter="unfold()" ng-mouseleave="fold()">Menu</a>
    <ul ng-show="menu.visible">
      ...
    </ul>
    

    该模板驱动应用程序,以便获得HTML级别的粒度。 如果我们想要逐案例外,模板使得这一点变得简单。

    关闭与$范围

    JQuery插件是在闭包中创建的。 隐私保持在该关闭中。 这取决于你在范围内维护你的范围链。 您只能真正访问通过jQuery传入插件的DOM节点集合,以及封闭体中定义的任何局部变量以及您定义的任何全局变量。 这意味着插件相当独立。 这是一件好事,但在创建整个应用程序时可能会受到限制。 尝试在动态页面的各个部分之间传递数据变成了一件苦差事。

    AngularJS有$ scope对象。 这些是AngularJS创建和维护的特殊对象,您可以在其中存储模型。 某些指令会产生一个新的$ scope,默认情况下,它使用JavaScript原型继承从它的包装$ scope继承。 $ scope对象可以在控制器和视图中访问。

    这是一个聪明的部分。 由于$ scope继承的结构大致遵循DOM的结构,因此元素可以无缝地访问其自己的作用域和任何包含的作用域,直至全局$作用域(与全局作用域不一样)。

    这使得传递数据以及将数据存储在适当的级别变得更加容易。 如果展开下拉菜单,只有下拉$ scope需要知道它。 如果用户更新他们的首选项,则可能需要更新全局$范围,并且会自动收到监听用户首选项的任何嵌套范围。

    这听起来很复杂,事实上,一旦你放松下来,就像飞行一样。 您不需要创建$ scope对象,AngularJS会根据您的模板层次结构正确适当地为您进行实例化和配置。 然后AngularJS使用依赖注入的魔力使它可用于你的组件(后面会详细介绍)。

    手动DOM更改与数据绑定

    在jQuery中,您手动完成所有DOM更改。 您以编程方式构建新的DOM元素。 如果你有一个JSON数组,并且你想把它放到DOM中,你必须编写一个函数来生成HTML并插入它。

    在AngularJS中,你也可以这样做,但鼓励你使用数据绑定。 更改你的模型,并且由于DOM通过模板绑定到DOM,DOM将自动更新,无需干预。

    因为数据绑定是通过模板完成的,所以使用属性或大括号语法,这是非常容易的。 与它相关的认知开销很小,所以你会发现自己一直在做。

    <input ng-model="user.name" />
    

    将输入元素绑定到$scope.user.name 。 更新输入将更新当前范围中的值,反之亦然。

    同样:

    <p>
      {{user.name}}
    </p>
    

    将在一个段落中输出用户名。 这是一个活动绑定,因此如果$scope.user.name值更新,模板也会更新。

    阿贾克斯所有的时间

    在jQuery中进行Ajax调用非常简单,但它仍然是您可能会考虑的问题。 思考的复杂性增加了,而且要维护一大堆脚本。

    在AngularJS中,Ajax是您默认的解决方案,它始终在发生,几乎没有您注意。 您可以使用ng-include包含模板。 您可以使用最简单的自定义指令来应用模板。 您可以在服务中包装Ajax调用,并创建自己的GitHub服务或Flickr服务,您可以轻松访问该服务。

    服务对象vs辅助函数

    在jQuery中,如果我们想完成一个小的与非DOM相关的任务,比如从API中提取一个feed,我们可能会编写一个小函数在我们的闭包中实现。 这是一个有效的解决方案,但如果我们想经常访问该Feed,该怎么办? 如果我们想在其他应用程序中重用该代码,该怎么办?

    AngularJS为我们提供服务对象。

    服务是包含函数和数据的简单对象。 他们总是单身,意味着永远不会超过一个。 假设我们想要访问堆栈溢出API,我们可以编写一个StackOverflowService来定义这样做的方法。

    假设我们有一个购物车。 我们可能会定义一个ShoppingCartService来维护我们的购物车并且包含添加和移除物品的方法。 由于该服务是单例服务,并且由所有其他组件共享,因此任何需要写入购物车并从中拉取数据的对象都可以使用。 它始终是同一个购物车。

    服务对象是自包含的AngularJS组件,我们可以在我们认为合适的时候使用和重用。 它们是包含函数和数据的简单JSON对象。 它们总是单身,所以如果你将数据存储在一个服务中,你可以通过请求相同的服务将数据发送到别的地方。

    依赖注入(DI)与Instatiation - 又名去spaghettification

    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,而应考虑使用HTML扩展。

    我的小书

    我对AngularJS感到非常兴奋,我写了一本关于它的简短书籍,欢迎您在线阅读http://nicholasjohnson.com/angular-book/。 我希望这有帮助。

    链接地址: http://www.djcxy.com/p/57.html

    上一篇: "Thinking in AngularJS" if I have a jQuery background?

    下一篇: How do I check out a remote Git branch?