javascript - "Thinking in AngularJS"如果我有 jQuery 背景?

标签 javascript jquery angularjs

关闭。这个问题需要更多 focused 。它目前不接受答案。




6年前关闭。










锁定。这个问题及其答案是 locked 因为这个问题是题外话但具有历史意义。它目前不接受新的答案或互动。








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

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

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

    最佳答案

    1.不要设计你的页面,然后用DOM操作改变它

    在 jQuery 中,您设计一个页面,然后使其动态化。这是因为 jQuery 是为增强而设计的,并且在这个简单的前提下已经取得了令人难以置信的发展。

    但是在 AngularJS 中,您必须从头开始考虑您的架构。与其从思考“我有这块 DOM 并且我想让它做 X”开始,你必须从你想要完成的事情开始,然后设计你的应用程序,最后设计你的 View 。

    2. 不要用 AngularJS 来扩充 jQuery

    同样,不要以为 jQuery 可以执行 X、Y 和 Z 的想法开始,所以我将在模型和 Controller 的基础上添加 AngularJS。当你刚开始时,这真的很诱人,这就是为什么我总是建议新的 AngularJS 开发人员根本不要使用 jQuery,至少在他们习惯了“Angular 方式”做事之前。

    我在这里和邮件列表上看到许多开发人员使用 150 或 200 行代码的 jQuery 插件创建了这些精心设计的解决方案,然后他们将这些复杂的解决方案粘贴到 AngularJS 中,其中包含一组回调和 $apply 令人困惑和费解;但他们最终让它发挥作用!问题是,在 的大多数 情况下,jQuery 插件可以在 AngularJS 中用一小部分代码重写,突然一切都变得容易理解和直接了。

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

    3. 始终从架构的 Angular 思考

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

    那么你怎么做呢?你如何“在 AngularJS 中思考”?下面是一些与 jQuery 对比的一般原则。

    观点是“官方记录”

    在 jQuery 中,我们以编程方式更改 View 。我们可以将下拉菜单定义为 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();
    

    当我们只看 View 时,并不会立即发现这里有任何功能。对于小型应用程序,这很好。但是对于非平凡的应用程序,事情很快就会变得困惑且难以维护。

    然而,在 AngularJS 中, View 是基于 View 的功能的官方记录。我们的 ul 声明看起来像这样:

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

    这两个做同样的事情,但在 AngularJS 版本中,任何查看模板的人都知道应该发生什么。每当开发团队的新成员加入时,她可以查看此内容,然后知道有一个名为 dropdownMenu 的指令在其上运行;她不需要凭直觉得出正确答案或筛选任何代码。 View 告诉我们应该发生什么。干净多了。

    刚接触 AngularJS 的开发人员经常会问这样的问题:如何找到特定类型的所有链接并在其中添加指令。当我们回答:你没有时,开发人员总是大吃一惊。但你不这样做的原因是这就像半 jQuery、半 AngularJS,而且不好。这里的问题是开发人员试图在 AngularJS 的上下文中“执行 jQuery”。这永远不会奏效。该 View 是官方记录。在指令之外(更多内容见下文),您永远永远永远不会更改 DOM。并且在 View 中应用了指令,因此意图很明确。

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

    数据绑定(bind)

    这是迄今为止 AngularJS 最棒的功能之一,并且省去了我在上一节中提到的各种 DOM 操作的需要。 AngularJS 会自动更新你的 View ,所以你不必更新!在 jQuery 中,我们响应事件然后更新内容。就像是:

    $.ajax({
      url: '/myEndpoint.json',
      success: function ( data, status ) {
        $('ul#log').append('<li>Data Received!</li>');
      }
    });
    

    对于看起来像这样的 View :

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

    除了混合关注点之外,我们也有我之前提到的表示意图的相同问题。但更重要的是,我们必须手动引用和更新 DOM 节点。如果我们想删除一个日志条目,我们也必须针对 DOM 进行编码。除了DOM,我们如何测试逻辑?如果我们想更改演示文稿怎么办?

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

    $http( '/myEndpoint.json' ).then( function ( response ) {
        $scope.log.push( { msg: 'Data Received!' } );
    });
    

    我们的 View 可以是这样的:

    <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 警告框。而且我们永远不必更改 Controller 代码!但更重要的是,无论日志在何处或如何更新, View 也会发生变化。自动地。整洁的!

    虽然我没有在这里展示它,但数据绑定(bind)是双向的。因此,只需执行以下操作,这些日志消息也可以在 View 中进行编辑: <input ng-model="entry.msg" /> 。有很多的欣喜。

    不同的模型层

    在 jQuery 中,DOM 有点像模型。但是在 AngularJS 中,我们有一个单独的模型层,我们可以以任何我们想要的方式管理它,完全独立于 View 。这有助于上述数据绑定(bind),维护 separation of concerns ,并引入了更大的可测试性。其他答案提到了这一点,所以我就留在那里。

    关注点分离

    以上所有内容都与这个总体主题有关:将您的关注点分开。你的观点是应该发生的事情的官方记录(大部分);您的模型代表您的数据;你有一个服务层来执行可重用的任务;您进行 DOM 操作并使用指令扩充您的 View ;然后你用 Controller 把它们粘在一起。其他答案中也提到了这一点,我唯一要添加的内容与可测试性有关,我将在下面的另一部分中讨论。

    依赖注入(inject)

    帮助我们分离关注点的是 dependency injection (DI)。如果您来自服务器端语言(从 JavaPHP ),您可能已经熟悉这个概念,但是如果您是来自 jQuery 的客户端人员,那么这个概念对于时髦人士来说可能看起来从愚蠢到多余.但事实并非如此。 :-)

    从广泛的 Angular 来看,DI 意味着您可以非常自由地声明组件,然后从任何其他组件中,只需请求它的一个实例,它就会被授予。您不必知道加载顺序、文件位置或类似内容。威力可能不会立即显现,但我将仅提供一个(常见)示例:测试。

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

    说到测试...

    4. 测试驱动的开发——永远

    这实际上是关于架构的第 3 部分的一部分,但它非常重要,我将其作为自己的顶级部分。

    在您见过、使用或编写过的所有 jQuery 插件中,有多少带有随附的测试套件?不是很多,因为 jQuery 不太适合。但 AngularJS 是。

    在 jQuery 中,测试的唯一方法通常是使用示例/演示页面独立创建组件,我们的测试可以针对该页面执行 DOM 操作。那么我们必须单独开发一个组件,然后将其集成到我们的应用程序中。多么不方便!很多时候,当使用 jQuery 开发时,我们选择迭代而不是测试驱动的开发。谁能怪我们?

    但是因为我们有关注点分离,所以我们可以在 AngularJS 中迭代地进行测试驱动开发!例如,假设我们想要一个 super 简单的指令在我们的菜单中指示我们当前的路线是什么。我们可以在应用程序的 View 中声明我们想要的内容:

    <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 操作”。 这是必须的。 以应有的尊重对待它!

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

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

    AngularJS 附带了一整套工具,使这变得非常容易;使用 ngClass 我们可以动态更新类; ngModel 允许双向数据绑定(bind); ngShowngHide 以编程方式显示或隐藏元素;还有更多——包括我们自己编写的那些。换句话说,我们可以在没有 DOM 操作的情况下做各种令人敬畏的事情。 DOM 操作越少,指令越容易测试,它们越容易设计样式,它们在 future 越容易改变,它们的可重用性和可分发性越强。

    我看到许多刚接触 AngularJS 的开发人员使用指令作为抛出一堆 jQuery 的地方。换句话说,他们认为“由于我无法在 Controller 中进行 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 才能工作,jqLit​​e ( angular.element ) 将始终使用 jQuery 如果它被加载!所以我们不需要使用 $ - 我们可以只使用 angular.element
  • 第四,与第三密切相关的是,jqLit​​e 元素不需要被包裹在 $ 中 - element 传递给 0x251812231 元素的 0x251812231 元素已经是 1343
  • 第五,我们在前面的部分提到过,为什么我们要在我们的逻辑中混合模板?

  • 这个指令可以重写(即使是非常复杂的情况!)更简单的像这样:

    .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 没有做一些开箱即用的事情,想想团队将如何完成它以适应 linkngClick 等。

    概括

    甚至不要使用jQuery。甚至不要包括它。它会阻止你。当你遇到一个你认为你已经知道如何在 jQuery 中解决的问题时,在你到达 ngClass 之前,试着考虑如何在 AngularJS 的范围内解决它。不知道就问! 20 次中有 19 次,最好的方法是不需要 jQuery,而尝试用 jQuery 解决它会为您带来更多的工作。

    关于javascript - "Thinking in AngularJS"如果我有 jQuery 背景?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14994391/

    相关文章:

    javascript - 丑陋的 AngularJS

    javascript - ExpressJS res.render 不适用于 angularjs

    javascript - AngularJS路由: get template with data

    javascript - 设置 iFrame CSS 显示 :none during load

    javascript - 在 td 元素中查找文本的功能不起作用

    javascript - Dojo 的 Stateful 的 jQuery 版本?

    javascript - 我怎样才能删除这个setTimeout函数

    javascript - 如何在 AngularJS 应用程序中显示代码块

    javascript - 一旦应用程序在asp.net中正确运行,就可以防止返回

    javascript - JsHint W083 不要在循环中创建函数和 jQuery 的 $.each() 函数