如果我有jQuery背景,”在AngularJS中思考”?
假设我熟悉在jQuery中开发客户端应用程序,但现在我想开始使用AngularJS.你能描述一下必要的范式转变吗?以下是一些可能有助于您确定答案的问题:
- 如何以不同方式构建和设计客户端Web应用程序?最大的区别是什么?
- 我应该停止做什么/使用什么; 我应该开始做什么/使用呢?
- 是否存在任何服务器端注意事项/限制?
我不是在寻找jQuery
和之间的详细比较AngularJS
.
回答
1.不要设计页面,然后使用DOM操作进行更改
在jQuery中,您设计了一个页面,然后将其设置为动态.这是因为jQuery是为增强而设计的,并且从这个简单的前提中获得了令人难以置信的增长.
但是在AngularJS中,您必须从头开始考虑您的架构.我不是首先考虑"我有这块DOM而我想让它做X",而是从你想要完成的事情开始,然后去设计你的应用程序,然后最后去设计你的视图.
2.不要使用AngularJS扩充jQuery
同样,不要从jQuery做X,Y和Z的想法开始,所以我只是在模型和控制器的基础上添加AngularJS.这是真的很诱人,当你刚刚起步的,这就是为什么我总是建议新AngularJS开发商不使用jQuery可言,至少直到他们习惯做的事情"角之路".
我在这里和邮件列表上看到很多开发人员使用150或200行代码的jQuery插件创建这些精心设计的解决方案,然后他们将这些代码粘合到AngularJS中,其中包含一系列$apply
令人困惑和错综复杂的回调.但他们最终得到它的工作!问题是,在大多数情况下,jQuery插件可以在AngularJS中以一小部分代码重写,突然之间一切都变得易于理解和直接.
最重要的是:解决问题时,首先要"在AngularJS中思考"; 如果你想不出解决方案,请向社区提问; 如果完全没有简单的解决方案,那么随时可以找到jQuery.但是不要让jQuery成为拐杖或者你永远不会掌握AngularJS.
3.始终从架构的角度思考
首先要知道单页应用程序是应用程序.他们不是网页.因此,我们需要这样想一个服务器端的开发者除了想着像客户端开发.我们必须考虑如何将我们的应用程序划分为单独的,可扩展的,可测试的组件.
那么接下来怎么做呢?你如何"在AngularJS中思考"?以下是一些与jQuery形成对比的一般原则.
该观点是"官方记录"
在jQuery中,我们以编程方式更改视图.我们可以将下拉菜单定义为ul
如此:
<ul>
<li>
<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 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>
</ul>
除了混合问题,我们也有同样的问题,表明我之前提到的意图.但更重要的是,我们必须手动引用和更新DOM节点.如果我们想要删除日志条目,我们也必须针对DOM进行编码.我们如何测试除DOM之外的逻辑?如果我们想改变演示文稿怎么办?
这有点凌乱,有点脆弱.但在AngularJS中,我们可以这样做:
$http( '/myEndpoint.json' ).then( function ( response ) {
$scope.log.push( { msg: 'Data Received!' } );
});
我们的观点可能如下所示:
<ul>
<li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>
但就此而言,我们的观点可能如下所示:
<div>
<div 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="https://qa.1r1g.com/hello" when-active>Hello</a>
好的,现在我们可以为不存在的when-active
指令编写一个测试:
it( 'should add "active" when the route changes', inject(function() {
var elm = $compile( '<a href="https://qa.1r1g.com/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操作越少,测试指令就越容易,它们的样式就越容易,将来它们就越容易改变,它们的可重用性和可分发性就越高.
我看到许多开发人员使用指令作为抛出一堆jQuery的地方的AngularJS的新手.换句话说,他们认为"因为我不能在控制器中进行DOM操作,所以我将把代码放在指令中".虽然这确实好得多,但它通常仍然是错误的.
想想我们在第3节中编写的记录器.即使我们将其放在指令中,我们仍然希望将其作为"Angular Way".它仍然不需要任何DOM操作!有很多时候需要DOM操作,但它比你想象的要少得多!在应用程序中的任何位置进行DOM操作之前,请问自己是否真的需要.可能有更好的方法.
这是一个快速示例,显示了我最常见的模式.我们想要一个可切换的按钮.(注意:这个例子有点人为,并且表示更复杂的案例,以完全相同的方式解决.)
.directive( 'myDirective', function () {
return {
template: '<a>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 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不需要它做某事,你可以编写一个指令来为你完成,然后就像使用它一样使用它.
换句话说,如果AngularJS没做什么开箱,认为球队如何完成它与合身的时候ngClick
,ngClass
等.
摘要
甚至不使用jQuery.甚至不包括它.它会阻止你.当你遇到一个问题,你认为你已经知道如何在jQuery中解决,在你达到之前$
,试着考虑如何在AngularJS的范围内做到这一点.如果你不知道,请问!20次中有19次,最好的方法是不需要jQuery并尝试使用jQuery解决它,为您提供更多的工作.
- 我认为在角度应用程序中使用JQuery是一个重要的用例,因为已经编写了所有现有的JQuery插件.我不是在jQuery中重写FancyBox来保留一个纯粹的Angular应用程序.
- @taudep我认为我们不同意你的想法.大多数jQuery插件都可以廉价地在AngularJS中重写,在这种情况下我们应该这样做.对于没有等价物的复杂事物,那就去吧.引用第2节:'底线是:解决时,首先"在AngularJS中思考"; 如果你想不出解决方案,请向社区提问;**如果毕竟没有简单的解决方案,那么随时可以找到jQuery**.但是不要让jQuery成为拐杖或者你永远不会掌握AngularJS.[强调补充]
- 一个中文翻译成这个伟大的答案,希望对你有帮助 http://hanzheng.github.io/tech/angularjs/2013/10/28/translate-how-do-i-in-angularjs-if-i-have-a-jquery-background.html
- @Benno"没有DOM操作"的意思是指令中的代码不直接执行DOM操作.该代码对DOM一无所知,它只是修改模型中的js变量.是的,最终结果是DOM被修改,但那是因为在我们编写的代码之外,有一些绑定会对我们的变量做出反应,但是在指令中我们对它一无所知,在DOM操作和商业逻辑.在您的jQuery示例中,您通过说"将此文本附加到此元素"直接更改DOM
- @trusktr如果开发人员在AngularJS应用程序中使用jQuery设置了输入元素的值,那么她将犯下一个**严重错误.我能想到的唯一一个例外是现有的jQuery插件,它太难以移植而无法自动更改输入,在这种情况下,挂钩回调或设置手表无论如何都要将更改与应用程序内联.
- 有人在programming.com上发布了这个:http://programming.com/tutorial/3920820380494726340/how-to-think-in-angularjs-if-you-have-a-jquery-background
- 我将这两者作为等价物进行比较,并意识到当我实际完成Angular教程时我的错误.正如许多人在这里所提到的...... jQuery是一个库/工具,而Angular是一个应用程序的框架,它可以完成所有工作 - 模型,视图,控制器和路由.因此,Angular不仅仅用于数据绑定,还有更多.如果您已经有现有的控制器和路由,那么您不打算替换它,它确实需要Angular提供的很大一部分功能.
势在必行→陈述性
在jQuery中,选择器用于查找DOM元素,然后将事件处理程序绑定/注册到它们.当事件触发时,执行(命令性)代码以更新/更改DOM.
在AngularJS中,您需要考虑视图而不是DOM元素.视图是包含AngularJS 指令的(声明性)HTML .指令为我们幕后设置了事件处理程序,并为我们提供了动态数据绑定.选择器很少使用,因此对ID(以及某些类型的类)的需求大大减少.视图与模型相关联(通过范围).视图是模型的投影.事件更改模型(即数据,范围属性)以及投影这些模型的视图会"自动"更新.
在AngularJS中,考虑模型,而不是jQuery选择的DOM元素来保存您的数据.将视图视为这些模型的投影,而不是注册回调来操纵用户看到的内容.
关注点分离
jQuery使用不引人注目的JavaScript - 行为(JavaScript)与结构(HTML)分离.
AngularJS使用控制器和指令(每个控制器和指令可以有自己的控制器和/或编译和链接函数)来从视图/结构(HTML)中删除行为.Angular还提供服务和过滤器,以帮助分离/组织您的应用程序.
另请参见/sf/answers/1004256991/
应用设计
设计AngularJS应用程序的一种方法:
- 想想你的模特.为这些模型创建服务或您自己的JavaScript对象.
- 想想你想要如何展示你的模特 - 你的观点.为每个视图创建HTML模板,使用必要的指令来获取动态数据绑定.
- 将控制器连接到每个视图(使用ng-view和routing,或ng-controller).让控制器只查找/获取视图执行其工作所需的任何模型数据.使控制器尽可能薄.
原型继承
你可以在不知道JavaScript原型继承如何工作的情况下使用jQuery做很多事情.在开发AngularJS应用程序时,如果您对JavaScript继承有很好的理解,就可以避免一些常见的陷阱.推荐阅读:AngularJS中范围原型/原型继承的细微差别是什么?
- @rajkamal,一个DOM元素(显然)是一个单独的元素,而jQuery通常是我们选择/目标/操作的元素.角度视图是相关DOM元素的集合/模板:菜单视图,页眉视图,页脚视图,右侧栏视图,配置文件视图,可能是多个主要内容视图(可通过ng-view切换).基本上,您希望将页面分成不同的视图.每个视图都有自己的关联控制器.每个视图都会预测模型的一部分.
- 那么在`on`的回调中执行什么样的代码呢?势在必行.
- 这种命令与声明只是一个抽象问题.最后,所有声明性代码(要做什么)都是由开发人员在较低抽象级别的子例程中,由框架或编译器/解释器强制实现(如何执行).一般来说,"jQuery是必要的"是一个非常奇怪的陈述,特别是考虑到它实际上提供了关于"手动"DOM操作的更多声明性API.
- jQuery是*NOT*命令式.```on```和```when```是高阶函数,在jQuery集合对象的成员上运行.
AngularJS与jQuery
AngularJS和jQuery采用了截然不同的意识形态.如果您来自jQuery,您可能会发现一些令人惊讶的差异.Angular可能会让你生气.
这是正常的,你应该推进.Angular值得.
差异很大(TLDR)
jQuery为您提供了一个工具包,用于选择DOM的任意位并对其进行临时更改.你几乎可以做任何你喜欢的事情.
相反,AngularJS为您提供了一个编译器.
What this means is that AngularJS reads your entire DOM from top to bottom and treats it as code, literally as instructions to the compiler. As it traverses the DOM, It looks for specific directives (compiler directives) that tell the AngularJS compiler how to behave and what to do. Directives are little objects full of JavaScript which can match against attributes, tags, classes or even comments.
When the Angular compiler determines that a piece of the DOM matches a particular directive, it calls the directive function, passing it the DOM element, any attributes, the current $scope (which is a local variable store), and some other useful bits. These attributes may contain expressions which can be interpreted by the Directive, and which tell it how to render, and when it should redraw itself.
Directives can then in turn pull in additional Angular components such as controllers, services, etc. What comes out the bottom of the compiler is a fully formed web application, wired up and ready to go.
This means that Angular is Template Driven. Your template drives the JavaScript, not the other way around. This is a radical reversal of roles, and the complete opposite of the unobtrusive JavaScript we have been writing for the last 10 years or so. This can take some getting used to.
If this sounds like it might be over-prescriptive and limiting, nothing could be farther from the truth. Because AngularJS treats your HTML as code, you get HTML level granularity in your web application. Everything is possible, and most things are surprisingly easy once you make a few conceptual leaps.
Let's get down to the nitty gritty.
First up, Angular doesn't replace jQuery
Angular and jQuery do different things. AngularJS gives you a set of tools to produce web applications. jQuery mainly gives you tools for modifying the DOM. If jQuery is present on your page, AngularJS will use it automatically. If it isn't, AngularJS ships with jQuery Lite, which is a cut down, but still perfectly usable version of jQuery.
Misko likes jQuery and doesn't object to you using it. However you will find as you advance that you can get a pretty much all of your work done using a combination of scope, templates and directives, and you should prefer this workflow where possible because your code will be more discrete, more configurable, and more Angular.
如果你使用jQuery,你不应该把它洒到这个地方.AngularJS中DOM操作的正确位置是在一个指令中.稍后会详细介绍.
使用选择器与声明模板的不显眼的JavaScript
jQuery通常不引人注意地应用.您的JavaScript代码链接在标题(或页脚)中,这是它唯一提到的地方.我们使用选择器来挑选页面的位并编写插件来修改这些部分.
JavaScript处于控制之中.HTML具有完全独立的存在.即使没有JavaScript,您的HTML仍然是语义.Onclick属性是非常糟糕的做法.
One of the first things your will notice about AngularJS is that custom attributes are everywhere. Your HTML will be littered with ng attributes, which are essentially onClick attributes on steroids. These are directives (compiler directives), and are one of the main ways in which the template is hooked to the model.
When you first see this you might be tempted to write AngularJS off as old school intrusive JavaScript (like I did at first). In fact, AngularJS does not play by those rules. In AngularJS, your HTML5 is a template. It is compiled by AngularJS to produce your web page.
This is the first big difference. To jQuery, your web page is a DOM to be manipulated. To AngularJS, your HTML is code to be compiled. AngularJS reads in your whole web page and literally compiles it into a new web page using its built in compiler.
Your template should be declarative; its meaning should be clear simply by reading it. We use custom attributes with meaningful names. We make up new HTML elements, again with meaningful names. A designer with minimal HTML knowledge and no coding skill can read your AngularJS template and understand what it is doing. He or she can make modifications. This is the Angular way.
The template is in the driving seat.
在开始使用AngularJS并运行教程时,我问自己的第一个问题是"我的代码在哪里?" .我没有写过JavaScript,但我有这些行为.答案很明显.因为AngularJS编译DOM,所以AngularJS将您的HTML视为代码.对于许多简单的情况,只需编写一个模板就可以了,让AngularJS将它编译成一个应用程序.
您的模板可以驱动您的应用 它被视为DSL.您编写AngularJS组件,AngularJS将负责将它们拉入并根据模板的结构在适当的时间提供它们.这与标准MVC模式非常不同,其中模板仅用于输出.
It's more similar to XSLT than Ruby on Rails for example.
This is a radical inversion of control that takes some getting used to.
Stop trying to drive your application from your JavaScript. Let the template drive the application, and let AngularJS take care of wiring the components together. This also is the Angular way.
Semantic HTML vs. Semantic Models
With jQuery your HTML page should contain semantic meaningful content. If the JavaScript is turned off (by a user or search engine) your content remains accessible.
Because AngularJS treats your HTML page as a template. The template is not supposed to be semantic as your content is typically stored in your model which ultimately comes from your API. AngularJS compiles your DOM with the model to produce a semantic web page.
Your HTML source is no longer semantic, instead, your API and compiled DOM are semantic.
In AngularJS, meaning lives in the model, the HTML is just a template, for display only.
At this point you likely have all sorts of questions concerning SEO and accessibility, and rightly so. There are open issues here. Most screen readers will now parse JavaScript. Search engines can also index AJAXed content. Nevertheless, you will want to make sure you are using pushstate URLs and you have a decent sitemap. See here for a discussion of the issue: /sf/answers/1627176561/
Separation of concerns (SOC) vs. MVC
Separation of concerns (SOC) is a pattern that grew up over many years of web development for a variety of reasons including SEO, accessibility and browser incompatibility. It looks like this:
- HTML - Semantic meaning. The HTML should stand alone.
- CSS - Styling, without the CSS the page is still readable.
- JavaScript - Behaviour, without the script the content remains.
Again, AngularJS does not play by their rules. In a stroke, AngularJS does away with a decade of received wisdom and instead implements an MVC pattern in which the template is no longer semantic, not even a little bit.
It looks like this:
- Model - your models contains your semantic data. Models are usually JSON objects. Models exist as attributes of an object called $scope. You can also store handy utility functions on $scope which your templates can then access.
- View - Your views are written in HTML. The view is usually not semantic because your data lives in the model.
- Controller - Your controller is a JavaScript function which hooks the view to the model. Its function is to initialise $scope. Depending on your application, you may or may not need to create a controller. You can have many controllers on a page.
MVC and SOC are not on opposite ends of the same scale, they are on completely different axes. SOC makes no sense in an AngularJS context. You have to forget it and move on.
If, like me, you lived through the browser wars, you might find this idea quite offensive. Get over it, it'll be worth it, I promise.
Plugins vs. Directives
Plugins extend jQuery. AngularJS Directives extend the capabilities of your browser.
In jQuery we define plugins by adding functions to the jQuery.prototype. We then hook these into the DOM by selecting elements and calling the plugin on the result. The idea is to extend the capabilities of jQuery.
For example, if you want a carousel on your page, you might define an unordered list of figures, perhaps wrapped in a nav element. You might then write some jQuery to select the list on the page and restyle it as a gallery with timeouts to do the sliding animation.
In AngularJS, we define directives. A directive is a function which returns a JSON object. This object tells AngularJS what DOM elements to look for, and what changes to make to them. Directives are hooked in to the template using either attributes or elements, which you invent. The idea is to extend the capabilities of HTML with new attributes and elements.
The AngularJS way is to extend the capabilities of native looking HTML. You should write HTML that looks like HTML, extended with custom attributes and elements.
If you want a carousel, just use a <carousel />
element, then define a directive to pull in a template, and make that sucker work.
Lots of small directives vs. big plugins with configuration switches
The tendency with jQuery is to write great big plugins like lightbox which we then configure by passing in numerous values and options.
This is a mistake in AngularJS.
Take the example of a dropdown. When writing a dropdown plugin you might be tempted to code in click handlers, perhaps a function to add in a chevron which is either up or down, perhaps change the class of the unfolded element, show hide the menu, all helpful stuff.
Until you want to make a small change.
Say you have a menu that you want to unfold on hover. Well now we have a problem. Our plugin has wired in our click handler for us, we're going to need to add a configuration option to make it behave differently in this specific case.
In AngularJS we write smaller directives. Our dropdown directive would be ridiculously small. It might maintain the folded state, and provide methods to fold(), unfold() or toggle(). These methods would simply update $scope.menu.visible which is a boolean holding the state.
Now in our template we can wire this up:
<a ng-click="toggle()">Menu</a>
<ul ng-show="menu.visible">
...
</ul>
Need to update on mouseover?
<a ng-mouseenter="unfold()" ng-mouseleave="fold()">Menu</a>
<ul ng-show="menu.visible">
...
</ul>
The template drives the application so we get HTML level granularity. If we want to make case by case exceptions, the template makes this easy.
Closure vs. $scope
JQuery plugins are created in a closure. Privacy is maintained within that closure. It's up to you to maintain your scope chain within that closure. You only really have access to the set of DOM nodes passed in to the plugin by jQuery, plus any local variables defined in the closure and any globals you have defined. This means that plugins are quite self contained. This is a good thing, but can get restrictive when creating a whole application. Trying to pass data between sections of a dynamic page becomes a chore.
AngularJS has $scope objects. These are special objects created and maintained by AngularJS in which you store your model. Certain directives will spawn a new $scope, which by default inherits from its wrapping $scope using JavaScript prototypical inheritance. The $scope object is accessible in the controller and the view.
This is the clever part. Because the structure of $scope inheritance roughly follows the structure of the DOM, elements have access to their own scope, and any containing scopes seamlessly, all the way up to the global $scope (which is not the same as the global scope).
This makes it much easier to pass data around, and to store data at an appropriate level. If a dropdown is unfolded, only the dropdown $scope needs to know about it. If the user updates their preferences, you might want to update the global $scope, and any nested scopes listening to the user preferences would automatically be alerted.
This might sound complicated, in fact, once you relax into it, it's like flying. You don't need to create the $scope object, AngularJS instantiates and configures it for you, correctly and appropriately based on your template hierarchy. AngularJS then makes it available to your component using the magic of dependency injection (more on this later).
Manual DOM changes vs. Data Binding
In jQuery you make all your DOM changes by hand. You construct new DOM elements programatically. If you have a JSON array and you want to put it to the DOM, you must write a function to generate the HTML and insert it.
In AngularJS you can do this too, but you are encouraged to make use of data binding. Change your model, and because the DOM is bound to it via a template your DOM will automatically update, no intervention required.
Because data binding is done from the template, using either an attribute or the curly brace syntax, it's super easy to do. There's little cognitive overhead associated with it so you'll find yourself doing it all the time.
<input ng-model="user.name" />
Binds the input element to $scope.user.name
. Updating the input will update the value in your current scope, and vice-versa.
Likewise:
<p>
{{user.name}}
</p>
will output the user name in a paragraph. It's a live binding, so if the $scope.user.name
value is updated, the template will update too.
Ajax all of the time
In jQuery making an Ajax call is fairly simple, but it's still something you might think twice about. There's the added complexity to think about, and a fair chunk of script to maintain.
In AngularJS, Ajax is your default go-to solution and it happens all the time, almost without you noticing. You can include templates with ng-include. You can apply a template with the simplest custom directive. You can wrap an Ajax call in a service and create yourself a GitHub service, or a Flickr service, which you can access with astonishing ease.
Service Objects vs Helper Functions
In jQuery, if we want to accomplish a small non-dom related task such as pulling a feed from an API, we might write a little function to do that in our closure. That's a valid solution, but what if we want to access that feed often? What if we want to reuse that code in another application?
AngularJS gives us service objects.
Services are simple objects that contain functions and data. They are always singletons, meaning there can never be more than one of them. Say we want to access the Stack Overflow API, we might write a StackOverflowService
which defines methods for doing so.
Let's say we have a shopping cart. We might define a ShoppingCartService which maintains our cart and contains methods for adding and removing items. Because the service is a singleton, and is shared by all other components, any object that needs to can write to the shopping cart and pull data from it. It's always the same cart.
Service objects are self-contained AngularJS components which we can use and reuse as we see fit. They are simple JSON objects containing functions and Data. They are always singletons, so if you store data on a service in one place, you can get that data out somewhere else just by requesting the same service.
Dependency injection (DI) vs. Instatiation - aka de-spaghettification
AngularJS manages your dependencies for you. If you want an object, simply refer to it and AngularJS will get it for you.
Until you start to use this, it's hard to explain just what a massive time boon this is. Nothing like AngularJS DI exists inside jQuery.
DI means that instead of writing your application and wiring it together, you instead define a library of components, each identified by a string.
Say I have a component called 'FlickrService' which defines methods for pulling JSON feeds from Flickr. Now, if I want to write a controller that can access Flickr, I just need to refer to the 'FlickrService' by name when I declare the controller. AngularJS will take care of instantiating the component and making it available to my controller.
For example, here I define a service:
myApp.service('FlickrService', function() {
return {
getFeed: function() { // do something here }
}
});
Now when I want to use that service I just refer to it by name like this:
myApp.controller('myController', ['FlickrService', function(FlickrService) {
FlickrService.getFeed()
}]);
AngularJS will recognise that a FlickrService object is needed to instantiate the controller, and will provide one for us.
This makes wiring things together very easy, and pretty much eliminates any tendency towards spagettification. We have a flat list of components, and AngularJS hands them to us one by one as and when we need them.
Modular service architecture
jQuery says very little about how you should organise your code. AngularJS has opinions.
AngularJS gives you modules into which you can place your code. If you're writing a script that talks to Flickr for example, you might want to create a Flickr module to wrap all your Flickr related functions in. Modules can include other modules (DI). Your main application is usually a module, and this should include all the other modules your application will depend on.
You get simple code reuse, if you want to write another application based on Flickr, you can just include the Flickr module and voila, you have access to all your Flickr related functions in your new application.
Modules contain AngularJS components. When we include a module, all the components in that module become available to us as a simple list identified by their unique strings. We can then inject those components into each other using AngularJS's dependency injection mechanism.
To sum up
AngularJS and jQuery are not enemies. It's possible to use jQuery within AngularJS very nicely. If you're using AngularJS well (templates, data-binding, $scope, directives, etc.) you will find you need a lot less jQuery than you might otherwise require.
The main thing to realise is that your template drives your application. Stop trying to write big plugins that do everything. Instead write little directives that do one thing, then write a simple template to wire them together.
Think less about unobtrusive JavaScript, and instead think in terms of HTML extensions.
My little book
I got so excited about AngularJS, I wrote a short book on it which you're very welcome to read online http://nicholasjohnson.com/angular-book/. I hope it's helpful.
- "分离关注点"与"MVC(模型,视图,控制器)"不同的想法完全是假的.分离关注点(HTML,CSS和JS)的Web语言模型通过让人们将内容放在网页(标记/ HTML)上而不关心它的外观(样式/布局/ CSS)或它"做什么"来实现这一点. (DOM事件/ AJAX/JavaScript).MVC也将担忧分开.MVC模式中的每个"层"都有不同的角色 - 数据,路由/逻辑或渲染.图层通过回调,路径和模型绑定进行耦合.理论上,一个人可以专注于每一层,这通常就是这种情况.
势在必行与陈述
使用jQuery,您可以一步一步地告诉DOM需要发生什么.使用AngularJS,您可以描述所需的结果,但不能描述如何执行此操作.更多关于这里.另外,请查看Mark Rajcok的答案.
AngularJS是一个使用MVC模式的整个客户端框架(查看它们的图形表示).它非常注重关注点的分离.
jQuery是一个库
AngularJS是一个漂亮的客户端框架,高度可测试,它结合了许多很酷的东西,如MVC,依赖注入,数据绑定等等.
它侧重于关注点和测试(单元测试和端到端测试)的分离,这有助于测试驱动的开发.
最好的方法是通过他们的精彩教程.你可以在几个小时内完成这些步骤; 但是,如果你想掌握幕后的概念,它们包含了无数的参考资料以供进一步阅读.
您可以在已经使用纯jQuery的现有应用程序上使用它.但是,如果要充分利用AngularJS功能,可以考虑使用RESTful方法对服务器端进行编码.
这样做将允许您利用他们的资源工厂,它创建服务器端RESTful API的抽象,并使服务器端调用(获取,保存,删除等)非常容易.
- 通过谈论jQuery如何成为一个"库"而Angular是一个"框架",我认为你正在混淆水域......首先,我认为有可能认为jQuery*是一个框架......它是DOM操作和事件处理的抽象.它可能不是Angular同类事物的框架,但这就是提问者所处的困境:他们并不真正了解Angular和jQuery之间的区别,并且对于所有提问者都知道,jQuery*是*构建客户密集型网站的框架.所以争论术语不会清楚.
- 我想你是那个感到困惑的人.这个问题恰好解决了这个http://stackoverflow.com/questions/7062775/.此外,这个答案可能有助于澄清框架和库之间的区别:http://stackoverflow.com/a/148759/620448
- __library__是您调用的代码.__framework__是调用代码的代码.通过这个定义,Angular是一个框架.您为它提供了组件,Angular认为您的组件是使用它们所需的依赖项实例化的.
- 库不会因为其函数集合特别有用或大而成为"框架".框架为您做出决策.当您开始使用AngularJS时,您可能会因其性质而与之相关联.(例如:你应该只是在指令中更新DOM,否则会出现问题.)这是因为AngularJS是一个框架.使用jQuery时,您可以相对轻松地混合和匹配工具,同时将冲突风险降至最低.那是因为jQuery是一个库,至少也是一个体面的库.
为了描述"范式转换",我认为一个简短的回答就足够了.
AngularJS改变了你找到元素的方式
在jQuery中,您通常使用选择器来查找元素,然后将它们连接起来:$('#id .class').click(doStuff);
在AngularJS中,您可以使用指令直接标记元素,以便将它们连接起来:<a ng-click="doStuff()">
AngularJS并不需要(或希望)你找到使用选择元素- AngularJS的之间的主要区别jqLite与全面的jQuery的是jqLite不支持选择.
所以当人们说"根本不包含jQuery"时,主要是因为他们不希望你使用选择器; 他们希望你学习使用指令.直接,不选择!
- 正如免责声明一样,Angular和jQuery之间存在更多重大差异.但**寻找元素**是需要最大改变思维的元素.
- @AlexanderPritchard Angular的观点是,你不是从你的JavaScript中选择,而是直接从你的模板中选择.控制权的颠倒将权力交给设计师.这是一个深思熟虑的设计原则.要真正__get__ Angular你需要以这种方式思考你的代码.这是一个艰难的转变.
- @superluminary多么棒的报价!"不要选择;直接!" 说真的,我会用那个.
jQuery的
jQuery制作了一些可笑的长命令,比如getElementByHerpDerp
简短和跨浏览器.
AngularJS
AngularJS允许您创建自己的HTML标记/属性,这些标记/属性可以很好地处理动态Web应用程序(因为HTML是为静态页面设计的).
编辑:
说"我有一个jQuery背景我如何在AngularJS中思考?" 就像说"我有一个HTML背景我怎么用JavaScript思考?" 你提问的事实表明你很可能不理解这两种资源的根本目的.这就是为什么我选择通过简单地指出基本区别来回答问题,而不是通过列表说"AngularJS使用指令而jQuery使用CSS选择器来创建一个jQuery对象来执行此操作等等......" .这个问题不需要冗长的答案.
jQuery是一种使浏览器中的JavaScript编程更容易的方法.更短的跨浏览器命令等
AngularJS扩展了HTML,因此您不必<div>
为了创建应用程序而将所有地方放在一起.它使HTML实际上适用于应用程序,而不是它的设计目标,即静态的教育网页.它使用JavaScript以迂回的方式实现这一点,但从根本上说它是HTML的扩展,而不是JavaScript.
jQuery:你对DOM元素的"查询DOM "以及做某事有很多想法.
AngularJS:模型是事实,你总是从那个角度思考.
例如,当您从要在DOM中以某种格式显示的服务器获取数据时,在jQuery中,您需要'1.找到'你要在DOM中放置这些数据的位置,'2.通过创建新节点或仅设置其innerHTML来更新/附加它.然后,当您想要更新此视图时,您将'3.找到'位置'和'4.UPDATE".在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成为框架而不是库的因素之一.
这些是一些非常好但很长的答案.
总结一下我的经历:
- 控制器和提供者(服务,工厂等)用于修改数据模型,而不是HTML.
- HTML和指令定义布局和与模型的绑定.
- 如果您需要在控制器之间共享数据,请创建服务或工厂 - 它们是在应用程序中共享的单例.
- 如果需要HTML小部件,请创建指令.
- 如果您有一些数据,现在正在尝试更新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操作分开,所以不要试图将它们包括在内.
收听播客JavaScript Jabber:第32集,其中包括AngularJS的原始创作者:Misko Hevery和Igor Minar.他们谈论了从其他JavaScript背景来到AngularJS的感受,特别是jQuery.
在播客中提出的要点之一就是针对您提出了很多问题,并对您提出了问题:
IGOR:因此,将指令视为编译器的指令,只要您遇到模板中的某个元素或此CSS,就会告诉它,并且您保留了这种代码,并且该代码负责元素以及该元素下面的所有内容在DOM树中.
整个剧集的成绩单可在上面提供的链接中找到.
所以,直接回答你的问题:AngularJS是非常自以为是的,是一个真正的MV*框架.但是,您仍然可以在指令中使用jQuery来完成所有非常酷的东西.这不是"我如何做我在jQuery中常用的东西?"的问题.就像"我如何使用jQuery中的所有东西来补充AngularJS一样"这个问题?
这真的是两种截然不同的心态.
- 我不确定我是否会完全同意Angular非常自以为是.你想要自以为是,看看Ember.我会把Angular描述为有金发女郎的意见 - 对于我看到的很多东西,jQuery的意见太少而Ember太多了.Angular似乎恰到好处.
我发现这个问题很有趣,因为我第一次认真接触JavaScript编程是Node.js和AngularJS.我从来没有学过jQuery,我想这是件好事,因为我不必忘记任何事情.事实上,我主动避免jQuery解决方案来解决我的问题,而只是寻找一种"AngularJS方式"来解决它们.所以,我想我对这个问题的回答基本上归结为"想象一个从未学过jQuery的人"并且避免任何直接合并jQuery的诱惑(显然AngularJS在某种程度上在幕后使用它).
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中编写表达式并在运行时评估它们.创建自己的指令和服务,并在不需要额外努力的情况下在另一个项目中使用它们.
作为一个JavaScript MV*初学者,纯粹关注应用程序架构(而不是服务器/客户端问题),我肯定会推荐以下资源(我很惊讶还没有提到):JavaScript设计模式,作者:Addy Osmani ,作为不同的JavaScript设计模式的介绍.本回答中使用的术语来自上面的链接文档.我不打算在接受的答案中重复措辞.相反,这个答案链接到为AngularJS(和其他库)提供动力的理论背景.
像我一样,你会很快意识到AngularJS(或者Ember.js,Durandal和其他MV*框架)是一个复杂的框架,它汇集了许多不同的JavaScript设计模式.
我发现它也更容易,以测试(1)天然的JavaScript代码和(2)对于这些模式中的每一个更小的文库分别潜水成一个全球框架之前.这使我能够更好地理解框架所针对的关键问题(因为您个人面临问题).
例如:
- JavaScript面向对象编程(这是一个Google搜索链接).它不是一个库,但肯定是任何应用程序编程的先决条件.它教会了我原型,构造函数,单例和装饰器模式的实现
- 的jQuery/下划线的门面图案(如WYSIWYG的用于操纵DOM)
- Prototype.js用于原型/构造函数/ mixin模式
- RequireJS/Curl.js用于模块模式/ AMD
- KnockoutJS用于可观察,发布/订阅模式
注意:这个列表不完整,也不是"最好的图书馆"; 他们恰好是我用过的图书馆.这些库还包括更多模式,提到的模式只是它们的主要焦点或原始意图.如果您觉得此列表中缺少某些内容,请在评论中提及,我将很乐意添加它.
实际上,如果您使用的是AngularJS,则不再需要jQuery.AngularJS本身具有绑定和指令,对于您可以使用jQuery执行的大多数事情来说,这是一个非常好的"替代".
我通常使用AngularJS和Cordova开发移动应用程序.我需要的jQuery唯一的东西是Selector.
通过谷歌搜索,我看到有一个独立的jQuery选择器模块.这是嘶嘶声.
我决定制作一个小代码片段,帮助我使用AngularJS快速启动一个网站,使用jQuery Selector(使用Sizzle).
我在这里分享了我的代码:https://github.com/huytd/Sizzular