假设我熟悉在jQuery中开发客户端应用程序,但现在我想开始使用AngularJS.你能描述一下必要的范式转变吗?以下几个问题可能会帮助你找到答案:

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

我不想详细比较jQueryAngularJS.

推荐答案

1. Don't design your page, and then change it with DOM manipulations

在jQuery中,您设计一个页面,然后使其动态化.这是因为jQuery是为增强而设计的,并且从这个简单的前提发展而来.

但是在AngularJS中,您必须从头开始考虑您的体系 struct .不要一开始就想"我有这块DOM,我想让它做X",您必须从您想要完成的事情开始,然后开始设计您的应用程序,最后开始设计您的视图.

2. Don't augment jQuery with AngularJS

类似地,不要从jQuery执行X、Y和Z的 idea 开始,所以我将在模型和控制器的基础上添加AngularJS.当你刚开始的时候,这是非常诱人的,这就是为什么我总是建议新的AngularJS开发人员不要使用jQuery,至少在他们习惯用"Angular 的方式"做事情之前.

我在这里和邮件列表上看到许多开发人员使用150或200行代码的jQuery插件创建了这些精心设计的解决方案,然后将这些插件粘到AngularJS中,其中包含一组回调和$apply个令人困惑和费解的代码;但他们最终还是成功了!问题是,在most种情况下,jQuery插件可以在一小部分代码中用AngularJS重写,突然之间,一切都变得易懂和简单.

底线是:解决问题时,首先要"用AngularJS思考";如果你想不出一个解决方案,问问社区;如果在所有这些之后没有简单的解决方案,那么就可以自由地使用jQuery.但不要让jQuery成为拐杖,否则你永远都无法掌握AngularJS.

3. Always think in terms of architecture

首先要知道single-page applications等于applications.它们是not个网页.因此,我们需要像服务器端开发人员in addition那样思考,也需要像客户端开发人员那样思考.我们必须考虑如何将我们的应用程序划分为单独的、可扩展的、可测试的组件.

那么你会这么做吗?你如何"用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版本中,任何查看模板的人都知道应该发生什么.每当开发团队的新成员加入时,她都可以看到这一点,然后know有一个名为dropdownMenu的指令在其上运行;她不需要凭直觉找到正确答案,也不需要筛选任何代码.景色告诉了我们应该发生什么.干净多了.

刚接触AngularJS的开发人员经常会问这样一个问题:如何找到特定类型的所有链接并向其添加指令.当我们回答:你不这样做的时候,开发人员总是目瞪口呆.但你不这样做的原因是,这就像半个jQuery,半个AngularJS,没有什么好处.这里的问题是,开发人员试图在AngularJS的上下文中"执行jQuery".这永远不会奏效.该观点与官方记录一致.在指令之外(下文将对此进行详细介绍),您永远不会更改DOM.指令被应用in the view,所以意图是明确的.

记住:不要设计,然后再做标记.你必须先设计,然后再设计.

数据绑定

到目前为止,这是AngularJS最棒的特性之一,并且省go 了我在上一节提到的各种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 node .如果我们想要删除日志(log)条目,我们也必须针对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 alert 框.而且我们从来不需要更改控制器代码!但更重要的是,无论日志(log)更新到wherehow,视图也会改变.自动地整洁的

虽然我没有在这里展示,但数据绑定是双向的.因此,这些日志(log)消息也可以在视图中编辑,只需执行以下操作:<input ng-model="entry.msg" />.大家都很高兴.

不同的模型层

在jQuery中,DOM有点像模型.但是在AngularJS中,我们有一个单独的模型层,可以以任何我们想要的方式进行管理,完全独立于视图.这有助于上述数据绑定,保持了separation of concerns,并引入了更好的可测试性.其他的回答都提到了这一点,所以我就到此为止了.

关注点分离

以上所有这些都与这个总体主题相联系:让你们的关注点分开.您的视图充当应该发生的事情的官方记录(在很大程度上);您的模型表示您的数据;您有一个服务层来执行可重用的任务;您执行DOM操作并使用指令增强您的视图;并且您使用控制器将其粘合在一起.在其他答案中也提到了这一点,我要添加的唯一一件事与可测试性有关,我将在下面的另一节中讨论这一点.

依赖项注入

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

从广泛的Angular 来看,DI意味着您可以非常自由地声明组件,然后从任何其他组件,只要请求它的一个实例,它就会被授予.您不必知道加载顺序、文件位置或诸如此类的信息.这种能力可能不会立即显现出来,但我只提供一个(常见的)例子:测试.

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

说到测试...

4. Test-driven development - always

这实际上是关于架构的第3节的一部分,但是它太重要了,所以我把它作为它自己的顶级部分.

在您看到、使用或编写的众多jQuery插件中,有多少有附带的测试套件?不是很多,因为jQuery对此不太适应.但是AngularJS是.

在jQuery中,测试的唯一方法通常是使用示例/演示页面独立创建组件,我们的测试可以根据该页面执行DOM操作.因此,我们必须单独开发一个组件,并将其集成到我们的应用程序中.太不方便了!很多时候,当使用jQuery开发时,我们 Select 迭代开发,而不是测试驱动的开发.谁又能责怪我们呢?

但是因为我们有分离的关注点,我们可以在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' );
                }
            });
        }
    };
});

我们的测试现在超过了and,我们的菜单按要求执行.我们的开发是both迭代and测试驱动的.太棒了-太酷了.

5. Conceptually, directives are not packaged jQuery

您会经常听到"只在指令中执行DOM操作".This is a necessity.以应有的尊重对待它!

不过,让我们再潜得更深一点.

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

AngularJS提供了一整套工具,使之非常容易;使用ngClass,我们可以动态更新类;ngModel允许双向数据绑定;ngShowngHide以编程方式显示或隐藏元素;还有更多——包括我们自己写的那些.换句话说,我们可以做各种令人惊叹的操作.DOM操作越少,测试指令就越容易,样式就越容易,将来就越容易更改,它们的可重用性和可分发性也就越强.

我看到很多刚接触AngularJS的开发人员使用指令作为抛出大量jQuery的地方.换句话说,他们认为"既然我不能在控制器中进行DOM操作,我就把代码放在指令中".虽然这当然好得多,但通常是still wrong.

想想我们在第3节编程的记录器.即使我们把它放在指令中,我们也希望以"Angular "的方式来做.它不需要任何DOM操作!有很多时候DOM操作是必要的,但这比你想象的要少!在应用程序中执行DOM操作anywhere之前,问问自己是否真的需要.也许有更好的办法.

这里有一个快速示例,它显示了我最常看到的模式.我们想要一个可移动的按钮.(注意:这个示例有点做作,非常冗长,用来表示以完全相同的方式解决的更复杂的 case .)

.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 was,jqLite(angular.element)也将always使用jQuery(如果它已加载)!所以我们不需要用$,我们可以用angular.element.
  4. 第四,与第三个密切相关的是,jqLite元素不需要包装在$中——传递给link函数的element将是一个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;
            };
        }
    };
});

同样,模板的东西在模板中,所以你(或你的用户)可以很容易地把它换成一个符合任何必要风格的,而且logic个从来都不用碰.可重用性-轰!

还有其他的好处,比如测试——很简单!无论模板中有什么,指令的内部API都不会被触及,因此重构很容易.您可以随意更改模板,而无需触碰指令.不管你做了什么改变,你的测试还是通过了.

w00t!

那么,如果指令不仅仅是类似jQuery的函数的集合,它们是什么呢?指令实际上是extensions of HTML个.如果HTML不能完成你需要它做的事情,你可以编写一个指令来完成它,然后像使用HTML一样使用它.

换句话说,如果AngularJS没有做一些开箱即用的事情,想一想团队将如何完成它,以适应ngClickngClass等.

Summary

甚至不要使用jQuery.甚至都不要包括在内.它会阻碍你前进.当你遇到一个你认为自己已经知道如何在jQuery中解决的问题时,在你达到$之前,试着想想如何在AngularJS的范围内解决它.如果你不知道,问吧!20次中有19次,最好的方法不需要jQuery,而try 用jQuery解决它会为您带来更多的工作.

Javascript相关问答推荐

如何在react-Router-dom 6中Forking 路由?

为什么子组件没有在reaction中渲染?

如何使用Paged.js仅渲染特定页面

在JavaScript中对大型级联数组进行切片的最有效方法?

为什么我的includes声明需要整个字符串?

未捕获错误:[]:getActivePinia()被调用,但没有活动Pinia.🍍""在调用app.use(pinia)之前,您是否try 使用store ?""

如何在输入元素中附加一个属性为checkbox?

如何解决useState错误—setSelect Image不是函数''

保持物品顺序的可变大小物品分配到平衡组的算法

在浏览器中触发插入事件时检索编码值的能力

在css中放置所需 colored颜色 以填充图像的透明区域

Reaction Redux&Quot;在派单错误中检测到状态Mutations

搜索功能不是在分页的每一页上进行搜索

我想使用GAS和HTML将从Electron 表格中获得的信息插入到文本字段的初始值中

有没有一种直接的方法可以深度嵌套在一个JavaScript对象中?

使用RxJS from Event和@ViewChild vs KeyUp事件和RxJS主题更改输入字段值

如何在脚本编译后直接将RxJ模块导入浏览器(无需Angel、webpack、LiteServer)

Pevent触发material 用户界面数据网格中的自动保存

select 2-删除js插入的项目将其保留为选项

在Press Reaction本机和EXPO av上播放单个文件