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

假设我熟悉使用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 插件创建这些精心设计的解决方案,然后将它们粘贴到 AngularJS 中,并使用一系列令人困惑和费解的回调和$apply ;但是他们最终使它起作用了!问题在于,在大多数情况下,jQuery 插件可以用少量代码在 AngularJS 中重写,从而突然之间所有内容都变得可理解和直接。

底线是:解决时,首先 “在 AngularJS 中思考”;如果您想不出解决方案,请询问社区;如果毕竟,有没有简单的解决方案, 然后随意达到了 jQuery。但是不要让 jQuery 成为拐杖,否则您将永远无法掌握 AngularJS。

3. 总是从架构上思考

首先知道单页应用程序application 。它们不是网页。因此,除了像客户端开发人员那样思考之外 ,我们还需要像服务器端开发人员那样思考。我们必须考虑如何将应用程序划分为各个可扩展的可测试组件。

那你该怎么做呢?您如何 “在 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允许双向数据绑定; ngShowngHide编程方式显示或隐藏元素;还有更多 - 包括我们自己编写的内容。换句话说,我们可以在没有 DOM 操作的情况下进行各种出色的工作。 DOM 操作越少,指令的测试就越容易,指令的样式就越容易,将来就越容易更改,它们的可重用性和可分发性就越高。

我看到很多使用指令作为投掷 jQuery 的地方的 AngularJS 新手。换句话说,他们认为 “由于我无法在控制器中进行 DOM 操作,因此我会将代码放入指令中”。虽然这肯定好得多,但通常仍然是错误的

想想我们在第 3 节中编写的记录器。即使将其放入指令中,我们仍然希望以 “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 元件不必在被包裹$ - 的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 解决它会为您带来更多工作。

命令式→声明式

在 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 和 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(这是一个局部变量存储区)以及一些其他有用的位。这些属性可能包含可由指令解释的表达式,该表达式指示指令的渲染方式以及何时重新绘制自身。

然后,指令又可以引入其他 Angular 组件,例如控制器,服务等。编译器的底部是一个完整的 Web 应用程序,已连接好并可以使用。

这意味着 Angular 是模板驱动的 。您的模板将驱动 JavaScript,而不是相反。这是角色的根本颠倒,这与过去十年左右我们一直在编写的不引人注目的 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模式非常不同,在标准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内容。不过,您将需要确保使用的是推送状态网址,并且您拥有一个不错的站点地图。请参阅此处以获取有关该问题的讨论: 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 不在相同规模的相对两端,它们在完全不同的轴上。在 AngularJS 上下文中,SOC 没有意义。您必须忘记它并继续前进。

如果像我一样,经历了浏览器大战,您可能会发现这个想法令人反感。克服它,这是值得的,我保证。

插件与指令

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

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

例如,如果要在页面上使用轮播,则可以定义无序的图形列表,也许将其包装在 nav 元素中。然后,您可以编写一些 jQuery 以选择页面上的列表,然后将其重新设置为具有超时功能的图库,以进行滑动动画处理。

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

AngularJS 的方法是扩展原生 HTML 的功能。您应该编写类似于 HTML 的 HTML,并使用自定义属性和元素进行扩展。

如果您想要轮播,只需使用<carousel />元素,然后定义指令以引入模板,并使该吸盘工作。

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

jQuery 的趋势是编写很棒的大型插件,例如 lightbox,然后我们通过传入大量值和选项进行配置。

这是 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,默认情况下,该 $ 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服务,您可以轻松地访问它们。

服务对象与助手功能

在 jQuery 中,如果我们想完成一个小的非 dom 相关的任务,例如从 API 中提取一个提要,我们可以在闭包中编写一个小函数来做到这一点。那是一个有效的解决方案,但是如果我们想经常访问该提要怎么办?如果我们想在另一个应用程序中重用该代码怎么办?

AngularJS 给了我们服务对象。

服务是包含功能和数据的简单对象。它们始终是单例,这意味着不能超过一个。假设我们要访问 Stack Overflow API,我们可以编写一个StackOverflowService来定义执行此操作的方法。

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

服务对象是独立的 AngularJS 组件,我们可以根据需要使用和重用它们。它们是包含功能和数据的简单 JSON 对象。它们始终是单例,因此,如果将数据存储在某个位置的某个服务上,则可以通过请求同一服务将数据从其他位置获取。

依赖注入 (DI)与状态 - aka despaghettification

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 模块和 voila,您可以在新应用程序中访问所有与 Flickr 相关的功能。

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

总结一下

AngularJS 和 jQuery 不是敌人。可以在 AngularJS 中很好地使用 jQuery。如果您很好地使用了 AngularJS(模板,数据绑定,$ scope,指令等),您会发现所需的 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 不需要(也不希望)使用选择器来查找元素-AngularJSjqLite与成熟的jQuery之间的主要区别是jqLite 不支持选择器

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

jQuery 的

jQuery 将较短的 JavaScript 命令(如getElementByHerpDerp缩短并跨浏览器。

AngularJS

AngularJS 允许您创建自己的 HTML 标记 / 属性,这些标记 / 属性可以与动态 Web 应用程序一起很好地工作(因为 HTML 是为静态页面设计的)。

编辑:

说 “我有 jQuery 背景,在 AngularJS 中如何看待?” 就像说 “我有 HTML 背景,我如何看待 JavaScript?” 您所提问题的事实表明您很可能不了解这两种资源的基本目的。这就是为什么我选择通过简单地指出根本差异而不是在列表中遍历 “AngularJS 使用指令,而 jQuery 使用 CSS 选择器来制作执行此操作的 jQuery 对象...” 来回答这个问题的原因。 。这个问题不需要冗长的答案。

jQuery 是一种使浏览器中的 JavaScript 编程更容易的方法。更短的跨浏览器命令等

AngularJS 扩展了 HTML,因此您不必在整个地方放置<div>即可创建应用程序。它使 HTML 实际上可用于应用程序,而不是为静态的教育性网页设计的。它使用 JavaScript 以一种 about 回的方式来完成此任务,但从根本上说,它是 HTML 的扩展,而不是 JavaScript。

jQuery:您对 DOM 元素的 “查询DOM ” 进行了很多思考并做了一些事情。

AngularJS:模型就是事实,您总是从那个角度思考。

例如,当您从服务器获取要以某种格式显示在 DOM 中的数据时,在 jQuery 中,您需要将其设置为 '1。在 DOM 中您要放置此数据的位置找到 “2”。通过创建新节点或仅设置其innerHTML来在 UPDATE / APPEND 中进行修改。然后,当您想更新此视图时,请输入 '3。查找位置和 “4。更新”。 AngularJS 中没有在从服务器获取和格式化数据的同一上下文中完成所有查找和更新的循环。

使用 AngularJS,您可以拥有模型(您已经习惯了 JavaScript 对象),并且模型的值(显然)告诉您有关模型和视图的信息,并且对模型的操作会自动传播到视图,因此您无需不必考虑。您将发现自己在 AngularJS 中不再在 DOM 中找到东西。

换句话说,在 jQuery 中,您需要考虑 CSS 选择器,即具有类或属性等的divtd在哪里,以便获取它们的 HTML 或颜色或值,但是在 AngularJS,您会发现自己是这样想的:我要处理的模型是什么,我将模型的值设置为 true。您不必担心反映此值的视图是复选框还是驻留在td元素中(详细信息,您通常需要在 jQuery 中进行思考)。

通过 AngularJS 中的 DOM 操作,您发现自己添加了指令和过滤器,可以将它们视为有效的 HTML 扩展。

您将在 AngularJS 中体验到另一件事:在 jQuery 中,您经常调用 jQuery 函数;在 AngularJS 中,AngularJS 将调用您的函数,因此 AngularJS 将 “告诉您如何做事”,但是好处是值得的,因此学习 AngularJS 通常意味着学习 AngularJS 想要什么或 AngularJS 要求您呈现函数的方式,它将相应地调用它。这是使 AngularJS 成为框架而不是库的原因之一。

这些是很好的答案,但答案冗长。

总结一下我的经验:

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

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 作为一流的语言来学习!” 我很难学到上述事实。

祝好运。

他们是苹果和橘子。您不想比较它们。他们是两回事。 AngularJs 已经内置了 jQuery lite,它允许您执行基本的 DOM 操作,甚至不包括完整的 jQuery 版本。

jQuery 全部涉及 DOM 操作。它解决了所有跨浏览器的烦恼,否则您将不得不面对,但它不是一个框架,它不允许您将应用程序划分为 AngularJS 之类的组件。

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

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

还有一件事,那就是指令。它是一个属性,可以附加到任何 DOM 元素,并且可以在其中使用 jQuery,而不必担心 jQuery 与 AngularJs 组件发生冲突或弄乱其体系结构。

我从参加的聚会中听说,Angular 的一位创始人表示,他们非常努力地将 DOM 操作分离出来,所以不要尝试将其重新加入。