如果我有jQuery背景,如何用AngularJS思考?

4506
假设我熟悉使用jQuery开发客户端应用程序,但现在我想开始使用AngularJS。你能描述必要的范式转变吗?以下是一些可能帮助你构思答案的问题:
  • 我如何以不同的方式架构和设计客户端Web应用程序?最大的区别是什么?
  • 我应该停止使用什么?应该使用什么来代替?
  • 是否有任何服务器端的考虑/限制?
我不需要详细比较jQueryAngularJS之间的区别。
15个回答

7161

1. 不要先设计页面,再用DOM操纵更改

在jQuery中,你先设计一个页面,然后再使它变得动态。这是因为jQuery的设计初衷是增强性能,在此基础上得到了极大的发展。

但在AngularJS中,你必须从头开始考虑你的架构。你不能从“我有一个DOM部分,我想让它做X”这样的思路开始,而必须从你想要实现什么开始,然后设计你的应用程序,最后再去设计你的视图。

2. 不要将AngularJS与jQuery混合使用

同样地,不要从jQuery已经具备X、Y和Z的功能出发,然后只需添加AngularJS来进行模型和控制器的操作。当你刚开始学习时,这种想法确实很诱人,这就是为什么我始终建议新的AngularJS开发者根本不要使用jQuery,至少要在他们习惯“Angular Way”之后再使用。

我看到许多开发者在这里和邮件列表上创建了使用 jQuery 插件的复杂解决方案,代码有 150 或 200 行,然后用一系列回调函数和 $apply 进行粘合,这些函数令人困惑和混乱;但他们最终使其工作!问题是,在大多数情况下,那个 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 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允许双向数据绑定;ngShowngHide可编程地显示或隐藏元素;还有很多其他的工具 - 包括我们自己编写的工具。换句话说,我们可以在不进行DOM操作的情况下实现各种神奇的功能。越少的DOM操作,指令就越容易测试,样式也更容易,未来修改时也更容易,而且它们更可重用和可分发。
    我看到很多新手开发者在AngularJS中使用指令来扔一堆jQuery代码。换句话说,他们认为“由于我不能在控制器中进行DOM操作,所以我将那些代码放入指令中”。虽然这确实好得多,但通常仍然是错误的。
    考虑我们在第三部分编程的记录器。即使我们将其放置在指令中,我们仍希望按照“Angular Way”进行操作。它仍然不需要进行任何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. 第三,即使假设jQuery对于此指令是必需的,jqLite(angular.element)将始终使用已加载的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解决它会给你带来更多的工作。


406

命令式 → 声明式

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

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

在 AngularJS 中,思考模型,而不是包含数据的 jQuery 选定 DOM 元素。将视图视为这些模型的投影,而不是注册回调以操作用户所看到的内容。

关注点分离

jQuery使用不侵入式JavaScript - 行为(JavaScript)与结构(HTML)分离。

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

另请参见https://dev59.com/MWYq5IYBdhLWcg3woB0W#14346528

应用程序设计

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

  1. 考虑您的模型。为这些模型创建服务或自己的JavaScript对象。
  2. 考虑如何呈现您的模型 - 您的视图。为每个视图创建HTML模板,使用必要的指令来获得动态数据绑定。
  3. 将控制器附加到每个视图(使用ng-view和路由或ng-controller)。让控制器只查找/获取视图需要完成其工作的任何模型数据。使控制器尽可能轻量级。

原型继承

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


184

AngularJS vs. jQuery

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

这是正常的,你应该坚持下去。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,并且不反对您使用它。但是,随着您的进步,您会发现可以使用作用域、模板和指令的组合来完成几乎所有工作,您应该尽可能地使用这种工作流程,因为您的代码将更加离散、可配置和更具Angular性。
如果您确实使用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模板并理解它正在做什么。他或她可以进行修改。这就是Angular的方式。

模板处于驾驶座位。

作为一个AngularJS初学者,我在学习和运行教程时首先问自己的问题之一是“我的代码在哪里?”。我没有编写任何JavaScript代码,但是我却有了所有这些行为。答案很明显。因为AngularJS编译DOM,所以AngularJS将您的HTML视为代码。对于许多简单情况,只需编写模板即可,让AngularJS将其编译为应用程序。

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

这更类似于XSLT而不是Ruby on Rails,这是一种彻底的控制反转,需要一些时间来适应。

停止试图从JavaScript驱动您的应用程序。让模板驱动应用程序,让AngularJS负责将组件连接起来。这也是Angular的方式。
语义化HTML vs. 语义化模型
使用jQuery时,您的HTML页面应包含语义化的有意义的内容。如果JavaScript关闭(由用户或搜索引擎),则您的内容仍然可访问。
因为AngularJS将您的HTML页面视为模板。模板不应该像您的内容一样具有语义性,因为您的内容通常存储在模型中,最终来自API。 AngularJS使用模型编译DOM以生成语义化的Web页面。
您的HTML源代码不再具有语义性,而是您的API和编译后的DOM具有语义性。
在AngularJS中,意义存在于模型中,HTML只是用于显示的模板。

目前您可能对SEO和可访问性有各种问题,这是正常的。这里存在一些未解决的问题。大多数屏幕阅读器现在可以解析JavaScript。搜索引擎也可以索引AJAXed内容。尽管如此,您仍需要确保使用pushstate URL并拥有一个良好的网站地图。请参见这里以了解该问题的讨论:https://dev59.com/xWYr5IYBdhLWcg3wqr3o#23245379

关注点分离(SOC)与MVC

关注点分离(SOC)是一个模式,经过多年的Web开发出现,原因包括SEO、可访问性和浏览器不兼容性。它看起来像这样:

  1. HTML - 语义化含义。HTML应该独立存在。
  2. CSS - 样式,没有CSS页面仍然可读。
  3. JavaScript - 行为,没有脚本内容仍然存在。
再次强调,AngularJS不按照传统规则操作。一下子,AngularJS抛弃了十年的传统智慧,并且实现了MVC模式,其中模板不再是语义化的,即使只有一点点。

它看起来像这样:

  1. Model - 模型包含语义数据。模型通常是JSON对象。模型作为一个名为$scope的对象的属性存在。您还可以在$scope上存储方便的实用程序函数,然后您的模板可以访问这些函数。
  2. View - 视图是用HTML编写的。视图通常不是语义化的,因为您的数据存储在模型中。
  3. Controller - 控制器是一个JavaScript函数,将视图连接到模型。它的功能是初始化$scope。根据您的应用程序,您可能需要创建控制器,也可能不需要。您可以在页面上拥有多个控制器。

MVC和SOC不是同一尺度上的相反极端,它们处于完全不同的轴线上。在AngularJS环境中,SOC没有任何意义。您必须忘记它并继续前进。

如果像我一样经历了浏览器战争,您可能会觉得这个想法相当冒犯。克服它,它会值得的,我保证。

插件与指令

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

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

例如,如果您想在页面上使用旋转木马,您可以定义一个无序的图像列表,可能包装在nav元素中。然后,您可以编写一些jQuery代码来选择页面上的列表,并将其重新设计为具有超时执行滑动动画的画廊。

在AngularJS中,我们定义指令。指令是返回JSON对象的函数。该对象告诉AngularJS要查找哪些DOM元素以及对它们进行哪些更改。指令使用属性或元素将其挂接到模板中,这些属性或元素是您自己发明的。其思想是使用新的属性和元素扩展HTML的功能。

AngularJS的方法是扩展本地外观的HTML的功能。您应该编写看起来像HTML的HTML,其中包含自定义属性和元素。

如果您想使用旋转木马,请只需使用<carousel />元素,然后定义一个指令来引入模板,并使其正常工作。

许多小指令 vs. 具有配置开关的大型插件

在jQuery中的倾向是编写非常大的插件,例如light box,然后通过传递众多值和选项来进行配置。

在AngularJS中这是一个错误。

以下拉菜单为例。当编写下拉菜单插件时,您可能会试图编写点击处理程序,或者添加一个朝上或朝下的尖头函数,也许更改未展开元素的类,显示/隐藏菜单等等有用的东西。

但是当您想要进行一些小更改时就会遇到问题。

比如说,您希望在悬停时展开菜单。现在我们有一个问题了。我们的插件已经为我们连接了点击处理程序,我们需要添加一个配置选项来使它在特定情况下表现不同。

在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级别的细粒度。如果我们想要进行逐案例的例外处理,模板会使这变得容易。
闭包与$scope JQuery插件是在闭包中创建的。隐私在该闭包内保持。在该闭包内,您需要维护自己的作用域链。您实际上只能访问由jQuery传递给插件的DOM节点集合,以及在闭包中定义的任何局部变量和您定义的任何全局变量。这意味着插件是相当自包含的。这是一件好事,但在创建整个应用程序时可能会受到限制。尝试在动态页面的各个部分之间传递数据会变得很麻烦。
AngularJS具有$scope对象。这些是由AngularJS创建和维护的特殊对象,在其中存储您的模型。某些指令将生成一个新的$scope,默认情况下使用JavaScript原型继承从其包装$scope继承。$scope对象在控制器和视图中都可以访问。
这是聪明的部分。因为$scope继承的结构大致遵循DOM的结构,所以元素可以访问它们自己的作用域以及任何包含作用域,无缝地一直到全局$scope(不是全局作用域)。
这使得数据传输更加容易,可以在适当的层次上存储数据。如果展开下拉菜单,则只需下拉菜单 $scope 知道它。如果用户更新其偏好设置,则可能要更新全局 $scope,并且任何监听用户偏好设置的嵌套作用域都会自动收到通知。
这听起来可能很复杂,但一旦你适应了它,就像飞行一样简单。您不需要创建 $scope 对象,AngularJS 会根据您的模板层次结构实例化并配置它,正确而适当地使其对组件可用,使用依赖注入的魔力(稍后会介绍)。
手动 DOM 更改与数据绑定
在 jQuery 中,您必须手动进行所有 DOM 更改。您可以通过编程方式构建新的 DOM 元素。如果您有一个 JSON 数组,想将其放置到 DOM 中,您必须编写一个函数来生成 HTML 并插入它。
在 AngularJS 中,您也可以这样做,但是建议您利用数据绑定。更改您的模型,因为 DOM 通过模板绑定到它,所以您的 DOM 将自动更新,无需干预。

因为数据绑定是从模板中完成的,可以使用属性或大括号语法,所以非常容易实现。它几乎没有认知负荷,所以你会发现自己一直在使用它。

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

将输入元素绑定到$scope.user.name。更新输入将更新当前作用域中的值,反之亦然。
同样地:
<p>
  {{user.name}}
</p>

将会在段落中输出用户名。这是一个实时绑定,所以如果$scope.user.name的值被更新,模板也会被更新。

始终使用Ajax

在jQuery中,发起Ajax调用非常简单,但你可能会考虑两次。需要考虑额外的复杂性和大量需要维护的脚本。

在AngularJS中,Ajax是默认解决方案,几乎无需注意就能一直使用它。可以使用ng-include包含模板。可以使用最简单的自定义指令应用模板。可以在服务中包装一个Ajax调用并创建GitHub服务或Flickr服务,然后轻松访问。

服务对象 vs 帮助函数

在jQuery中,如果我们想完成一个小的非DOM相关任务(例如从API中获取信息),我们可能会在闭包中编写一个小函数来完成。那是一个有效的解决方案,但如果我们经常需要访问该信息怎么办?如果我们想在另一个应用程序中重用该代码怎么办?

AngularJS提供了服务对象。

服务是包含函数和数据的简单对象。它们始终是单例的,这意味着永远不会有多个。假设我们想要访问Stack Overflow API,我们可以编写一个StackOverflowService,其中定义了执行此操作的方法。

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

服务对象是自包含的AngularJS组件,我们可以根据需要使用和重用它们。它们是包含函数和数据的简单JSON对象。它们始终是单例的,因此如果您在一个地方存储服务上的数据,则可以通过请求相同的服务在其他地方获取该数据。

依赖注入(DI)与实例化-又称去除代码纷乱

AngularJS为您管理依赖项。如果您想要一个对象,只需引用它,AngularJS将为您获取它。

在你开始使用它之前,很难解释这是多么巨大的时间优势。jQuery内没有类似AngularJS DI的东西。

DI意味着,你不需要编写和连接应用程序,而是定义一个组件库,每个组件由一个字符串标识。

比如说我有一个名为“FlickrService”的组件,它定义了从Flickr获取JSON feed的方法。现在,如果我想编写一个可以访问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(模板,数据绑定,$scope,指令等)得当,你会发现你需要的jQuery比你原本需要的少得多。
要意识到的主要事情是你的模板驱动你的应用程序。停止尝试编写能做所有事情的大型插件。相反,编写小指令来完成一件事情,然后编写一个简单的模板将它们连接起来。
不要再考虑无侵入式JavaScript,而是考虑HTML扩展。
我的小书
我对AngularJS感到非常兴奋,所以我写了一本短小的书,你可以在线阅读 http://nicholasjohnson.com/angular-book/。希望对你有所帮助。

152

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

命令式 vs 声明式

使用 jQuery,您逐步告诉DOM需要发生什么。而使用AngularJS,您描述了想要的结果,但不描述如何实现。更多信息请参见这里。另外,请查看Mark Rajcok的答案。

我应该如何在架构和设计客户端Web应用程序方面有所不同?

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

最大的区别是什么?我应该停止使用什么,使用什么代替?

jQuery是一个库。

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

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

最好的开始方式是通过他们令人惊叹的教程。你可以在几个小时内完成步骤;然而,如果你想掌握背后的概念,他们包括大量参考资料供进一步阅读。

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

你可以在已经使用纯jQuery的现有应用程序中使用它。但是,如果您想充分利用AngularJS的功能,则可以考虑使用RESTful方法编写服务器端代码。
这样做将允许您利用他们的resource factory,它创建了您的服务器端RESTful API的抽象,并使服务器端调用(获取、保存、删除等)非常容易。

84

谈到“范式转变”,我认为简短的回答足以。

AngularJS改变了您查找元素的方式

jQuery中,通常使用选择器来查找元素,然后对其进行绑定:
$('#id .class').click(doStuff);

AngularJS中,您使用指令直接标记元素并将其绑定:
<a ng-click="doStuff()">

AngularJS不需要(也不希望)您使用选择器查找元素 - AngularJS的jqLite与全面的jQuery之间的主要区别在于,jqLite不支持选择器

因此,当人们说“根本不要包含jQuery”时,主要是因为他们不希望您使用选择器;他们希望您学会使用指令。 直接而非选择!


69

jQuery

jQuery可以让像getElementByHerpDerp这样的漫长JavaScript命令更简洁,并且跨浏览器。

AngularJS

AngularJS允许您创建自己的HTML标签/属性,以便与动态Web应用程序良好配合使用(因为HTML是针对静态页面设计的)。

编辑:

说“我有jQuery背景,如何思考AngularJS?”就像说“我有HTML背景,如何思考JavaScript?”你提出这个问题实际上表明你很可能不理解这两种资源的根本目的。这就是为什么我选择通过简单指出基本区别来回答问题,而不是逐项列出说“AngularJS利用指令,而jQuery使用CSS选择器来创建一个jQuery对象,然后做这个那个等等...”。这个问题不需要冗长的答案。

jQuery是一种使在浏览器中编写JavaScript更容易的方式。它可以缩短命令长度,实现跨浏览器等功能。

AngularJS扩展了HTML,因此您不必在所有地方都放置<div>来制作应用程序。它使HTML实际上可以为应用程序工作,而不是其最初的设计目的:用于静态教育性网页。它通过JavaScript以巧妙的方式实现这一点,但从根本上说,它是HTML的扩展,而不是JavaScript。


61

jQuery:你会考虑在“查询DOM”中查找DOM元素并执行操作。

AngularJS:模型是真相,你总是从这个角度思考。

例如,当你从服务器获取数据并打算以某种格式在DOM中显示时,在jQuery中,你需要“1.查找”你想要放置此数据的DOM位置,“2.更新/追加”它,创建一个新节点或仅设置其innerHTML。然后,当你想要更新此视图时,你需要“3.查找”位置并“4.更新”。在AngularJS中,这个在同一上下文中完成查找和更新的循环已经消失了,因为你可以直接操作模型。

在AngularJS中,你有你的模型(JavaScript对象),模型的值告诉你关于模型(显然)和视图的信息,对模型的操作自动传播到视图,所以你不必考虑它。你会发现在AngularJS中,你不再需要在DOM中查找元素。

换句话说,在jQuery中,您需要考虑CSS选择器,即具有类或属性的
或在哪里,以便我可以获取它们的HTML、颜色或值,但在AngularJS中,您会发现自己这样思考:我正在处理哪个模型,我将把模型的值设置为true。您不必担心反映此值的视图是选中的框还是位于元素中(这些细节通常需要在jQuery中考虑)。
使用AngularJS进行DOM操作时,您会发现自己添加指令和过滤器,您可以将其视为有效的HTML扩展。
在AngularJS中,您将体验到另一件事:在jQuery中,您经常调用jQuery函数,在AngularJS中,AngularJS将调用您的函数,因此AngularJS会“告诉您如何做事”,但好处是值得的,因此学习AngularJS通常意味着学习AngularJS想要什么或AngularJS要求您呈现函数的方式,并相应地调用它。这是使AngularJS成为框架而不是库的原因之一。

46

这些答案都很好,但是有点冗长。

总结我的经验:

  1. 控制器和提供者(服务、工厂等)用于修改数据模型,而不是 HTML 。
  2. HTML 和指令定义布局和与模型的绑定。
  3. 如果您需要在控制器之间共享数据,请创建一个服务或工厂-它们是整个应用程序共享的单例。
  4. 如果您需要一个 HTML 小部件,请创建一个指令。
  5. 如果你有一些数据,现在正在尝试更新 HTML...停止!更新模型,并确保你的 HTML 与模型绑定。

45

jQuery是一个DOM操作库。

AngularJS是一个MV*框架。

事实上,AngularJS是为数不多的几个JavaScript MV*框架之一(许多JavaScript MVC工具仍属于库类别)。

作为一个框架,它托管您的代码并负责决定何时以及如何进行调用!

AngularJS本身包含了jQuery-lite版本。因此,对于一些基本的DOM选择/操作,您真的不必包括jQuery库(这可以节省网络运行的字节数)。

AngularJS有“指令”的概念,用于DOM操作和设计可重用的UI组件,因此在需要执行DOM操作相关任务时,应该使用它(在使用AngularJS时,指令是唯一编写jQuery代码的地方)。

AngularJS涉及一些学习曲线(比jQuery更多 :-)。

-> 对于任何来自jQuery背景的开发人员,我的第一个建议是“在跳入像AngularJS这样的丰富框架之前,先学习JavaScript作为一种一流语言!”我通过艰苦的方式学到了以上事实。

祝好运。


34

它们就像苹果和橙子一样。你不应该比较它们。它们是两个不同的东西。AngularJs已经内置了jQuery lite,允许您执行基本的DOM操作,甚至不需要包含完整的jQuery版本。

jQuery专注于DOM操作。它解决了跨浏览器问题,否则你将不得不处理,但它不是一个像AngularJS这样允许你将应用程序划分为组件的框架。

AngularJs的一个好处是它允许您在指令中分离/隔离DOM操作。有内置指令可供您使用,例如ng-click。您可以创建自己的自定义指令,其中将包含所有视图逻辑或DOM操作,以便您不会混合控制器或服务中应该处理业务逻辑的DOM操作代码。

Angular将您的应用程序分解为 - 控制器 - 服务 - 视图 - 等等

还有一件事,那就是指令。它是一个属性,您可以将其附加到任何DOM元素上,并且可以在其中使用jQuery而不必担心您的jQuery会与AngularJs组件发生冲突或破坏其架构。

我从一个我参加的聚会上听说,Angular的创始人之一说他们非常努力地分离DOM操作,因此不要尝试将它们包含在内。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接