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

IT技术 javascript jquery angularjs
2020-12-17 22:03:34

假设我熟悉用jQuery开发客户端应用程序,但现在我想开始使用AngularJS你能描述一下必要的范式转变吗?以下是一些可能有助于您确定答案的问题:

  • 我如何以不同的方式构建和设计客户端 Web 应用程序?最大的区别是什么?
  • 我应该停止做什么/使用什么;我应该开始做什么/使用什么?
  • 是否有任何服务器端注意事项/限制?

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

6个回答

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' );
                }
            });
        }
    };
});

我们的测试现在通过了我们的菜单按要求执行。我们的发展,迭代测试驱动。邪恶的酷。

5. 从概念上讲,指令不是jQuery 封装的

您经常会听到“只在指令中进行 DOM 操作”。这是必需品。以应有的尊重对待它!

但是让我们深入一点......

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

AngularJS 附带了一整套工具,使这变得非常容易;ngClass我们可以动态更新的类; ngModel允许双向数据绑定;ngShow并以ngHide编程方式显示或隐藏元素;还有更多——包括我们自己编写的那些。换句话说,我们可以在没有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. 第三,即使假设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 都不会被触及,因此重构很容易。您可以根据需要随意更改模板,而无需更改指令。无论您更改什么,您的测试仍然通过。

哇!

因此,如果指令不仅仅是类似 jQuery 的函数的集合,那么它们是什么?指令实际上是 HTML 的扩展如果 HTML 没有做你需要它做的事情,你可以编写一个指令来为你做,然后就像它是 HTML 的一部分一样使用它。

换句话说,如果 AngularJS 不做一些开箱即用的事情,想想团队将如何完成它以适应ngClick, ngClass, 等。

概括

甚至不要使用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 应用程序的一种方法:

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

原型继承

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

AngularJS 与 jQuery

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

这是正常的,你应该挺过去。角是值得的。

大不同(TLDR)

jQuery 为您提供了一个工具包,用于选择 DOM 的任意位并对它们进行临时更改。你几乎可以一点一点地做任何你喜欢的事情。

相反,AngularJS 为您提供了一个编译器

这意味着 AngularJS 从上到下读取您的整个 DOM 并将其视为代码,字面意思是编译器的指令。当它遍历 DOM 时,它会寻找告诉 AngularJS 编译器如何表现和做什么的特定指令(编译器指令)。指令是充满 JavaScript 的小对象,可以匹配属性、标签、类甚至注释。

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

然后,指令可以反过来引入额外的 Angular 组件,例如控制器、服务等。编译器底部出来的是一个完整的 Web 应用程序,连接好并准备就绪。

这意味着 Angular 是模板驱动的您的模板驱动 JavaScript,而不是相反。这是一种彻底的角色转换,与我们过去 10 年左右一直在编写的不引人注目的 JavaScript 完全相反。这可能需要一些时间来适应。

如果这听起来可能过于规范和限制,那么事实并非如此。因为 AngularJS 将您的 HTML 视为代码,所以您可以在您的 Web 应用程序中获得HTML 级别的粒度一切皆有可能,一旦您进行了一些概念上的飞跃,大多数事情都会出奇地简单。

让我们深入了解细节。

首先,Angular 不会取代 jQuery

Angular 和 jQuery 做不同的事情。AngularJS 为您提供了一组用于生成 Web 应用程序的工具。jQuery 主要为您提供修改 DOM 的工具。如果您的页面上存在 jQuery,AngularJS 将自动使用它。如果不是,AngularJS 附带 jQuery Lite,它是 jQuery 的精简版,但仍然是完美可用的 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 模板并了解它在做什么。他或她可以进行修改。这是 Angular 的方式。

模板在驾驶座上。

在开始使用 AngularJS 并浏览教程时,我问自己的第一个问题是“我的代码在哪里?” . 我没有编写 JavaScript,但我有所有这些行为。答案是显而易见的。因为 AngularJS 编译 DOM,AngularJS 将您的 HTML 视为代码。对于许多简单的情况,通常只编写一个模板并让 AngularJS 为您编译成一个应用程序就足够了。

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

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

这是一种彻底的控制反转,需要一些时间来适应。

停止尝试通过 JavaScript 驱动您的应用程序。让模板驱动应用程序,让 AngularJS 负责将组件连接在一起。这也是 Angular 的方式。

语义 HTML 与语义模型

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

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

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

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

在这一点上,您可能对SEO和可访问性有各种疑问,这是正确的。这里有未解决的问题。大多数屏幕阅读器现在都会解析 JavaScript。搜索引擎还可以索引AJAX内容。尽管如此,您还是要确保您使用的是 pushstate URL 并且您有一个不错的站点地图。有关该问题的讨论,请参见此处:https : //stackoverflow.com/a/23245379/687677

关注点分离 (SOC) 与 MVC

关注点分离(SOC) 是一种在多年 Web 开发中成长起来的模式,其原因有多种,包括 SEO、可访问性和浏览器不兼容。它看起来像这样:

  1. HTML - 语义。HTML 应该是独立的。
  2. CSS - 样式,没有 CSS 页面仍然可读。
  3. JavaScript - 行为,没有脚本内容仍然存在。

同样,AngularJS 不遵守他们的规则。一下子,AngularJS 摒弃了十年公认的智慧,而是实现了一种 MVC 模式,其中模板不再具有语义,甚至没有一点。

它看起来像这样:

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

MVC 和 SOC 不在同一尺度的两端,它们在完全不同的轴上。SOC 在 AngularJS 上下文中毫无意义。你必须忘记它并继续前进。

如果你像我一样经历过浏览器大战,你可能会觉得这个想法很冒犯。克服它,这将是值得的,我保证。

插件与指令

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

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

例如,如果您希望页面上有一个轮播,您可以定义一个无序列表的数字,可能包含在一个 nav 元素中。然后,您可以编写一些 jQuery 来选择页面上的列表,并将其重新设置为带有超时的画廊以执行滑动动画。

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

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

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

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

jQuery 的趋势是编写像灯箱这样的大型插件,然后我们通过传入大量值和选项来配置这些插件。

这是 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 会根据您的模板层次结构正确且适当地为您实例化和配置它。然后 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) 与 Instatiation - aka de-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 会将它们一一交给我们。

module化服务架构

jQuery 很少说明应该如何组织代码。AngularJS 有意见。

AngularJS 为您提供了可以放置代码的module。例如,如果您正在编写一个与 Flickr 对话的脚本,您可能想要创建一个 Flickr module来包装所有与 Flickr 相关的功能。module可以包含其他module (DI)。您的主应用程序通常是一个module,这应该包括您的应用程序将依赖的所有其他module。

您可以获得简单的代码重用,如果您想编写另一个基于 Flickr 的应用程序,您可以只包含 Flickr module,瞧,您可以在新应用程序中访问所有与 Flickr 相关的功能。

module包含 AngularJS 组件。当我们包含一个module时,该module中的所有组件都可以作为一个由其唯一字符串标识的简单列表供我们使用然后我们可以使用 AngularJS 的依赖注入机制将这些组件相互注入。

总结

AngularJS 和 jQuery 不是敌人。可以很好地在 AngularJS 中使用 jQuery。如果您使用AngularJS以及(模板,数据绑定,$范围,指令等),你会发现你需要很多少jQuery的比,否则你可能需要。

要意识到的主要事情是您的模板驱动您的应用程序。停止尝试编写可以做所有事情的大插件。而是编写一些只做一件事的小指令,然后编写一个简单的模板将它们连接在一起。

少考虑不引人注目的 JavaScript,而是考虑 HTML 扩展。

我的小书

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

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

命令式与声明式

使用jQuery,您可以一步一步地告诉 DOM 需要发生什么。使用AngularJS,你可以描述你想要的结果,而不是如何去做。更多关于这个在这里另外,请查看 Mark Rajcok 的回答。

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

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

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

jQuery是一个库

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

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

最好的开始方式是阅读他们很棒的教程您可以在几个小时内完成这些步骤;但是,如果您想掌握幕后的概念,它们包含了大量可供进一步阅读的参考资料。

是否有任何服务器端注意事项/限制?

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

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

为了描述“范式转变”,我认为一个简短的答案就足够了。

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

jQuery 中,您通常使用选择器来查找元素,然后将它们连接起来:
$('#id .class').click(doStuff);

AngularJS 中,你使用指令直接标记元素,将它们连接起来:
<a ng-click="doStuff()">

AngularJS并不需要(或希望)你找到使用选择元素- AngularJS的之间的主要区别jqLit​​e与全面的jQuery的jqLit​​e不支持选择

所以当人们说“根本不包含 jQuery”时,主要是因为他们不想让你使用选择器;他们希望你学会使用指令来代替。直接,不选择!