为什么将JavaScript和HTML分离是一种好的实践?

5
我一直在阅读关于JavaScript的一些好的实践方法,其中之一是不侵入式JavaScript。第一点引起了我的注意:

将功能(“行为层”)与Web页面的结构/内容和呈现分离

在维基页面上,其中一个例子是应该在JS文件中绑定动作到事件,而不是在HTML中。这个例子
<input type="text" name="date" id="date" />
...
window.onload = function() {
    document.getElementById('date').onchange = validateDate;
};

被青睐于

<input type="text" name="date" onchange="validateDate()" />

然而,我认为我更喜欢第二个带有onchange属性的代码,原因如下:
  • 易于阅读,并且立即清楚在该元素上更改(或任何其他事件)会发生什么。
  • 我不必查看JavaScript文件并查找是否绑定了onchange事件以及是否为#date定义了其他事件,例如click
  • 像AngularJS这样的框架具有ng-click,并混合HTML结构和JS。为什么我不能呢?

我读过不使用非侵入式javascript的缺点:

  • 污染全局名称空间。
  • 创建冗长且难以阅读的内联代码。
  • 如果事件的代码发生更改,则只需在JS文件中更改一次即可。

但我认为这些缺点可以解决。

  • 不要污染命名空间,将应用程序创建在一个变量中,这样代码看起来就像 onchange="app.validateDate()",就不会发生污染。
  • 内联代码不会被编写,而是在JS文件中分离出一个函数,然后像这样调用它 onclick="app.action();"
  • 这不是和在onclick属性中使用函数一样吗?因为最终你只需要在两种方法中的一个函数中进行更改,无论是$('input').change(function () {/* ... change ... */});还是app.action = function () {/* ... change ... */}

那么这是否仍然被认为是良好的实践呢?


4
我们将它们分开的主要原因是为了将来更容易进行更改。您不希望更改onChange函数的名称,然后必须浏览所有HTML才能进行更改。您还希望人们在没有JavaScript的情况下能够使用您的网站,因此只在有JavaScript时绑定它们是一种好的实践。它还使您的代码更简单,因为您把每个部分分开。就像外部CSS文件一样容易,这样您可以交换文件以更改样式,交换JS文件允许您以相同的方式进行交互。 - somethinghere
1
此外,并不是所有浏览器都为所有元素实现相同的事件。例如,你可能会有特定于浏览器的实现。 - Octav Zlatior
1
最后,这也是一个好的实践,因为它是“常规做法”。其他任何开发人员要使用你的代码吗?他们知道会发生什么。这在很大程度上是一种惯例。另请参见:“第三点,代码更多地是你所谓的“指南”而不是实际规则。欢迎加入。”——巴博萨船长 - somethinghere
2
尽管我同意第二段代码更短,也许更好看,但将JavaScript与HTML分离是更好的实践。想象一下,你在一个团队中工作,只负责JavaScript编程。创建和编辑HTML文件的是其他人。还有其他人负责CSS。通过关注点的分离,每个团队都可以专注于自己的部分,而不会干涉其他人的事务。 - cezar
1
我对于AngularJS不知道该说些什么... 这是一个我从来不喜欢的特性,但你可以争辩说它实际上并不是JavaScript - 它是框架用于预解析HTML的属性。之后,猜猜看:事件是从JS代码中添加的(而不是在HTML代码中添加的):) - Octav Zlatior
显示剩余6条评论
1个回答

3

这是一个非常广泛的话题,很大程度上基于个人观点。没有一个通用答案适用于所有情况。但是,以下是一些观察结果:

  1. You are polluting the namespace whatever you do. app.validateDate pollutes the namespace just as validateDate does, just by virtue of needing to have a globally accessible function name. In complex modern sites, there are tons of scripts competing for global names. Ideally you're never exposing any name globally, not even a namespace name.

  2. .onclick = handler is not great either. You'd want:

    document.getElementById('date').addEventListener('change', function () { .. });
    

    This is even less obtrusive and allows several scripts to bind event listeners to the same element. Again, in complex modern sites one of the highest priorities you can have is to ensure nobody is stepping on anyone else's feet. You never know who else might be interested in the change event of that element in the future.

  3. It is still more code to write it inline than elsewhere. Longer HTML code that is. HTML can already be very verbose. Anything you can move elsewhere you should. Reducing the amount of code in any one particular file is an art in itself and important for readability. Yeah, it's "just one more attribute"... on top of all the other attributes and elements and inline declarations you're also not avoiding. It's just piling up, and that's how code gets messy and unreadable and unmaintainable.

  4. Reusability: document.getElementById(..).addEventListener can be written once in an external file and automagically reused across many different pages. <.. onclick=".."> needs to be repeatedly written every single time. DRY your code.

对于小型项目,这并不重要。但是,网站变得越来越复杂。业务需求需要不断变化。只需再添加一个分析脚本,再添加一个基于Javascript的社交小工具,现在将所有内容更改回去,现在保持所有依赖项版本同步,现在彻底删除并重新设计我们下周的2.0发布。在这样的环境中,每一点解耦和间接性和灵活性都有所帮助。
既然你提到了Angular:
Angular通过采用完全不同的模板解析模型来避免其中一些问题。当您编写 onclick = foo 时,需要绑定到全局函数名称。但是,当Angular执行 ng-click = foo 时, foo 是ng-scope中的局部作用域实体。它不是全局名称。Angular在控制器和视图之间进行区分,其中控制器基本上在 $ scope 对象上公开特定API,视图可以使用该API; 只要保持指定的API合同(也就是只要 $ scope 对象保持相同的属性),则控制器和视图仍然是可互换的。
所有 ng 指令都针对自定义作用域和评估引擎进行评估,该引擎与Javascript的默认操作方式几乎没有关系。

我意识到这是一个广泛的话题,我并不是在寻找一个答案,而是想听听其他人的意见,并实际看到一些为什么不应该使用HTML属性的原因。你在回答中提到了很好的观点,所以感谢你花时间解释! - leopik

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