Angular在HTML属性中使用内联JavaScript不是“不良实践”吗?

51

我阅读 Angular 教程时,很喜欢其中的许多内容,但是 "ng-click" 不是相当于内联 onClick 吗?我的理解是 JavaScript 社区已经确定在 HTML 中使用内联 JavaScript 事件处理程序是“不良实践”,对吗?

<img ng-src="{{img}}" ng-click="setImage(img)">

了解为什么在使用 Angular 时这不再被视为“不正确”将会很有帮助。

来源:http://docs.angularjs.org/tutorial/step_10


我个人不喜欢任何内联内容,并且会避免使用这样的内容,尽管这是一种观点问题。理论上,如果某人使用内联,只要代码仍然有效,我想某人可以使用它。如果是我的话,我会避免使用任何HTML定义的内联事件。 - sajawikio
我从未使用过AngularJS,但它看起来像一个模板框架,所以我需要更多的了解才能确定。如果它在HTML上呈现,它可能根本没有“ng-click”属性,你确定它真的是内联的吗?还是它创建了一个JS,在生成模板时绑定事件?虽然我不太熟悉AngularJS,但它可能只是在内联事件中执行操作,但据说它为您执行模板化,这可能是为什么人们想要使用它的原因(尽管个人认为我不喜欢那个库,再次强调,这是一个意见问题,所以人们可能会对这个问题投反对票)。 - sajawikio
3
实际上它并不是内联的。将属性内容设置为“setImage(img)”实际上不会执行绑定。解析模板的脚本会执行绑定。 - nikola
非常相似的问题:https://dev59.com/MWYq5IYBdhLWcg3woB0W - Mark Rajcok
1
AngularJS(和ReactJS)是侵入式的JavaScript框架,将静态数据层(HTML)与处理该数据所需的逻辑(JavaScript)耦合在一起。这是不好的做法。实际上,您正在将开发人员锁定到Google(或Facebook)产品中。例如:您花了2年时间编写一个AngularJS应用程序,现在想转移到ReactJS,因为它具有更快的性能。如果您将逻辑与数据解耦,您可以重用HTML模板。但是,现在您被困在继续使用AngularJS或使用ReactJS重新编写前端的境地中。 - tim-montague
1个回答

60

实际上,一切都归结于你的视图代码必须以某种方式与应用程序逻辑相连接。 AngularJS 的最佳实践通常建议您编写自己的模型——代表业务领域的对象——并将它们附加到作用域上。想象一下类似这样的代码:

<img ng-src="{{img}}" ng-click="myProfile.setMainImage(img)">

myApp.controller("ProfileController", function($scope, myProfile) {
  $scope.myProfile = myProfile;
});

这段文本涉及编程相关内容,主要讲述了点击图片时会调用myProfile对象中的setMainImage()方法来实现业务逻辑,而视图只是一个钩子。在传统的jQuery设置中,需要编写类似以下的代码:
$("#someId img").on('click', function() {
  var img = $(this).attr('src');
  myProfile.setMainImage(img); // where does myProfile come from here?
                               // how does it update the view?
});

当然,JavaScript社区已经确定以这种方式编写大型应用程序并不可行,部分原因是视图和模型对象之间存在断开(如代码片段中的注释所示),这就是为什么我们首先有了像Angular这样的框架的原因。
因此,我们知道这个原生jQuery代码并不理想,但我们仍然不确定整个ngClick的问题。让我们将其与另一个非常流行的提供MV *体系结构的JavaScript框架Backbone进行比较。在关于AngularJS的最近的RailsCasts剧集中,某人提出了一个非常相似的问题:

是我自己的问题还是AngularJS看起来不太好?Ryan,别误会,这一集很棒,但是我对这个框架并不信服。

所有的ng-showng-repeatng-class看起来像旧的Java JSF和类似的框架。它还通过ng-submitng-click强制执行显眼的JS。

所以我的观点是:你的视图很容易变得混乱,并且完全依赖于它。其他框架(如Backbone)的优点在于展示和行为之间有关注点分离(更少或没有依赖项),以及结构化的客户端应用程序(MVVM)。

我的回复在这里也适用:

In a framework like Backbone, you'd have something like the following code (taken from the Backbone website, minus a few lines):

var DocumentView = Backbone.View.extend({

  events: {
    "dblclick"                : "open",
    "click .icon.doc"         : "select",
    "contextmenu .icon.doc"   : "showMenu",
    "click .show_notes"       : "toggleNotes",
    "click .title .lock"      : "editAccessLevel",
    "mouseover .title .date"  : "showTooltip"
  },

  open: function() {
    window.open(this.model.get("viewer_url"));
  },

  select: function() {
    this.model.set({selected: true});
  },

});

In this object which is a view, you are setting up event handlers on various elements. These event handlers call functions on the view object, which delegate to models. You also set up callbacks on various model events (such as change) which in turn call functions on the view object to update the view accordingly.

In Angular, the DOM is your view. When using ng-click, ng-submit, etc., you are setting up event handlers on these elements, which call functions that should delegate to model objects. When using ng-show, ng-repeat, etc. you are setting up callbacks on model events that change the view.

The fact that AngularJS sets up these [hooks and] callbacks behind the scenes for you is irrelevant; the only difference between this and something like Backbone is that Angular lets you write your view declaratively--you describe what your view is--rather than imperatively--describing what your view does.

So, in the end, <a ng-click="model.set({selected: true})"> really adds no more dependencies than

events: {
  'click a': 'select'
},

select: function() {
  this.model.set({selected: true});
}

...but it sure is a hell of a lot less code. ;)

(注:实际上,Angular版本应该是<a ng-click="select()">,并且作用域中的select方法类似于Backbone示例中视图中的select方法。)
现在,也许一个“合理”的担忧是你不喜欢在你的标记中使用事件挂钩。就个人而言,我非常喜欢Angular视图的声明性本质,其中你的标记描述了视图是什么,并且你有两种绑定方式来处理事件(无论是用户生成的还是模型中的更改)和你的视图——我发现我编写的连接事件的样板代码要少得多(特别是由模型变化驱动的视图变化),而且我认为这更容易推理视图。

14
我感觉使用Angular的声明式事件定义更加安全的另一个原因是,一个列出ng-click方法的清单通常位于该视图控制器的$scope上,而在普通HTML/JS中使用内联onClick则是全局方法调用。 - checketts
4
据我看来,作用域是Angular可行的原因,而内联onclick则不好。 - Guillaume86
2
我可能因为这样说而受到一些指责,但是当你有另一个想要重复使用代码的项目时会发生什么?现在你必须进入每个项目并添加ng-click="model.set({selected: true})到你想要使用它的每个地方。如果你想要改变东西会发生什么,现在你必须编辑HTML来更改JavaScript,而不是只编辑行为作为单独问题。我想全力支持Angular,但我仍然担心它过度依赖HTML。 - Chris
6
如果您需要将同一事件绑定到页面上的多个元素,则需要在标记中分别将 ng-click 附加到它们所有元素中。我感觉过去5年里我学习了像这样的内联javascript(不要试图说它不是-您实际上正在从HTML元素定义中调用方法 select())是不好的做法,只是为了被告知“只要是JavaScript框架鼓励使用就可以”。这感觉很奇怪。关注点分离是怎么了? - nzifnab
2
@nzifnab 如果使用Backbone示例,如果每个链接都不应该触发“select”事件怎么办?您可以向模板和选择器添加类,但是这样很容易在不知情的情况下更改模板中的类,而不知道您已经破坏了视图。您可以添加其他“data-”属性,但那与在想要添加新链接时必须复制粘贴“ng-click”有什么区别吗? ng-click等是对非全局对象($scope)上的方法的绑定,以明显和声明性的方式编写。(通过指令也可以帮助复制/粘贴问题。) - Michelle Tilley
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接