jQuery和AngularJS的区别浅析

最近一直在研究angularjs,最大的感受就是它和之前的jQuery以及基于jQuery的各种库设计理念完全不同,如果不能认识到这点而对于之前做jQuery开发的程序员,去直接学习angularjs的话,很可能学了很久还不知道这个东西能用来干什么以及怎么使用,怎么和UI进行结合等问题,在stackoverflow上找到一篇关于这方面的文章,阅读之后颇有收获,在此基础上将它译成中文,以求抛砖引玉大家一同学习。

原问题:假如我熟悉利用jQuery去开发客户端应用,那么我怎么上手angularjs,能否描述下所需要的模式转变,下面这些问题能够帮助你给出一个回答:

1.在设计客户端web应用的时候有什么区别,最大的区别是什么?

2.我应该停止使用哪些技术,同时又使用哪些技术作为替代?

3.是否存在服务端需要考虑的东西或者说一些限制呢?

回答:

1.不要首先设计好你的页面,然后再通过DOM操作去修改它

在jQuery中,你首先设计了一个page,然后再去动态修改它的内容,这是因为jQuery被设计用来进行扩展并在这个前提下大幅度地增加和修改内容,但是在angularjs中,你必须在心中先设计好你的架构,

从一开始,你就要摒弃“我拥有一个DOM元素并且想让它去做某件事”,代之为“我需要完成什么任务,然后接着设计你的应用,最后再去设计你的视图view层”。

2.不要使用angularjs去扩展jQuery

相应地,不要存在说让jQuery去干某些事情,然后在此基础上添加angularjs的功能让它去管理model以及controller的想法。所以我一般不推荐AngularJS开发新手同时使用jQuery,至少在他们还没有适应AngularJS的开发模式之前不会去推荐这样做,但是当你真正开始适应angularjs 的方式之后,你会发觉这是一件很诱人的事情。

我曾经看到过很多开发者采用将150到200行代码的jQuery插件利用angularjs的回调以及$apply方法封装起来,这种方式使得代码看起来极其复杂,但是实际上他们让这些插件跑起来了!问题在于,在大部分情况下jQuery插件能够用angularjs进行重写,并且可能只会使用很少量的代码,同时这种重写使得代码很直观且易于理解,这显然好过于将jQuery代码直接做封装。

所以最后说,当你遇见问题的时候,首先要以angularjs的思维进行思考,如果找不到解决方案,可以求助于社区,如果说没有人能够给出一个简单的方案,那么才考虑使用jQuery,不要让jQuery成为你的拐杖,否则你永远掌握不了AngularJS。

3.要以架构为中心进行思考

首先你要知道单页应用属于web应用,它们不是传统的多网页网站,所以我们要同时作为一个服务端和客户端开发者的思维进行思考,我们需要思考如何将我们的应用分为独立的,可扩展的以及可测试的部分。

那么接下来我们如何采用AngularJS思维去工作呢,以下是一些将其与jQuery对比之后的基本准则:

以下是某个应用的视图层:

在jQuery中,我们动态地去修改这个视图,我们使用ul去定义一个dropdown menu

代码如下:

<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中,我们采用如下逻辑使用这个dropdownMenu

代码如下:

$('.main-menu').dropdownMenu();

让我们回头看看这个view,你会发现它的功能并不是很直白,对于小型应用来讲,这样是可以的,但是对于大型应用来讲,这种方式会让人费解并且难以维护;

在angularjs中,这个视图实际上是一项基于视图的功能,我们可以这样来定义ul

代码如下:

<ul class="main-menu" dropdown-menu>
    ...
</ul>

这两种方式实际上做了同样的事情,但是在AngularJS方式下任何人看到这个视图模板就知道接下来要干什么。无论何时当一个新成员加入开发团队之后他都能够看到这里并发现这里有一个叫做dropdownMenu的指令去操作view,他不需要去猜想正确的答案或者审查其他的代码,这个视图就直接告诉我们它要做什么,相比于jQuery,它更为简洁。

常常有些AngularJS新手问这样的问题:我怎么才能找到某个确切类型的所有link并在此基础上添加一个directive,但是当我们回答了“你不应该这样去做,你这是一种半jQuery半angularjs的想法”时,他们会觉得很吃惊。

问题在于他们试图在AngularJS背景下用jQuery去做某件事,这通常不是一种好的方式,在指令之外你不需要去做任何dom操作,而指令是直接内添加在视图上的,所以意图已经很明显了。记住,不要先设计好之后再去修改,而是先有架构然后在这个框架下再去设计。

数据绑定

这是到目前为止AngularJS最令人瞩目的特性了,在数据绑定方面它舍弃了对DOM的操作方式,而这一切都是由AngularJS来自动更新视图,你不必写操作dom的代码,在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>

现在我们不使用ul,而是使用Bootstrap的弹出框,但是我们不用修改controller中的代码,更为重要的是,不管是数据如何修改,视图层也会自动随之发生变化,非常简洁!

尽管我这里不会做演示,但是你需要知道数据绑定是双向的,你可以编辑数据通过添加指令<input ng-model="entry.msg" />,此外还有很多其他的令人兴奋的地方。

区别model层

在jQuery中,DOM类似于一种model,但是在AngularJS中,我们拥有不同于jQuery中的model层以便我们可以以任何我们想要的方式去管理它,它是完全独立于视图之外的。这种方式是有助于我们进行数据绑定并且可以保持对分离的关注,而且可以具备更好的可测试性。

关注点分离

以上所讲都和这个总体的话题相关:让你关注分离,你的视图层显示记录,你的model层代表数据,你还有个服务层用来执行这些可复用的任务。你使用directive来执行dom操作并扩展你的视图,并将它和controller连接起来,这也就是我在其他方面提到的有关于增强可测试性的原因。

依赖注入

帮助我们解决关注点分离的是依赖注入(DI),如果你是一个服务端开发者(Java或者PHP),你可能已经很熟悉这个概念了,但是如果你是从事客户端开发的,你会觉得这个概念可能有些多余和纯属追求时髦,但是实际上不是这样。

从广义的角度讲,DI意味着你可以自由地声明组件然后从这些组件中进行实例化,这是理所当然的。你不必知道加载顺序,文件位置等诸如此类的事情,这种魔力不是能够立即看到,但是我会给出一个例子:测试。

我们说在应用中,我们需要一个依赖于应用状态和本地存储的服务用来通过一个rest API来执行服务端存储,当我们测试我们的controller时,我们不必和服务端进行通信,毕竟只是在测试controller而已。我们仅添加一个与我们最初组件相同的mock服务,注入器能够确保我们的controller获得一个虚拟的服务,controller自身不必也不需要了解这种差异。

那么说说测试吧。

4.以测试驱动的开发

这部分是一个架构的第三部分,但是他是很重要的,以至于我需要将它放在最重要的位置。

在我们所有见过的,用过的以及写过的jQuery插件中,有多少具有一套测试组件呢?其实并不多,这是因为jQuery在测试上不易控制,但是AngularJS却与此不同。

在jQuery中,测试的唯一方法是使用一个demo页去创建一个独立组件来使得我们的测试可以执行dom操作。我们接下来我们必须开发一个独立的组件然后将它集成到我们的应用中来,这是多不方便啊!在很多情况下,当我们使用jQuery开发实际上是做了很多重复开发而不是以测试驱动的开发,这又能怪我们吗?

但是在AngularJS中我们可以关注分离点,所以我们可以做一些测试驱动的开发。例如,我们有一个directive用来说明在menu中我们的当前路径,我们可以在视图中这样声明:

代码如下:

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

我们直接run测试用例,你会发现是失败的,这时候需要创建这个指令,如下:

代码如下:

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

再次run这个测试用例,你会发现通过了并且菜单如请求的样子显示,我们的开发是兼有反复性和可测试性,非常酷吧!

5.从概念上讲,指令不是打包的jQuery

你常常听说,dom操作只能在指令中,这是必须的,你必须严肃对待。

让我们深入讨论,

某些指令仅仅是装饰我们的视图(例如ngClass),因此有时候直接操作dom是可以的,但是当一个指令类似于一个小物件并且拥有自己的模板,那么它应该当做一个分离的关注点,这就是说,它的模板需要和link中的执行逻辑以及其他controller函数分离开。

AngularJS拥有一整套的工具可以是这种分离更简单,使用ngClass指令,我们可以动态地更新class,使用ngBind我们可以进行双向数据绑定,使用ngShow和ngHide 我们

可以采用编程的形式显示和隐藏一个元素,也包括我们自己写的很多指令。换句话说,我们可以不用Dom操作而完成所有工作,dom操作越少,指令越容易测试,越容易指定他们的style属性,就越容易在将来改变他们,那么他们就越容易复用和分发。

我看过很多AngularJS新手使用指令封装一大串 jQuery代码,换句话说,既然我不能在controller里面进行dom操作,那么我可以将他放在指令中,虽然这相对于直接操作dom好很多,但是任然是错误的。

看看我们在上面的记录,即使我们将其放在一个指令中,我们任然需要以Angular的方式去操作它,这种方式不执行dom操作!在很多时候dom操作是需要的,但是这种情况比你想的要少得多。当我们需要做dom操作的时候先问问自己这里是否必须这样做,这才是一种更好的方式。

下面是一个简单的例子用来表明我常常见到的一种模式,我们需要I一个可切换的button:

代码如下:

.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去进行替代,只要引入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;
            };
        }
    };
});

模板元素是在 template属性中,你可以很容易替换掉它的style,而逻辑根本不用发生变化,达到了完全复用!

还有其他的好处,比如测试起来很简单,不管模板里面是什么,指令API都不会发生改变,所以重构它很简单。你可以随意多次改变你的模板而不用改变指令,无论你怎么改变,你的测试总能通过!

所以说指令不是一堆jQuery代码的集合,比如函数等,而是HTML代码的扩展,如果HTML代码不能实现你需要的功能,你可以写一个指令去实现它,然后像使用HTML那样去使用它。

以另外一种方式讲,AngularJS如果不做额外的事情,想想我们怎么能够使用ngClick,ngClass指令呢?

总结

不要总使用jquery ,甚至不要去引用它,它会阻止你前进,当我们回到这个问题—你知道你怎么在AngularJS中以jquery方式解决问题,但是当你使用诸如$等选择器时,你要想想它们实际上是禁锢了AngularJS,如果你不知道怎么不用jQuery实现,那么去请教别人,一次一次去问,最好的方式是不需要使用jQuery,使用jQuery只会导致你的工作量提升。

(0)

相关推荐

  • Angular动态添加、删除输入框并计算值实例代码

    Angular动态添加.删除输入框并计算值实例代码 摘要: 在学习群中交流时,有人分享了一个动态添加输入框的方法,我在其基础上进行了一些改进 这个功能本身并不复杂,但还是要注意,每个ng-model的对象必须是不同的,这样才能把它们分隔开. 下面是完整代码: JS: angular.module("myApp",[]) .controller("inputController",function($scope){ $scope.items=[]; //初始化数组,以

  • Angularjs中UI Router全攻略

    首先给大家介绍angular-ui-router的基本用法. 如何引用依赖angular-ui-router angular.module('app',["ui.router"]) .config(function($stateProvider){ $stateProvider.state(stateName, stateCofig); }) $stateProvider.state(stateName, stateConfig) stateName是string类型 stateCon

  • AngularJS内置指令

    指令,我将其理解为AngularJS操作HTML element的一种途径. 由于学习AngularJS的第一步就是写内置指令ng-app以指出该节点是应用的根节点,所以指令早已不陌生. 这篇日志简单记录了一些内置指令,先使用起来,再谈一些有趣的东西. 内置指令 所有的内置指令的前缀都为ng,不建议自定义指令使用该前缀,以免冲突. 首先从一些常见的内置指令开始. 先列出一些关键的内置指令,顺便简单说说作用域的问题. ng-model 将表单控件和当前作用域的属性进行绑定,这么解释似乎也不太正确.

  • AngularJS 日期格式化详解

    AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了.所以我做了一些工作(你也可以觉得是小花招)来让浏览器做我想要的事. AngularJS的日期格式化有两种形式,一种是在HTML页面,一种是在JS代码里,都是用到AngularJS的过滤器$filter. HTML: date_expression 即 你在$scope中设的date类型变量(注意,一定是date object才正确), 也是要显

  • angularjs指令中的compile与link函数详解

    通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你创建非常语义化以及高度重用的组件,可以理解为web components的先驱者. 网上已经有很多介绍怎么使用指令的文章以及相关书籍,相互比较的话,很少有介绍compile与link的区别,更别说pre-link与post-link了. 大部分教程只是简单的说下compile会在ng内部用到,而且建

  • AngularJs根据访问的页面动态加载Controller的解决方案

    用Ng就是想做单页面应用(simple page application),就是希望站内所有的页面都是用Ng的Route,尽量不用location.href,但是这样的webapp好处是很多,但是美中不足的是当你的webapp随着时间的推移,用户变多,功能变得更丰富,controller也变得越来越多,你不得不把所有的controller当作全局模块进行加载,以使得在站内任何一个页面中按F5刷新后能route到任意一个其他页面,而不会发生找不到controller的错误,加载所有的control

  • angular.element方法汇总

    addClass()-为每个匹配的元素添加指定的样式类名 after()-在匹配元素集合中的每个元素后面插入参数所指定的内容,作为其兄弟节点 append()-在每个匹配元素里面的末尾处插入参数内容 attr() - 获取匹配的元素集合中的第一个元素的属性的值 bind() - 为一个元素绑定一个事件处理程序 children() - 获得匹配元素集合中每个元素的子元素,选择器选择性筛选 clone()-创建一个匹配的元素集合的深度拷贝副本 contents()-获得匹配元素集合中每个元素的子元

  • angularJS中$apply()方法详解

    对于一个在前端属于纯新手的我来说,Javascript都还是一知半解,要想直接上手angular JS,遇到的阻力还真是不少.不过我相信,只要下功夫,即使是反人类的设计也不是什么大的问题. Okay,废话不多说.为了弄明白angular JS为何物,我先是从Scope开始.那么什么是Scope呢?借用官方文档的一段话: 复制代码 代码如下: "scope is an object that refers to the application model. It is an execution c

  • jQuery和AngularJS的区别浅析

    最近一直在研究angularjs,最大的感受就是它和之前的jQuery以及基于jQuery的各种库设计理念完全不同,如果不能认识到这点而对于之前做jQuery开发的程序员,去直接学习angularjs的话,很可能学了很久还不知道这个东西能用来干什么以及怎么使用,怎么和UI进行结合等问题,在stackoverflow上找到一篇关于这方面的文章,阅读之后颇有收获,在此基础上将它译成中文,以求抛砖引玉大家一同学习. 原问题:假如我熟悉利用jQuery去开发客户端应用,那么我怎么上手angularjs,

  • 体验jQuery和AngularJS的不同点及AngularJS的迷人之处

    AngualrJS是一个很贴心的web应用框架.它有很不错的官方文档和示例:经过在现实环境中的测试著名的TodoMVC project,它在海量的框架中脱颖而出:而且网上到处都是很不错演示或者展示.但是对于一个没有接触过和AngularJS相似的框架而且还是几乎都在使用像jQuery这样的JavaScript类库的开发者来讲,要从jQuery的思想转变成AngularJS的思想是有点困难的.至少对于我来说是这样的,所以我想要分享一些学习笔记希望帮到一些开发者. 本篇通过jQuery和Angula

  • jquery操作angularjs对象

    jquery是一个非常强大的js框架,angularjs是一个非常牛的前端mvc框架.虽然用其中的任何一个框架在项目中够用了,但是有时候这两个框架需要混合着用,虽然不推荐.但有时候混合用时,却非常方便,不要考虑那么多,只要能实现功能,何乐而不为? 最近做的一个产品,前端用angularjs,但表格框架用的却是jquery.datatables.js,当然其中少不了碰到jquery与angularjs交互问题.由于公司保密,我就不用真实项目演示了,写个小demo吧,当然真实的项目要复杂得多. <!

  • 玩转spring boot 结合jQuery和AngularJs(3)

    在上篇的基础上 准备工作: 修改pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&

  • JavaScript中的 attribute 和 jQuery中的 attr 方法浅析

    根据大体上的意思我感觉js setAttribute与jquery中attr工作是完全一样的,只是jquery中简写了并且工能更强大了,下面我来分别介绍一下他们的用法. attribute 是原生js dom 对象上的一个属性,这个属性有很多子属性,比如 isId(判断属性是否是Id) , name (获取属性名称) , value (获取属性值),attributes 用来获取dom元素 的所有属性集合. 话不多说,上例子了: <input type="text" name=&

  • ASP.NET MVC中jQuery与angularjs混合应用传参并绑定数据

    要求是这样子的,在一个列表页中,用户点击详细铵钮,带记录的主键值至另一页. 在另一外页中,获取记录数据,然后显示此记录数据在网页上. 先用动图演示: 昨天有分享为ng-click传递参数 <angularjs为ng-click事件传递参数>http://www.cnblogs.com/insus/p/7017737.html 上面仅仅是在ng-click传入一个值,但是在ASP.NET MVC中,还需要把这个值传至另外一个视图中<ASP.NET MVC传递参数(model)>htt

  • JavaScript中const、var和let区别浅析

    在JavaScript中有三种声明变量的方式:var.let.const.下文给大家介绍js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. const b = 2;//正确 // const b;//错误,必须初始化 console.log('函数外const定义b:' + b);//有输出值 // b = 5; // console.log('函数外修改const定义b:' + b);//无法输出 2.var定义的变量可以修改,

  • boostrapTable的refresh和refreshOptions区别浅析

    在使用bootstrapTable时,刷新数据有两个方法refresh.refreshOptions,其用法相似: <table id="event_table"></table> ... <script> $("#event_table").bootstrapTable('refresh', {url: new_url }); $("#event_table").bootstrapTable('refresh

  • C#中数组、ArrayList、List、Dictionary的用法与区别浅析(存取数据)

    前言 在工作中经常遇到C#数组.ArrayList.List.Dictionary存取数据,但是该选择哪种类型进行存储数据,对于初学者的我一直不知道该怎么取舍.于是抽空好好看了下他们的用法和比较,在这里总结下来,后面有需要改进的再更新. 初始化 数组: int[] buff = new int[6]; ArrayList: ArrayList buff = new ArrayList(); List: List<int> buff = new List<int>(); Dictio

  • fragment中的add和replace方法的区别浅析

    使用 FragmentTransaction 的时候,它提供了这样两个方法,一个 add , 一个 replace ,对这两个方法的区别一直有点疑惑. 我觉得使用 add 的话,在按返回键应该是回退到上一个 Fragment,而使用 replace 的话,那个别 replace 的就已经不存在了,所以就不会回退了.但事实不是这样子的.add 和 replace 影响的只是界面,而控制回退的,是事务. public abstract FragmentTransaction add (int con

随机推荐