ChatGPT解决这个技术问题 Extra ChatGPT

“在 AngularJS 中思考”如果我有 jQuery 背景? [关闭]

关闭。这个问题需要更加集中。它目前不接受答案。 7年前关闭。锁定。这个问题及其答案被锁定,因为这个问题离题但具有历史意义。它目前不接受新的答案或交互。

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

如何以不同的方式构建和设计客户端 Web 应用程序?最大的区别是什么?

我应该停止做什么/使用什么;我应该开始做什么/使用什么?

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

我不是在寻找 jQueryAngularJS 之间的详细比较。


J
Josh David Miller

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

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

但是在 AngularJS 中,您必须从头开始考虑您的架构。不要一开始就想“我有这部分 DOM,我想让它做 X”,你必须从你想要完成的事情开始,然后开始设计你的应用程序,最后再设计你的视图。

2. 不要用 AngularJS 扩充 jQuery

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

我在这里和邮件列表中看到许多开发人员使用 150 或 200 行代码的 jQuery 插件创建了这些精心设计的解决方案,然后他们通过一系列令人困惑和令人费解的回调和 $apply 将其粘合到 AngularJS 中;但他们最终让它工作!问题在于,在大多数 情况下,jQuery 插件可以在 AngularJS 中用一小部分代码重写,突然间一切都变得易于理解和直接。

底线是:解决时,首先“在 AngularJS 中思考”;如果您想不出解决方案,请咨询社区;如果毕竟没有简单的解决方案,那么请随意使用 jQuery。但是不要让 jQuery 成为拐杖,否则你永远不会掌握 AngularJS。

3. 始终从架构的角度思考

首先要知道 single-page applications应用程序。它们不是网页。所以我们需要像服务器端开发者一样思考除了像客户端开发者一样思考。我们必须考虑如何将我们的应用程序划分为单独的、可扩展的、可测试的组件。

那么你是怎么做到的呢?您如何“在 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 中,我们有一个单独的模型层,我们可以以任何我们想要的方式管理它,完全独立于视图。这有助于上述数据绑定、维护 separation of concerns 并引入更大的可测试性。其他答案提到了这一点,所以我就这样说吧。

关注点分离

以上所有内容都与这个总体主题相关:将您的担忧分开。您的观点作为应该发生的事情的官方记录(大部分);您的模型代表您的数据;你有一个服务层来执行可重用的任务;您进行 DOM 操作并使用指令增强您的视图;然后你用控制器把它们粘在一起。其他答案中也提到了这一点,我要添加的唯一内容与可测试性有关,我将在下面的另一节中讨论。

依赖注入

dependency injection (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 操作,然后基本上就完成了。但是如果一个指令就像一个“小部件”并且有一个模板,它应该尊重关注点的分离。也就是说,模板 too 应该在很大程度上独立于其在链接和控制器功能中的实现。

AngularJS 带有一整套工具,使这变得非常容易;使用 ngClass 我们可以动态更新类; ngModel 允许双向数据绑定; ngShowngHide 以编程方式显示或隐藏元素;还有更多——包括我们自己写的。换句话说,我们可以在 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);
            });
        }
    };
});

这有几个问题:

首先,jQuery 从来都不是必需的。我们在这里所做的一切都不需要 jQuery!其次,即使我们的页面上已经有 jQuery,也没有理由在这里使用它;我们可以简单地使用 angular.element 并且我们的组件在放入没有 jQuery 的项目时仍然可以工作。第三,即使假设该指令需要 jQuery 才能工作,jqLite (angular.element) 如果已加载,将始终使用 jQuery!所以我们不需要使用 $ - 我们可以使用 angular.element。第四,与第三个密切相关的是 jqLite 元素不需要用 $ 包裹——传递给链接函数的元素已经是一个 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 解决它会为您带来更多的工作。


C
Community

命令式→声明式

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

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

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

关注点分离

jQuery 使用 unobtrusive JavaScript - 行为 (JavaScript) 与结构 (HTML) 分离。

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

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

应用程序设计

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

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

原型继承

在不了解 JavaScript 原型继承如何工作的情况下,您可以使用 jQuery 做很多事情。在开发 AngularJS 应用程序时,如果您对 JavaScript 继承有很好的了解,您将避免一些常见的陷阱。推荐阅读:What are the nuances of scope prototypal / prototypical inheritance in AngularJS?


C
Community

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 的精简版,但仍然可以完美使用。

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 模式非常不同,其中模板仅用于输出。

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

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

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

语义 HTML 与语义模型

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

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

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

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

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

关注点分离 (SOC) 与 MVC

Separation of concerns (SOC) 是一种在多年的网络开发中成长起来的模式,原因有很多,包括 SEO、可访问性和浏览器不兼容。它看起来像这样:

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 的功能。

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

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

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

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

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

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

这是 AngularJS 中的一个错误。

以下拉列表为例。在编写下拉插件时,您可能会想在点击处理程序中编写代码,也许是在向上或向下的人字形中添加一个函数,也许更改展开元素的类,显示隐藏菜单,所有有用的东西。

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

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

在 AngularJS 中,我们编写更小的指令。我们的下拉指令会非常小。它可能保持折叠状态,并提供 fold()、展开() 或 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 值,模板也会更新。

一直都是阿贾克斯

在 jQuery 中进行 Ajax 调用是相当简单的,但它仍然是您可能要三思而后行的事情。需要考虑额外的复杂性,并且需要维护相当多的脚本。

在 AngularJS 中,Ajax 是您默认的首选解决方案,它一直在发生,几乎没有您注意到。您可以使用 ng-include 包含模板。您可以使用最简单的自定义指令应用模板。您可以将 Ajax 调用包装在服务中,并为自己创建一个 GitHub 服务或 Flickr 服务,您可以非常轻松地访问它们。

服务对象与辅助函数

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

AngularJS 为我们提供服务对象。

服务是包含函数和数据的简单对象。它们总是单例,这意味着它们中的一个永远不会超过一个。假设我们要访问 Stack Overflow API,我们可能会编写一个 StackOverflowService 来定义这样做的方法。

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

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

依赖注入 (DI) 与 Instatiation - 也就是去意大利化

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 对象来实例化控制器,并会为我们提供一个。

这使得将事物连接在一起变得非常容易,并且几乎消除了任何导致 spagottification 的倾向。我们有一个简单的组件列表,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/。我希望它会有所帮助。


C
Community

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

命令式与声明式

使用 jQuery 您可以一步一步地告诉 DOM 需要发生什么。使用 AngularJS,您可以描述想要的结果,而不是如何实现。有关此 here 的更多信息。另外,请查看 Mark Rajcok 的答案。

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

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

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

jQuery 是一个库

AngularJS 是一个漂亮的客户端框架,具有高度的可测试性,它结合了大量很酷的东西,例如 MVC、dependency injection、数据绑定等等。

它侧重于 separation of concerns 和测试(unit testing 和端到端测试),这有助于测试驱动的开发。

开始的最佳方式是通过 their awesome tutorial。您可以在几个小时内完成这些步骤;但是,如果您想掌握幕后的概念,它们包含大量参考资料以供进一步阅读。

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

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

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


P
Peter Mortensen

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

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

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

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

AngularJS 不需要(或不希望)您使用选择器查找元素 - AngularJS 的 jqLite 与成熟的 jQuery 之间的主要区别在于 jqLite does not support selectors

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


W
Willem Van Onsem

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。


P
Peter Mortensen

jQuery:你想了很多关于 DOM 元素的“查询 DOM”并做一些事情。

AngularJS:模型就是真理,你总是从那个角度思考。

例如,当您从服务器获取数据时,您打算在 DOM 中以某种格式显示,在 jQuery 中,您需要 '1. FIND' 在 DOM 中您要放置此数据的位置,即 '2.通过创建新节点或仅设置其 innerHTML 来更新/追加它。然后当你想更新这个视图时,你然后'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 成为框架而不是库的原因之一。


P
Peter Mortensen

这些是一些非常好的,但冗长的答案。

总结一下我的经验:

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


P
Peter Mortensen

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 作为一门一流的语言学习!”我以艰难的方式了解到上述事实。

祝你好运。


J
Jin

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

jQuery 是关于 DOM 操作的。它解决了所有跨浏览器的痛苦,否则你将不得不处理它,但它不是一个允许你将应用程序划分为 AngularJS 等组件的框架。

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

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

还有一件事,那就是指令。这是一个你可以附加到任何 DOM 元素的属性,你可以在其中使用 jQuery,而不必担心你的 jQuery 会与 AngularJs 组件冲突或弄乱它的架构。

我从参加的一次聚会上听说,Angular 的一位创始人说他们非常努力地分离出 DOM 操作,所以不要试图将它们重新包含进来。


C
Community

收听播客JavaScript Jabber: Episode #32,其中介绍了 AngularJS 的原始创建者:Misko Hevery &伊戈尔·米纳尔。他们谈论了很多关于从其他 JavaScript 背景(尤其是 jQuery)来到 AngularJS 的感觉。

关于你的问题,播客中提出的观点之一让我很高兴:

MISKO:[...] 我们在 Angular 中几乎没有考虑过的一件事是,我们如何提供大量的逃生舱口,以便您可以脱身并从根本上找出解决办法。所以对我们来说,答案就是这个叫做“指令”的东西。有了指令,你基本上变成了一个普通的小 jQuery JavaScript,你可以做任何你想做的事情。 IGOR:因此,可以将指令视为对编译器的指令,当你在模板中遇到这个特定元素或这个 CSS 时,它会告诉它,你保留这种代码,而该代码负责元素以及该元素下的所有内容在 DOM 树中。

整个剧集的成绩单可在上面提供的链接中找到。

所以,直接回答你的问题:AngularJS - 非常 - 自以为是,是一个真正的 MV* 框架。但是,您仍然可以在指令中使用 jQuery 完成所有您知道和喜爱的非常酷的事情。这不是“我如何做我以前在 jQuery 中做的事情?”的问题。就像“我如何用我以前在 jQuery 中做的所有东西来补充 AngularJS?”的问题一样多。

这真的是两种截然不同的心态。


P
Peter Mortensen

我觉得这个问题很有趣,因为我第一次认真接触 JavaScript 编程是 Node.js 和 AngularJS。我从来没有学过 jQuery,我想这是一件好事,因为我不需要忘记任何东西。事实上,我积极避免使用 jQuery 解决我的问题,而是只寻找一种“AngularJS 方式”来解决它们。所以,我想我对这个问题的回答基本上可以归结为“像从未学过 jQuery 的人一样思考”,并避免任何直接合并 jQuery 的诱惑(显然 AngularJS 在某种程度上在幕后使用它)。


S
Sanjeev Singh

AngularJS 和 jQuery:

除了 JQLite 功能之外,AngularJs 和 JQuery 在每个级别上都完全不同,一旦您开始学习 AngularJs 核心功能(我在下面解释),您就会看到它。

AngularJs 是一个客户端框架,提供构建独立的客户端应用程序。 JQuery 是一个围绕 DOM 运行的客户端库。

AngularJs Cool Principle - 如果您想对 UI 进行一些更改,请从模型数据更改的角度思考。更改您的数据,UI 将重新呈现自己。你不需要每次都在 DOM 周围玩耍,除非并且直到它几乎不需要并且也应该通过 Angular Directives 来处理。

为了回答这个问题,我想分享我在第一个使用 AngularJS 的企业应用程序上的经验。这些是 Angular 提供的最棒的特性,我们开始改变我们的 jQuery 思维方式,我们将 Angular 作为一个框架而不是库。

双向数据绑定非常棒:我有一个包含所有功能 UPDATE、DELTE、INSERT 的网格。我有一个使用 ng-repeat 绑定网格模型的数据对象。您只需要为删除和插入编写一行简单的 JavaScript 代码就可以了。随着网格模型的即时更改,网格会自动更新。更新功能是实时的,没有代码。你觉得不可思议!!!

可重用指令是超级的:在一个地方编写指令并在整个应用程序中使用它。我的天啊!!!我将这些指令用于分页、正则表达式、验证等。这真的很酷!

路由很强大:如何使用它取决于您的实现,但它只需要很少的代码行来路由请求以指定 HTML 和控制器 (JavaScript)

控制器很棒:控制器负责处理自己的 HTML,但是这种分离对于通用功能也很有效。如果您想在主 HTML 上单击按钮调用相同的函数,只需在每个控制器中编写相同的函数名称并编写单独的代码。

插件:还有许多其他类似的功能,例如在您的应用中显示叠加层。您无需为它编写代码,只需使用可用作 wc-overlay 的覆盖插件,这将自动处理所有 XMLHttpRequest (XHR) 请求。

RESTful 架构的理想选择: 作为一个完整的框架,AngularJS 非常适合与 RESTful 架构一起使用。调用 REST CRUD API 非常简单,而且

服务:使用服务编写通用代码,在控制器中编写更少的代码。服务可用于在控制器之间共享通用功能。

可扩展性:Angular 使用 Angular 指令扩展了 HTML 指令。在 html 中编写表达式并在运行时评估它们。创建您自己的指令和服务,并在另一个项目中使用它们而无需任何额外的努力。


P
Peter Mortensen

作为一名 JavaScript MV* 初学者并且只关注应用程序架构(而不是服务器/客户端问题),我当然会推荐以下资源(我很惊讶尚未提及):JavaScript Design Patterns,作者:Addy Osmani ,作为对不同 JavaScript 设计模式的介绍。此答案中使用的术语取自上面的链接文档。我不会重复在接受的答案中措辞非常好的内容。相反,这个答案链接回支持 AngularJS(和其他库)的 理论背景

像我一样,您很快就会意识到 AngularJS(或 Ember.js、Durandal 和其他 MV* 框架)是一个组合许多不同 JavaScript 设计模式的复杂框架。

我还发现,在深入研究一个全局框架之前,分别为每一种模式测试 (1) 本机 JavaScript 代码和 (2) 较小的库。这使我能够更好地了解框架解决了哪些关键问题(因为您亲自面临这个问题)。

例如:

JavaScript 面向对象编程(这是一个谷歌搜索链接)。它不是一个库,但肯定是任何应用程序编程的先决条件。它教会了我原型、构造函数、单例和装饰器模式的原生实现

jQuery/ Underscore 用于外观模式(就像 WYSIWYG 用于操作 DOM)

Prototype.js 用于原型/构造函数/混合模式

RequireJS/ Curl.js 用于模块模式/ AMD

用于可观察的发布/订阅模式的 KnockoutJS

注意:此列表不完整,也不是“最佳库”;它们恰好是我使用的库。这些库还包含更多模式,提到的只是它们的主要关注点或原始意图。如果您觉得此列表中缺少某些内容,请在评论中提及,我很乐意添加。


P
Peter Mortensen

实际上,如果你使用 AngularJS,你就不再需要 jQuery。 AngularJS 本身具有绑定和指令,对于您可以用 jQuery 做的大多数事情,这是一个非常好的“替代品”。

我通常使用 AngularJS 和 Cordova 开发移动应用程序。我需要的唯一来自 jQuery 的东西是选择器。

通过谷歌搜索,我看到那里有一个独立的 jQuery 选择器模块。是嘶嘶声。

我决定制作一个小代码片段,帮助我使用 AngularJS 和 jQuery Selector(使用 Sizzle)快速启动一个网站。

我在这里分享了我的代码:https://github.com/huytd/Sizzular