如果我有 jQuery 背景,可以“用 AngularJS 思考”吗?[关闭]
- 2024-11-02 21:00:00
- admin 原创
- 38
问题描述:
假设我熟悉使用jQuery开发客户端应用程序,但现在我想开始使用AngularJS。您能描述一下必要的范式转变吗?以下几个问题可能有助于您提出答案:
我该如何以不同的方式构建和设计客户端 Web 应用程序?最大的区别是什么?
我应该停止做/使用什么;我应该开始做/使用什么?
服务器端有任何考虑/限制吗?
我并不是想寻找jQuery
和之间的详细比较AngularJS
。
解决方案 1:
不要设计你的页面,然后使用DOM操作来更改它
在 jQuery 中,您可以设计一个页面,然后将其变为动态页面。这是因为 jQuery 是为增强功能而设计的,并且从这个简单的前提中发展得非常出色。
但在 AngularJS 中,您必须从头开始考虑架构。您不必一开始就想着“我有这部分 DOM,我想让它做 X”,而是必须先考虑要实现的目标,然后设计应用程序,最后设计视图。
不要用 AngularJS 来增强 jQuery
同样,不要一开始就认为 jQuery 可以完成 X、Y 和 Z,所以我会在此基础上添加 AngularJS 来实现模型和控制器。当你刚开始时,这确实很诱人,这就是为什么我总是建议新的 AngularJS 开发人员根本不要使用 jQuery,至少在他们习惯以“Angular 方式”做事之前不要使用 jQuery。
我见过这里和邮件列表中的许多开发人员使用 150 或 200 行代码的 jQuery 插件创建这些复杂的解决方案,然后他们将其与一组令人困惑$apply
和复杂的回调和 s 粘合到 AngularJS 中;但他们最终让它工作了!问题是,在大多数情况下,jQuery 插件可以在 AngularJS 中以一小部分代码重写,突然间一切都变得可以理解和直接。
底线是:解决问题时,首先“用 AngularJS 思考”;如果您想不出解决方案,请咨询社区;如果经过所有这些,仍然没有简单的解决方案,那么请随意使用 jQuery。但不要让 jQuery 成为拐杖,否则您永远无法掌握 AngularJS。
始终从架构的角度思考
首先要知道单页应用是应用,而不是网页。因此,除了像客户端开发人员一样思考之外,我们还需要像服务器端开发人员一样思考。我们必须考虑如何将我们的应用划分为单独的、可扩展的、可测试的组件。
那么你该怎么做呢?你如何“用 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
允许双向数据绑定;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);
});
}
};
});
这种做法有几个问题:
首先,jQuery 根本就没有必要。我们这里所做的一切都不需要 jQuery!
其次,即使我们的页面上已经有 jQuery,也没有理由在这里使用它;我们可以简单地使用,
angular.element
并且我们的组件在没有 jQuery 的项目中仍然可以工作。第三,即使假设此指令需要 jQuery才能工作,jqLite (
angular.element
) 也会始终使用 jQuery(如果已加载)!所以我们不需要使用$
- 我们只需使用angular.element
。第四,与第三点密切相关,jqLite 元素不需要包装
$
-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 都不会被触及,因此重构很容易。您可以随意更改模板,而无需触及指令。而且无论您更改什么,您的测试仍然会通过。
哇喔!
那么,如果指令不仅仅是 jQuery 类函数的集合,那么它们是什么呢?指令实际上是HTML 的扩展。如果 HTML 没有执行您需要执行的操作,您可以编写一个指令来为您执行该操作,然后像使用 HTML 的一部分一样使用它。
换句话说,如果 AngularJS 没有做一些开箱即用的事情,想想团队将如何实现它以适应ngClick
、、ngClass
等。
概括
甚至不要使用 jQuery。甚至不要包含它。它会阻碍你。当你遇到一个你认为你已经知道如何用 jQuery 解决的问题时,在你接触之前$
,试着想想如何在 AngularJS 的范围内解决它。如果你不知道,就问!20 次中有 19 次,最好的方法不需要 jQuery,而尝试用 jQuery 解决它只会给你带来更多的工作。
解决方案 2:
命令式 → 声明式
在 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 和路由,或 ng-controller)。让控制器仅查找/获取视图完成其工作所需的模型数据。使控制器尽可能精简。
原型继承
您可以使用 jQuery 做很多事情,而无需了解 JavaScript 原型继承的工作原理。在开发 AngularJS 应用程序时,如果您对 JavaScript 继承有很好的理解,您将避免一些常见的陷阱。推荐阅读:AngularJS 中范围原型/原型继承的细微差别是什么?
解决方案 3:
AngularJS 与 jQuery
AngularJS 和 jQuery 采用了截然不同的理念。如果你以前用过 jQuery,你可能会对其中的一些差异感到吃惊。Angular 可能会让你生气。
这是很正常的,你应该坚持下去。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,并不反对你使用它。但是,随着你的进步,你会发现你可以使用范围、模板和指令的组合来完成几乎所有的工作,并且你应该尽可能地选择这种工作流程,因为你的代码将更加离散、更加可配置、更加 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模式非常不同,在标准 MVC 模式中,模板仅用于输出。
例如,它与XSLT比与Ruby on Rails更相似。
这是一种彻底的控制反转,需要一些人去适应。
不要再尝试用 JavaScript 来驱动应用程序。让模板驱动应用程序,让 AngularJS 负责将组件连接在一起。这也是 Angular 的方式。
语义 HTML 与语义模型
使用 jQuery,您的 HTML 页面应包含语义上有意义的内容。即使 JavaScript 被关闭(由用户或搜索引擎关闭),您的内容仍可访问。
因为 AngularJS 将您的 HTML 页面视为模板。模板不应该是语义的,因为您的内容通常存储在模型中,而模型最终来自您的 API。AngularJS 使用模型编译您的 DOM 以生成语义网页。
您的 HTML 源不再是语义的,相反,您的 API 和编译的 DOM 是语义的。
在 AngularJS 中,意义存在于模型中,HTML 只是一个模板,仅用于显示。
此时,您可能对SEO和可访问性有各种疑问,这是理所当然的。这里有一些未解决的问题。大多数屏幕阅读器现在都会解析 JavaScript。搜索引擎也可以索引AJAX内容。不过,您需要确保使用的是推送状态 URL,并且拥有一个不错的站点地图。请参阅此处讨论此问题:https://stackoverflow.com/a/23245379/687677
关注点分离 (SOC) 与 MVC
关注点分离(SOC) 是一种在多年的 Web 开发中逐渐发展起来的模式,其原因多种多样,包括 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 的一个错误。
以下拉菜单为例。编写下拉菜单插件时,您可能会想编写点击处理程序,也许是添加向上或向下的 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 级别的粒度。如果我们想逐个处理异常,模板可以轻松实现这一点。
闭包与 $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) 与 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 对象来实例化控制器,并将为我们提供一个。
这使得将东西连接在一起变得非常容易,并且几乎消除了任何走向 spagettification 的趋势。我们有一个扁平的组件列表,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/。希望它能对你有所帮助。
解决方案 4:
您能描述一下必要的范式转变吗?
命令式 vs 声明式
使用jQuery,您可以一步一步地告诉 DOM 需要发生什么。使用AngularJS,您可以描述想要的结果,但不知道如何实现。有关更多信息,请点击此处。此外,请查看 Mark Rajcok 的回答。
我该如何以不同的方式构建和设计客户端 Web 应用程序?
AngularJS 是一个使用MVC模式的完整客户端框架(查看其图形表示)。它非常注重关注点分离。
最大的区别是什么?我应该停止做/使用什么?我应该开始做/使用什么?
jQuery是一个库
AngularJS是一个漂亮的客户端框架,可测试性强,它结合了大量很酷的东西,如 MVC、依赖注入、数据绑定等等。
它注重关注点和测试的分离(单元测试和端到端测试),从而促进测试驱动开发。
开始的最佳方式是阅读他们精彩的教程。你可以在几个小时内完成这些步骤;但是,如果你想掌握幕后的概念,他们提供了大量的参考资料供进一步阅读。
服务器端有任何考虑/限制吗?
您可以在已经使用纯 jQuery 的现有应用程序上使用它。但是,如果您想充分利用 AngularJS 功能,您可以考虑使用RESTful方法对服务器端进行编码。
这样做将允许您利用他们的资源工厂,它创建服务器端 RESTfulAPI的抽象并使服务器端调用(获取、保存、删除等)变得非常容易。
解决方案 5:
为了描述“范式转变”,我想一个简短的回答就足够了。
AngularJS 改变了你查找元素的方式
在jQuery中,通常使用选择器来查找元素,然后将它们连接起来:
$('#id .class').click(doStuff);
在AngularJS中,你可以使用指令直接标记元素并将它们连接起来:
<a ng-click="doStuff()">
AngularJS 不需要(或希望)您使用选择器查找元素 - AngularJS 的jqLite与成熟的jQuery之间的主要区别在于jqLite 不支持选择器。
因此,当人们说“完全不要包含 jQuery”时,主要是因为他们不希望您使用选择器;他们希望您学会使用指令。直接,而不是选择!
解决方案 6:
jQuery
jQuery 使非常长的 JavaScript 命令变得getElementByHerpDerp
更短并且跨浏览器。
AngularJS
AngularJS 允许您创建自己的 HTML 标签/属性,以便可以很好地与动态 Web 应用程序配合使用(因为 HTML 是为静态页面设计的)。
编辑:
说“我有 jQuery 背景,我如何用 AngularJS 思考?”就像说“我有 HTML 背景,我如何用 JavaScript 思考?”你问这个问题的事实表明你很可能不了解这两种资源的基本用途。这就是为什么我选择通过简单地指出根本区别来回答这个问题,而不是通过列表说“AngularJS 使用指令,而 jQuery 使用 CSS 选择器来创建一个执行此操作等的 jQuery 对象……”。这个问题不需要冗长的回答。
jQuery 是一种使在浏览器中编写 JavaScript 代码更加容易的方法。更短、跨浏览器的命令等等。
AngularJS 扩展了 HTML,因此您无需<div>
为了制作应用程序而到处乱放代码。它使 HTML 真正适用于应用程序,而不是其设计初衷,即静态的教育网页。它使用 JavaScript 以迂回的方式实现了这一点,但从根本上讲,它是 HTML 的扩展,而不是 JavaScript。
解决方案 7:
jQuery:您会花很多时间考虑“查询DOM ”以获取 DOM 元素并执行某些操作。
AngularJS:模型就是真相,你总是从那个角度思考。
例如,当您从服务器获取数据并打算以某种格式显示在 DOM 中时,在 jQuery 中,您需要“1. 查找”要在 DOM 中放置此数据的位置,然后“2. 更新/附加”它,方法是创建一个新节点或仅设置其innerHTML。然后,当您想要更新此视图时,您需要“3. 查找”位置并“4. 更新”。在 AngularJS 中,在从服务器获取和格式化数据的相同上下文中完成的查找和更新循环已不复存在。
使用 AngularJS,您拥有模型(您已经习惯的 JavaScript 对象),模型的值会告诉您有关模型(显然)和视图的信息,并且对模型的操作会自动传播到视图,因此您不必考虑它。您会发现自己在 AngularJS 中不再在 DOM 中查找内容。
换句话说,在 jQuery 中,你需要考虑 CSS 选择器,也就是说,具有类或属性等的div
或td
在哪里,以便我可以获取它们的 HTML 或颜色或值,但在 AngularJS 中,你会发现自己是这样想的:我正在处理什么模型,我将模型的值设置为 true。你不必担心反映此值的视图是复选框还是位于元素中td
(在 jQuery 中你经常需要考虑的细节)。
通过 AngularJS 中的 DOM 操作,您会发现自己添加了指令和过滤器,您可以将其视为有效的 HTML 扩展。
在 AngularJS 中您还会遇到一件事:在 jQuery 中,您会多次调用 jQuery 函数,而在 AngularJS 中,AngularJS 会调用您的函数,因此 AngularJS 会“告诉您如何做事”,但这样做的好处是值得的,因此学习 AngularJS 通常意味着学习 AngularJS 想要什么或 AngularJS 要求您呈现函数的方式,然后它会相应地调用它。这是使 AngularJS 成为框架而不是库的原因之一。
解决方案 8:
这些答案很好,但是很冗长。
总结一下我的经验:
控制器和提供者(服务、工厂等)用于修改数据模型,而不是 HTML。
HTML 和指令定义模型的布局和绑定。
如果您需要在控制器之间共享数据,请创建服务或工厂 - 它们是在应用程序之间共享的单例。
如果您需要 HTML 小部件,请创建一个指令。
如果您有一些数据并且现在正尝试更新 HTML...停止!更新模型,并确保您的 HTML 已绑定到该模型。
解决方案 9:
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 作为一门一流的语言来学习!”我是通过艰苦的方式学到上述事实的。
祝你好运。
解决方案 10:
它们是苹果和橘子。你不会想比较它们。它们是两种不同的东西。AngularJs 已经内置了 jQuery lite,它允许你执行基本的 DOM 操作,甚至不需要包含完整的 jQuery 版本。
jQuery 完全是关于 DOM 操作的。它解决了所有跨浏览器的难题,否则您将不得不面对它,但它并不是一个允许您将应用程序划分为组件(如 AngularJS)的框架。
AngularJs 的一个优点是它允许您分离/隔离指令中的 DOM 操作。有内置指令可供您使用,例如 ng-click。您可以创建自己的自定义指令,其中包含所有视图逻辑或 DOM 操作,这样您就不会在应该处理业务逻辑的控制器或服务中混杂 DOM 操作代码。
Angular 将您的应用程序分解为 - 控制器 - 服务 - 视图 - 等等。
还有一件事,那就是指令。它是一个可以附加到任何 DOM 元素的属性,您可以在其中使用 jQuery,而不必担心 jQuery 与 AngularJs 组件发生冲突或扰乱其架构。
我在参加的一次聚会上听说,Angular 的一位创始人说,他们非常努力地分离出 DOM 操作,所以不要试图将它们重新包含进去。
解决方案 11:
收听播客JavaScript Jabber:第 32 集,其中介绍了 AngularJS 的原创者:Misko Hevery 和 Igor Minar。他们谈论了很多从其他 JavaScript 背景(尤其是 jQuery)转到 AngularJS 的感受。
播客中提出的一个观点让我对你的问题有了很大的理解:
MISKO:[...] 我们在 Angular 中仔细考虑过的一个问题是,如何提供大量逃生出口,以便您可以摆脱困境并找到摆脱困境的方法。所以对我们来说,答案就是这个叫做“指令”的东西。有了指令,您基本上就变成了一个普通的 jQuery JavaScript,您可以做任何您想做的事情。
IGOR:所以将指令视为对编译器的指令,当您在模板中遇到这个特定元素或这个 CSS 时,它会告诉编译器,并且您保留这种代码,并且该代码负责 DOM 树中的元素和该元素下的所有内容。
整个剧集的文字记录可在上述链接中找到。
因此,直接回答您的问题:AngularJS 非常固执己见,是一个真正的 MV* 框架。但是,您仍然可以使用 jQuery 在指令中完成您熟悉和喜爱的所有非常酷的事情。这不是“我如何用 jQuery 做我以前做过的事情?”的问题,而是“我如何用我以前在 jQuery 中做过的所有事情来补充 AngularJS?”的问题。
这确实是两种截然不同的心态。
解决方案 12:
我觉得这个问题很有趣,因为我第一次认真接触 JavaScript 编程就是Node.js和 AngularJS。我从未学过 jQuery,我想这是件好事,因为我不需要忘记任何东西。事实上,我积极避免使用 jQuery 解决方案来解决我的问题,而是只寻找“AngularJS 方式”来解决它们。所以,我想我对这个问题的回答基本上可以归结为“像从未学过 jQuery 的人一样思考”,避免任何直接使用 jQuery 的诱惑(显然 AngularJS 在幕后在某种程度上使用了它)。
解决方案 13:
AngularJS 和 jQuery:
除了 JQLite 功能之外,AngularJs 和 JQuery 在各个层面上都完全不同,一旦您开始学习 AngularJs 核心功能(我在下面解释了这一点),您就会看到这一点。
AngularJs 是一个客户端框架,可用于构建独立的客户端应用程序。JQuery 是一个围绕 DOM 运行的客户端库。
AngularJs 酷炫原则 - 如果您想要更改 UI,请从模型数据更改角度考虑。更改数据后,UI 将自行重新渲染。您无需每次都摆弄 DOM,除非几乎不需要,这也应该通过 Angular 指令来处理。
为了回答这个问题,我想分享一下我使用 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 中编写表达式并在运行时对其进行评估。创建自己的指令和服务,并在另一个项目中使用它们,而无需任何额外的努力。
解决方案 14:
作为一名 JavaScript MV* 初学者,并且只关注应用程序架构(而不是服务器/客户端问题),我肯定会推荐以下资源(我很惊讶它还没有被提及): Addy Osmani 编写的《JavaScript 设计模式》 ,作为不同JavaScript 设计模式的介绍。此答案中使用的术语取自上面链接的文档。我不会重复已接受答案中措辞非常好的内容。相反,这个答案链接回了支持 AngularJS(和其他库)的理论背景。
像我一样,您很快就会意识到 AngularJS(或Ember.js、Durandal 和其他 MV* 框架)是一个组装了许多不同 JavaScript 设计模式的复杂框架。
我还发现,在深入研究一个全局框架之前,分别测试(1)原生 JavaScript 代码和(2)较小的库会更容易。这让我能够更好地了解框架解决的关键问题(因为你亲自面对过这个问题)。
例如:
JavaScript 面向对象编程(这是 Google 搜索链接)。它不是一个库,但肯定是任何应用程序编程的先决条件。它教会了我原型、构造函数、单例和装饰器模式的本机实现
jQuery / Underscore作为外观模式(类似 WYSIWYG 操作 DOM)
Prototype.js用于原型/构造函数/混合模式
RequireJS / Curl.js用于模块模式/ AMD
KnockoutJS用于可观察的发布/订阅模式
注意:此列表并不完整,也不是“最佳库”;它们只是我使用的库。这些库还包含更多模式,所提及的只是它们的主要重点或原始意图。如果您觉得此列表中缺少某些内容,请在评论中提及,我很乐意添加它。
解决方案 15:
实际上,如果您正在使用 AngularJS,那么您就不再需要 jQuery。AngularJS 本身具有绑定和指令,对于您使用 jQuery 可以完成的大多数事情来说,它们是一个非常好的“替代品”。
我通常使用 AngularJS 和Cordova开发移动应用程序。我唯一需要的 jQuery 功能就是 Selector。
通过谷歌搜索,我发现有一个独立的 jQuery 选择器模块。它就是 Sizzle。
我决定制作一个小代码片段,帮助我使用 AngularJS 和 jQuery Selector 的强大功能(使用 Sizzle)快速启动一个网站。
我在这里分享了我的代码: https: //github.com/huytd/Sizzular
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件