当前在互联网上,任何一个稍微复杂的网站或者应用程序都会包含许多HTML、CSS和JavaScript。随着互联网运用的发展以及咱们对它的依赖性日益增长,设定一个关于组织和维护你的前端代码的计划是绝对须要的。javascript
当今的一些大型互联网公司,因为愈来愈多的人会接触到日益增长的前端代码,它们会试图去坚持代码的模块化。这样更改程序的部分代码,并不会无心中过多地影响后续不相关部分的执行过程。css
防止意想不到的后果不是一个容易解决的问题,尤为是HTML,CSS和JavaScript本质上是相互依赖的。更糟糕的是,当涉及到前端代码时,一些传统计算机科学原则,好比关注分离,这一长期运用在服务端开发中,不多会讨论到。html
在本文中,我将会讲讲我所学到的如何去解耦个人HTML,CSS和JavaScript代码。从我的以及他人经验所得,这种的最好办法并非那么显而易见,而一般是不直观的,并且有时还会与许多所谓的最佳实践相违背。前端
HTML,CSS和JavaScript之间总会存在耦合关联。无论怎样,这些技术与生俱来就是要和其它进行交互。举个例子,一种飞闪转换效果可能会在样式表中用带有类选择器定义,但它常常由HTML初始化,并经过用户交互,如编写JavaScript,来触发。因为前端代码的有些耦合是不可避免的,你的目标就不该该是简单地消除之间的耦合,而应该是减小代码间没必要要的依赖耦合关系。一个后端开发者应该可以对HTML模板中的标记进行更改,而无需担忧意外破坏CSS规则或者一些JavaScript功能。因为当今的web团队日渐增大且专业化,这个目标比以往更甚。java
前端代码的紧耦合现象并不老是很明显。事实上复杂的是,一方面看起来彷佛松耦合,但从另外一方面则是紧耦合。如下是我曾经屡次作过或者看过,以及吸收个人过错中,总结的全部的反模式。对每个模式,我会尝试去解释为什么耦合这么糟糕,而且指出如何去避免它。web
过分复杂的选择器编程
CSS Zen Garden向世界展现了你能够彻底改变整个网站的外观而无需更改任意一个的HTML标记。这是语义网运动的典型表明,主要原则之一就是去避免使用表象类。乍一看,CSS Zen Garden可能看起来像是一个很好的解耦合例子,毕竟,把样式从标记语言中分离出来是它的重点所在。可是,若按照这样作,问题就来了,你会常常须要在你的样式表里有这样的选择器,以下:后端
#sidebar section:first-child h3 + p { }
CSS Zen Garden中,虽然HTML几乎与CSS彻底分离,但CSS会强耦合到HTML中去,此时就须要你对标记语言的结构有深层次的理解。这可能看起来彷佛并非很糟糕,尤为是某人维护着CSS,同时须要维护HTML,但一旦你增长了许多人手进去,这种状况就变得没法控制了。若是某个开发者在某种状况下在第一个<section>前增长了<div>,上面的规则就没法生效,然而他也不清楚其中原因。浏览器
只要你网站的标记不多改动,CSS Zen Garden就是一个很是不错的主意。可是这和当今的Web应用不尽然都是这种状况。与冗长而又复杂的CSS选择器相比,最好的办法是在可视化组件自己的根元素增长一个或多个类选择器。好比,若是侧边栏有子菜单,只须要为每一个子菜单元素增长submenu类选择器,而不要用这样的形式:ide
ul.sidebar > li > ul { /* submenu styles */ }
这种方式的结果是在HTML中须要更多的类选择器,但从长远来看,这又下降了耦合度,以及让代码更可重用和可维护,而且还能让你的标记自文档化。若是HTML里没有类选择器,那些对CSS不熟悉的开发者就不清楚HTML的改动如何影响了其它代码。另外一方面,在HTML中使用类选择器能很清晰地看到那些样式或者功能被使用到了。
一个类选择器每每是用来同时做为样式和JavaScript的钩子。虽然这看起来彷佛很节约(由于至少减小了一个类标记),但事实上,这是把元素的表现和功能耦合起来了。
<button class="add-item">Add to Cart</button>
以上例子描述了一个带有add-item类样式的”添加到购物车”按钮。
若是开发者想为此元素添加一个单击事件监听器,用已经存在的类选择器做为钩子很是的容易。个人意思是,既然已经存在了一个,为什么要添加另外一个呢? 可是想一想看,有不少像这样的按钮,遍及了整个网站,都调用了相同的JavaScript功能。再想一想看,若是市场团队想要其中一个和其它看起来彻底不一样但功能相同的按钮呢。也许这样就须要更多显著的色彩了。
问题就来了,由于监听单击事件的JavaScript代码但愿add-item类选择器被使用到,可是你新的按钮又没法使用这个样式(或者它必须清除全部声明的,而后再重置新的样式)。还有,若是你测试的代码同时也但愿使用add-item类选择器,那么你不得不要去更新那么代码用到的地方。更糟糕的是,若是这个”添加到购物车”功能不只仅是当前应用用到的话,也就是说,把这份代码抽象出来做为一个独立的模块,那么即便一个简单的样式修改,可能会在彻底不一样的应用中引起问题。
使用javaScript钩子最好的(事实上也是比较鼓励的)作法是,若是你须要这么作,使用一种方式来避免样式和行为类选择器之间的耦合。
个人我的建议是让JavaScript钩子使用前缀,好比:js-*。这样的话,当开发者在HTML源代码中看到这样的类选择器,他就彻底明白个中缘由了。因此,上述的”添加到购物车”的例子能够重写成这样:
<button class="js-add-to-cart add-item">Add to Cart</button>
如今,若是须要一个看起来不一样的按钮,你能够很简单地修改下样式类选择器,而无论行为的类选择器。
<button class="js-add-to-cart add-item-special">Add to Cart</button>
JavaScript能用类选择器去DOM中查找元素,一样,它也能经过增长或移除类选择器来改变元素的样式。但若是这些类选择器和当初加载页面时不一样的话也会有问题。当JavaScript代码使用太多的组成样式操做时,那些CSS开发者就会轻易去改变样式表,殊不知道破坏了关键功能。也并非说,JavaScript不该该在用户交互以后改变可视化组件的外观,而是若是这么作,就应该使用一种一致的接口,应该使用和默认样式不一致的类选择器。
和js-*前缀的类选择器相似,我推荐使用is-*前缀的类选择器来定义那些要改变可视化组件的状态,这样的CSS规则能够像这样:
.pop-up.is-visible { }
注意到状态类选择器(is-visible)是链接在组件类选择器(pop-up)后,这很重要。由于状态规则是描述一个的状态,不该该单独列出。如此不一样就能够用来区分更多和默认组件样式不一样的状态样式。
另外,可让咱们能够编写测试场景来保证像is-*这样的前缀约定是否听从。一种测试这些规则的方式是使用CSSLint和HTML Inspector。
更多关于特定状态类选择能够查阅Jonathan Snnok编写的很是优秀的SMACSS书籍。
jQuery和新的API,像document.querySelectorAll,让用户很是简单地经过一种他们已经很是熟悉的语言–CSS选择器来查找DOM中的元素。虽然如此强大,但一样有CSS选择器已经存在的相同的问题。JavaScript选择器不该过分依赖于DOM结构。这样的选择器很是慢,而且须要更深刻认识HTML知识。
就第一个例子来说,负责HTML模板的开发者应该能在标记上作基本的改动,而不需担忧破坏基本的功能。若是有个功能会被破坏,那么它就应该在标记上显而易见。
我已经说起到应该用js-*前缀的类选择器来表示JavaScript钩子。另外针对消除样式和功能类选择器之间的二义性,须要在标记中表达出来。当某人编写HTML看到js-*前缀的类选择器时,他就会明白这是别有用途的。但若是JavaScript代码使用特定的标记结构查找元素时,正在触发的功能在标记上就不那么明显了。
为了不使用冗长而又复杂的选择器遍历DOM,坚持使用单一的类或者ID选择器。 考虑如下代码:
var saveBtn = document.querySelector("#modal div:last-child > button:last-child")
这么长的选择器是能够节省你在HTML中添加一个类选择器,但一样让你的代码对于标记更改很是容易受到影响。若是设计者忽然决定要把保持按钮放在左边,而让取消按钮放在右边,这样的选择器就再也不匹配了。
一个更好的方式(使用上述的前缀方法)是仅仅使用类选择器。
var saveBtn = document.querySelector(".js-save-btn")
如今标记能够更改它想改的,而且只要类选择仍是在正确的元素上,一切都会很正常。
使用合适的类选择器以及可预测的类名约定能够减小几乎每一种HTML,CSS和JavaScript之间的耦合。起初因为为了展示HTML须要知道不少类选择器的名称,这种在标记中使用不少类选择器看起来像是强耦合的迹象。可是我发觉,使用类选择器和传统编程设计中的事件或者观察者模式很是类似。在事件驱动编程中,为了避免直接在对象A上调用对象B,而是对象A简单地在提供的环境中发布一个特定的事件,而后对象B可以订阅那个事件。这样,对象B就不须要知道任何关于对象A的接口,而仅仅须要知道监听什么事件。按理说,事件系统须要某种形式上的耦合,由于对象B须要知道订阅的事件名称,但和对象A须要知道对象B的公共方法相比,这已经更松散的耦合了。
HTML类选择器都很是类似。与CSS文件中定义复杂的选择器(就像HTML的内部接口同样)不一样的是,它能够经过单一类选择器简单定义一个可视化组件的外观。CSS文件不须要关心HTML对类选择器的使用与否。一样,JavaScript不用那些须要更深刻理解HTML结构的复杂DOM遍历功能,而是仅仅监听与类名一致的元素的用户交互。类选择器应该像是胶水同样,把HTML,CSS和JavaScript链接在一块儿。从我的经验得知,它们也是最容易以及最好的方式把三者技术链接起来,而不是混合过分。
网页超文本技术工做小组(WHATWG)正在致力于web组件的规范,能让开发者把HTML,CSS和JavaScript绑定一块儿做为一个单独的组件或者模块,并与其它的页面元素进行交互封装。若是这个规范已经在大多数的浏览器中实现的话,那么我在本文中提供的不少建议就变得不那么重要了(由于代码和谁交互变得很清晰);可是不管如何,理解这些更普遍的原则以及为什么须要它们仍然很重要。即便这些实践在Web组件时代会变得不那么重要,但其中的理论仍然适用。在大型团队和大型应用中的实践仍然要适用于小模块的编写中,反之则不须要。
可维护的HTML,CSS和JavaScript的标志是每一个开发者能够容易而且很自信地编写代码库的每一个部分,而不需担忧这些修改会无心中影响到其它不相关部分。阻止这样意想不到的后果的最佳方式之一是,经过一组可以表达其义的,任何开发者碰到时能想出它的用途的,可预测的人性化的类选择器名,把这三者技术结合在一块儿。
为避免上述的反模式,请把下述的原则谨记于心:
在HTML中这样运用类选择器常常会须要不少须要表现的类选择器,但获取的是可预见性和可维护性,这点值得确定。毕竟,为HTML增长类选择器是至关容易的,不须要开发者有多少技能。摘自Nicolas Gallagher的原话:
当你要寻找一种方式来减小花费在编写和修改CSS的时间上来制做HTML和CSS时,这就涉及到你必须接受若是你想更改样式,你是不想花费更多时间去更改HTML元素上的类选择器。这对前端和后端开发者都有必定的实用性,任何人均可以从新安排预构建的乐高积木。这样没有人会去展现CSS的魔力了。
原文连接: Philip Walton 翻译: 伯乐在线 - 蝈蝈