一尘不染

如果我有jQuery背景,那么“ AngularJS中的思考”吗?

javascript

假设我熟悉使用jQuery开发客户端应用程序,但是现在我想开始使用AngularJS。您能否描述必要的范式转换?以下是一些可以帮助您确定答案的问题:

  • 我如何以不同的方式设计和设计客户端Web应用程序?最大的区别是什么?
  • 我应该停止做什么/使用;我应该开始做什么/使用呢?
  • 是否有服务器端注意事项/限制?

我不希望在jQuery和之间进行详细的比较AngularJS


阅读 290

收藏
2020-04-22

共1个答案

一尘不染

1.不要设计您的页面,然后通过DOM操作对其进行更改

在jQuery中,您可以设计一个页面,然后将其动态化。这是因为jQuery是为增强而设计的,并且在此简单前提下得到了难以置信的增长。

但是在AngularJS中,您必须从头开始,牢记架构。您不必从“我拥有DOM的这个部分,而我想使其成为X”开始,而是必须从要完成的事情开始,然后开始设计应用程序,然后最后开始设计视图。

2.不要用AngularJS扩充jQuery

同样,不要以jQuery做X,Y和Z的想法开始,所以我只在模型和控制器的基础上添加AngularJS。这是 真的
,当你刚刚起步的,这就是为什么我总是建议新AngularJS开发完全不使用jQuery,至少直到他们习惯做的事情“角路”诱人。

我在这里和邮件列表中已经看到许多开发人员使用jQuery插件(包含150或200行代码)创建了这些精心设计的解决方案,然后将其粘贴到AngularJS中,并使用了一系列$apply令人困惑和费解的回调和;但是他们最终使它起作用了!问题在于,在
大多数 情况下,可以用少量代码在AngularJS中重写jQuery插件,突然之间所有事情都变得容易理解和直接了。

底线是:解决时,首先“在AngularJS中思考”;如果您想不出解决方案,请询问社区;如果毕竟,有没有简单的解决方案, 然后
随意达到了jQuery。但是不要让jQuery成为拐杖,否则您将永远无法掌握AngularJS。

3.总是从架构上思考

首先知道单页应用程序。它们 不是 网页。因此 ,除了 像客户端开发人员那样思考 之外 ,我们还需要像服务器端开发人员 那样
思考。我们必须考虑如何将应用程序划分为各个可扩展的可测试组件。

那你该 怎么 做呢?您如何“在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' );
                }
            });
        }
    };
});

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

5.从概念上讲,指令 不是 打包的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 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的 需要这种指令工作,jqLit​​e( angular.element)将 始终 使用jQuery,如果它是装的!因此我们不必使用$-我们可以使用angular.element
  4. 第四,与第三个紧密相关,是不需要包装jqLit​​e元素$- element传递给link函数的jqLit​​e元素 已经是 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;
            };
        }
    };
});

同样,模板内容在模板中,因此您(或您的用户)可以轻松地将其替换为符合任何必要样式的样式,而无需触及 逻辑 。可重用性-繁荣!

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

w00t!

因此,如果指令不仅仅是类似jQuery的函数的集合,它们是什么?指令实际上 是HTML的扩展
。如果HTML不能做您需要做的事情,您可以编写一条指令来为您做,然后像使用HTML一样使用它。

换句话说,如果AngularJS没做什么开箱,认为球队如何完成它适合对符合ngClickngClass等。

摘要

甚至不使用jQuery。甚至不包含它。它会让你退缩。并且当您遇到问题时,您认为自己已经知道如何使用jQuery解决问题,因此在$尝试之前,请尝试考虑如何在AngularJS范围内解决该问题。如果您不知道,请询问!20的19倍中,最好的方法不需要jQuery,并尝试使用jQuery解决它会为您带来更多工作。

2020-04-22