如果我有jQuery背景,是否应该学习AngularJS?

发布时间 2023-10-15 23:36:40作者: 小满独家

内容来自 DOC https://q.houxu6.top/?s=如果我有jQuery背景,是否应该学习AngularJS?

假设我熟悉在jQuery上开发客户端应用程序,但现在我想开始使用AngularJS。你能描述一下必要的范式转变吗?以下是一些问题,可能有助于你回答:

  • 我如何以不同的方式架构和设计客户端Web应用程序?最大的区别是什么?
  • 我应该停止做什么/使用什么;我应该开始做什么/使用什么代替?
  • 有没有服务器端的考虑/限制?

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


  1. 不要在设计页面后使用DOM操作进行更改
    ====================================================================================================================

在jQuery中,你可以先设计页面,然后使其动态化。这是因为jQuery的设计初衷是为了增强功能,并由此得到了巨大的发展。

但在AngularJS中,你必须从头开始考虑架构。不要以“我有这个DOM片段,我想让它做X”的想法开始,而必须从你想要实现的目标开始,然后设计应用程序,最后设计视图。

  1. 不要在AngularJS中使用jQuery增强功能
    ====================================

同样地,不要认为jQuery可以完成X、Y、Z等功能,所以我会在其上添加AngularJS模型和控制器。当你刚开始学习时,这非常诱人,这就是为什么我建议新的AngularJS开发人员不要使用jQuery,至少在他们习惯使用“Angular方式”之前。

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

总之:在解决问题时,首先“用AngularJS思考”;如果你想不到解决方案,可以向社区寻求帮助;在所有这些事情之后,如果仍然没有简单的解决方案,那么请随意使用jQuery。但不要让jQuery成为你的拐杖,否则你永远无法掌握AngularJS。

  1. 始终要考虑架构
    ========================================

首先,要知道单页应用程序是应用程序,而不是网页。因此,我们需要像服务器端开发人员一样思考,同时像客户端开发人员一样思考。我们必须考虑如何将我们的应用程序划分为独立的、可扩展的、可测试的组件。

那么你该怎么做呢?如何用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的指令在操作它;她不需要猜测正确的答案或筛选任何代码。视图告诉我们应该发生什么。更干净。

新手开发者经常问一个问题,比如:如何找到特定类型的所有链接并在它们上添加指令。当我们回答“你不需要这样做”时,开发者总是感到惊讶。但你不这样做的原因是这就像是半jQuery,半AngularJS,而且效果不好。问题出在开发者试图在AngularJS的上下文中“做jQuery”。这永远不会很好地工作。视图是官方记录。在指令(下面会详细介绍)之外,你永远、永远、永远不要改变DOM。指令是在视图中应用的,所以意图是明确的。

请记住:不要先设计,然后再标记。你必须先架构,然后再设计。

数据绑定

这是AngularJS迄今为止最令人惊叹的功能之一,大大减少了我之前提到的需要进行DOM操作的需求。AngularJS会自动更新你的视图,所以你不需要手动操作!在jQuery中,我们响应事件然后更新内容。例如:

$.ajax({
  url: '/myEndpoint.json',
  success: function ( data, status ) {
    $('ul#log').append('<li>数据已接收!</li>');
  }
});

对于一个看起来像这样的视图:

<ul class="messages" id="log">
</ul>

除了关注点混合的问题,我们还会遇到我之前提到的表示意图的问题。但更重要的是,我们必须手动引用和更新DOM节点。如果我们想删除日志条目,我们也必须针对DOM进行编码。我们如何在不依赖DOM的情况下测试逻辑?如果我们想要更改外观呢?

这有点混乱,有点脆弱。但在AngularJS中,我们可以这样做:

$http( '/myEndpoint.json' ).then( function ( response ) {
    $scope.log.push( { msg: '数据已接收!' } );
});

我们的视图可以像这样:

<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中,我们有一个可以完全独立于视图管理的单独模型层。这有助于上述数据绑定,保持关注点分离,并引入更大的可测试性。其他答案提到了这一点,所以我只会留下这句话。

关注点分离

以上第3部分内容:

所有的上述内容都与这个总体主题有关:保持你的关注点分离。你的视图充当着预期发生的事情的官方记录(大部分时间);你的模型代表你的数据;你有一个服务层来执行可重用的任务;你进行DOM操作并用指令增强你的视图;你用控制器将它们粘合在一起。这也在其他答案中提到过,我只会在下面关于可测试性的部分补充一些内容。

依赖注入

为了帮助我们实现关注点分离,我们使用依赖注入(DI)。如果你来自服务器端语言(如Java到PHP),你可能已经熟悉这个概念,但如果你是来自jQuery的客户端开发人员,这个概念可能看起来有些愚蠢、多余或者时髦。但它并非如此。:-)

从广义上讲,DI意味着你可以自由声明组件,然后从任何其他组件中请求它的实例,它就会被赋予。你不需要知道加载顺序,或者文件位置,或者其他任何类似的事情。这种力量可能不会立即显现出来,但我会提供一个(常见的)例子:测试。

假设在我们的应用程序中,我们需要一个通过REST API实现服务器端存储的服务,并根据应用程序状态使用本地存储。当我们在控制器上运行测试时,我们不想与服务器进行通信 - 我们正在测试控制器,毕竟。我们可以添加一个与我们原始组件同名的模拟服务,注入器将确保我们的控制器自动获得假的实例 - 我们的控制器不知道也不需要知道区别。

说到测试...

  1. 始终进行测试驱动开发
    =====================================

这部分实际上是第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();
}));

当我们运行测试时,我们可以确认它会失败。现在才应该创建我们的指令。
指令:当激活时,函数($location){
return {
范围: true,
link: function (范围,元素,属性) {
范围.$on( '$routeChangeSuccess', function () {
如果 ($location.path() == 元素.attr( 'href' ) ) {
元素.addClass( 'active' );
}
否则 {
元素.removeClass( 'active' );
}
});
}
};
}

我们的测试现在通过并且我们的菜单按照要求执行。我们的开发既迭代又测试驱动。太酷了。

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

你经常听到“只在指令中进行DOM操作”。这是一个必要条件。请给予应有的尊重!

但是让我们深入一点...

有些指令只是装饰视图中已经存在的内容(想想ngClass),因此有时直接进行DOM操作,然后基本上就完成了。但是,如果一个指令就像一个“小部件”并且有一个模板,它也应该也尊重关注点分离。也就是说,模板也应该在很大程度上独立于它在链接和控制器函数中的实现。

AngularJS提供了一整套工具,使这变得非常简单;使用ngClass我们可以动态更新类;ngModel允许双向数据绑定;ngShowngHide以编程方式显示或隐藏元素;以及许多其他 - 包括我们自己编写的一些。换句话说,我们可以做各种各样的神奇*而不需要DOM操作。减少DOM操作的次数,指令就越容易测试,越容易样式化,将来更改起来就越容易,也就越可重用和可分发。

我看到许多新来的AngularJS开发者将指令作为放置一堆jQuery的地方。换句话说,他们认为“既然我不能在控制器中进行DOM操作,我就把那些代码放到指令中”。虽然这当然要好得多,但它通常*仍然是错误的。

想想我们在第3节中编写的记录器。即使我们把它放到指令中,我们仍然想要以“Angular的方式”去做。它仍然不进行任何DOM操作!有很多时候需要进行DOM操作,但它比你想象的要罕见得多!在你应用程序的任何地方进行DOM操作之前,问问自己是否真的需要。可能有更好的方法。

这里有一个快速的例子,展示了我最常见的模式。我们想要一个可切换的按钮。(注意:这个例子有点牵强附会,为了表示更复杂的情况而过于冗长,但这些情况都用完全相同的方式解决了。)

.directive( 'myDirective', function () {
    return {
        模板: '<a class="btn">Toggle me!</a>',
        link: function ( scope, element, attrs ) {
            var on = false;

            $(element).click( function () {
                on = !on;
                $(element).toggleClass('active', on);
            });
        }
    };
});

这个指令有几个问题:

  1. 首先,jQuery从来都不是必要的。我们在这里没有做任何需要jQuery的事情!
  2. 其次,即使我们已经在我们的页面上有jQuery,我们也没有理由在这里使用它;我们可以直接使用angular.element,我们的组件即使在没有jQuery的项目中也可以正常工作。
  3. 第三,即使假设这个指令需要jQuery才能工作,jqLite(angular.element)如果加载了jQuery,也会始终使用jQuery!所以我们不需要使用$ - 我们只需要使用angular.element
  4. 第四,与第三个密切相关的是,jqLite元素不需要用$包装 - 传递给链接函数的element已经是jQuery元素了!
  5. 最后,我们在前几个部分提到过,为什么我们要将模板内容混合到我们的逻辑中呢?
    指令:可以像这样更简单地重写这个指令(即使是对于非常复杂的情况也可以!)
.directive( 'myDirective', function () {
    return {
        范围: true,
        模板: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
        链接: function (范围,元素,属性) {
            范围.on = false;

            范围.toggle = function () {
                范围.on = !范围.on;
            };
        }
    };
});

再次强调,模板内容在模板中,因此您(或您的用户)可以轻松地将其替换为满足任何所需风格的模板,而逻辑部分永远不需要更改。可重用性 - 很棒!

还有其他好处,比如测试 - 很简单!无论模板中包含什么,指令的内部API永远不会被触及,因此重构很容易。您可以随意更改模板而无需更改指令。而且无论您对模板进行了什么更改,测试仍然会通过。

哇哦!

那么,如果指令不仅仅是类似jQuery的函数的集合,那它们是什么东西呢?指令实际上是HTML的扩展。如果HTML没有完成您需要它完成的任务,则编写一个指令来完成它,然后就像它是HTML的一部分一样使用它。

换句话说,如果AngularJS没有提供某些功能,请考虑团队如何将其与ngClickngClass等集成。

总结

不要使用jQuery。甚至不要包含它。它会阻碍您的发展。当您遇到一个问题,认为您已经知道如何使用jQuery解决时,在伸手去拿$之前,尝试思考如何在AngularJS的范围内解决这个问题。如果您不知道答案,请寻求帮助!通常情况下,最好的解决方法不需要jQuery,而试图用jQuery解决问题只会为您带来更多的工作。