현재 인터넷상의 약간 복잡한 웹사이트나 애플리케이션에는 HTML, CSS 및 JavaScript가 많이 포함되어 있습니다. 인터넷 사용이 발전하고 인터넷에 대한 의존도가 증가함에 따라 프런트 엔드 코드를 구성하고 유지 관리하기 위한 계획을 세우는 것이 반드시 필요합니다.
오늘날 일부 대형 인터넷 기업에서는 점점 더 많은 사람들이 프런트엔드 코드에 노출됨에 따라 코드의 모듈성을 고수하려고 노력할 것입니다. 이런 방식으로 프로그램 코드의 일부를 변경해도 관련되지 않은 후속 부분의 실행에 의도치 않게 많은 영향을 미치지 않습니다.
의도하지 않은 결과를 방지하는 것은 해결하기 쉬운 문제가 아닙니다. 특히 HTML, CSS 및 JavaScript는 본질적으로 상호 의존적이기 때문입니다. 설상가상으로, 프런트 엔드 코드의 경우 서버 측 개발에서 오랫동안 사용되었던 관심사 분리와 같은 일부 전통적인 컴퓨터 과학 원칙이 거의 논의되지 않습니다.
이 기사에서는 HTML, CSS 및 JavaScript 코드를 분리하는 방법에 대해 배운 내용에 대해 이야기하겠습니다. 개인 및 다른 사람들의 경험에 따르면 이를 수행하는 가장 좋은 방법은 명확하지 않고 종종 직관적이지 않으며 때로는 소위 많은 모범 사례에 어긋납니다.
타겟
HTML, CSS 및 JavaScript 간에는 항상 결합이 있습니다. 그럼에도 불구하고 이러한 기술은 본질적으로 서로 상호 작용하도록 설계되었습니다. 예를 들어 플래시 전환 효과는 클래스 선택기가 있는 스타일시트에 정의될 수 있지만 HTML에 의해 초기화되고 JavaScript 작성과 같은 사용자 상호 작용에 의해 트리거되는 경우가 많습니다. 프런트 엔드 코드의 일부 결합은 불가피하므로 단순히 결합을 제거하는 것이 아니라 코드 간의 불필요한 종속성 결합을 줄이는 것이 목표여야 합니다. 백엔드 개발자는 실수로 CSS 규칙이나 일부 JavaScript 기능을 위반할 염려 없이 HTML 템플릿의 마크업을 변경할 수 있어야 합니다. 오늘날의 웹 팀이 더 크고 전문화되면서 이 목표는 그 어느 때보다 커졌습니다.
안티 패턴
프런트 엔드 코드의 긴밀한 결합이 항상 명확하지는 않습니다. 실제로 복잡한 것은 한편으로는 느슨한 결합처럼 보이는 것이 다른 한편으로는 단단하게 결합되어 있다는 것입니다. 여기에 내가 여러 번 보았거나 보고 실수를 통해 배운 모든 안티 패턴이 있습니다. 각 패턴에 대해 결합이 왜 그렇게 나쁜지 설명하고 이를 방지하는 방법을 지적하려고 노력할 것입니다.
지나치게 복잡한 선택기
CSS Zen Garden은 HTML 태그 하나만 변경하지 않고도 전체 웹사이트의 모양을 완전히 바꿀 수 있음을 세상에 보여주었습니다. 이는 프레젠테이션 클래스 사용을 피하는 것이 주요 원칙 중 하나인 시맨틱 웹 운동의 전형입니다. 언뜻 보면 CSS Zen Garden은 분리의 좋은 예처럼 보일 수 있습니다. 결국 스타일과 마크업 언어를 분리하는 것이 핵심입니다. 그러나 이렇게 하면 문제가 발생합니다. 스타일 시트에 다음과 같은 선택기가 필요한 경우가 많습니다.
#sidebar section:first-child h3 + p { }
CSS Zen Garden에서는 HTML이 CSS와 거의 완전히 분리되어 있지만, , CSS는 HTML과 강력하게 결합되므로 마크업 언어의 구조에 대한 깊은 이해가 필요합니다. 이는 나쁜 일처럼 보이지 않을 수 있습니다. 특히 누군가가 CSS를 유지하고 HTML도 유지해야 하는 경우에는 더욱 그렇습니다. 그러나 일단 여기에 많은 사람을 추가하면 상황은 관리하기 어려워집니다. 개발자가 특정 상황에서 첫 번째 d8590c8c280921e23bec1a50b551c651 앞에 dc6dce4a544fdca2df29d5ac0ea9906b를 추가하면 위의 규칙이 적용되지 않지만 그 이유는 알 수 없습니다.
사이트의 마크업이 거의 변경되지 않는 한 CSS Zen Garden은 매우 좋은 아이디어입니다. 그러나 오늘날의 웹 애플리케이션에서는 반드시 그런 것은 아닙니다. 길고 복잡한 CSS 선택기와 비교할 때 가장 좋은 방법은 시각적 구성 요소 자체의 루트 요소에 하나 이상의 클래스 선택기를 추가하는 것입니다. 예를 들어 사이드바에 하위 메뉴가 있는 경우 다음 형식을 사용하는 대신 각 하위 메뉴 요소에 대해 하위 메뉴 클래스 선택기만 추가하면 됩니다.
ul.sidebar > li > ul { /* submenu styles */ }
이 접근 방식의 결과는 HTML 클래스에 더 많은 요소가 필요하다는 것입니다. 그러나 장기적으로 이는 결합을 줄이고 코드를 더 재사용 가능하고 유지 관리하기 쉽게 만들고 마크업을 자체 문서화하게 만듭니다. HTML에 클래스 선택기가 없으면 CSS에 익숙하지 않은 개발자는 HTML 변경 사항이 다른 코드에 어떤 영향을 미치는지 알 수 없습니다. 반면에 HTML에서 클래스 선택기를 사용하면 어떤 스타일이나 기능이 사용되고 있는지 매우 명확해집니다.
여러 클래스 선택자의 책임
클래스 선택자는 스타일 및 JavaScript 후크로 사용되는 경우가 많습니다. 이는 적어도 하나의 클래스 태그가 줄어들기 때문에 경제적으로 보일 수 있지만 실제로는 요소의 성능과 기능을 결합합니다.
<button class="add-item">Add to Cart</button>
위의 예에서는 항목 추가 클래스 스타일의 "장바구니에 추가" 버튼을 설명합니다.
如果开发者想为此元素添加一个单击事件监听器,用已经存在的类选择器作为钩子非常的容易。我的意思是,既然已经存在了一个,为何要添加另一个呢? 但是想想看,有很多像这样的按钮,遍布了整个网站,都调用了相同的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更多的样式操作
JavaScript能用类选择器去DOM中查找元素,同样,它也能通过增加或移除类选择器来改变元素的样式。但如果这些类选择器和当初加载页面时不同的话也会有问题。当JavaScript代码使用太多的组成样式操作时,那些CSS开发者就会轻易去改变样式表,却不知道破坏了关键功能。也并不是说,JavaScript不应该在用户交互之后改变可视化组件的外观,而是如果这么做,就应该使用一种一致的接口,应该使用和默认样式不一致的类选择器。
和js-*前缀的类选择器类似,我推荐使用is-*前缀的类选择器来定义那些要改变可视化组件的状态,这样的CSS规则可以像这样:
.pop-up.is-visible { }
注意到状态类选择器(is-visible)是连接在组件类选择器(pop-up)后,这很重要。因为状态规则是描述一个的状态,不应该单独列出。如此不同就可以用来区分更多和默认组件样式不同的状态样式。
另外,可以让我们可以编写测试场景来保证像is-*这样的前缀约定是否遵从。一种测试这些规则的方式是使用CSSLint和HTML Inspector。
更多关于特定状态类选择可以查阅Jonathan Snnok编写的非常优秀的SMACSS书籍。
JavaScript”选择器”
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가 구독할 이벤트 이름을 알아야 하기 때문에 이벤트 시스템에 어떤 형태의 결합이 필요한 것은 당연합니다. 그러나 객체 B의 공개 메서드를 알아야 하는 객체 A와 비교할 때 이는 이미 더 느슨한 결합입니다.
HTML 클래스 선택기는 모두 매우 유사합니다. CSS 파일에서 복잡한 선택기를 정의하는 것과는 달리(HTML의 내부 인터페이스와 마찬가지로) 단일 클래스 선택기를 통해 시각적 구성 요소의 모양을 간단하게 정의하는 것이 가능합니다. CSS 파일은 HTML이 클래스 선택기를 사용하는지 여부를 신경 쓸 필요가 없습니다. 마찬가지로 JavaScript는 HTML 구조에 대한 깊은 이해가 필요한 복잡한 DOM 탐색 기능을 사용하지 않고 동일한 클래스 이름을 가진 요소에 대한 사용자 상호 작용만 수신합니다. 클래스 선택자는 HTML, CSS, JavaScript를 함께 묶는 접착제와 같아야 합니다. 나는 개인적인 경험을 통해 세 가지 기술을 과도하게 혼합하지 않고 연결하는 가장 쉽고 최선의 방법이라는 것을 알고 있습니다.
미래
WHATWG(Web Hypertext Technology Working Group)는 개발자가 HTML, CSS 및 JavaScript를 바인딩할 수 있는 웹 구성 요소에 대한 사양을 개발 중입니다. 별도의 구성 요소 또는 모듈로 함께 결정되며 다른 페이지 요소와 상호 작용하여 이를 캡슐화합니다. 이 사양이 대부분의 브라우저에서 구현되었다면 이 기사에서 내가 제공하는 많은 제안은 덜 중요해질 것입니다(코드가 누구와 상호 작용하는지 명확해지기 때문입니다). 하지만 그럼에도 불구하고 이를 이해하는 것은 더 광범위한 원칙과 그 이유입니다. 그것들은 여전히 중요하게 필요합니다. 웹 구성 요소 시대에는 이러한 관행이 덜 중요해지더라도 이론은 여전히 적용됩니다. 대규모 팀과 대규모 애플리케이션에서 사용되는 방식은 소규모 모듈 작성에도 여전히 적용되지만 그 반대의 경우는 적용되지 않습니다.
결론
유지관리가 가능한 HTML, CSS 및 JavaScript의 특징은 모든 개발자가 코드 베이스의 모든 부분을 걱정하지 않고 쉽고 자신있게 작성할 수 있다는 것입니다. 이러한 수정 사항은 관련되지 않은 다른 부분에 실수로 영향을 미칠 수 있습니다. 이와 같은 의도하지 않은 결과를 방지하는 가장 좋은 방법 중 하나는 의미를 전달하고 이를 접하는 모든 개발자가 알아낼 수 있는 예측 가능하고 인간과 유사한 클래스 선택기 이름 세트를 제공하는 것입니다.
위의 안티 패턴을 방지하려면 다음 원칙을 염두에 두세요.
1. CSS 및 JavaScript에서는 명시적인 구성 요소 및 동작 클래스 선택기와 복잡하지 않은 CSS 선택기에 우선 순위를 둡니다. .
2. 위치가 아닌 구성 요소의 이름을 지정하세요
3. 스타일과 동작에 동일한 클래스 선택기를 사용하지 마세요
4. 상태 스타일과 기본 스타일 사이
HTML에서 클래스 선택자를 사용하려면 표시해야 할 클래스 선택자가 많이 필요한 경우가 많지만, 얻을 수 있는 이점은 인정할 만한 예측 가능성과 유지 관리 가능성입니다. 결국 클래스 선택기를 HTML에 추가하는 것은 매우 쉽고 개발자 측에서는 기술이 거의 필요하지 않습니다. Nicolas Gallagher의 원문 발췌:
HTML과 CSS를 만들기 위해 CSS를 작성하고 수정하는 데 드는 시간을 줄이는 방법을 찾고 있다면 이것이 바로 그것입니다. 스타일을 변경하려면 HTML 요소의 클래스 선택기를 변경하는 데 더 많은 시간을 소비하고 싶지 않다는 점을 받아들여야 합니다. 이는 프런트엔드 개발자와 백엔드 개발자 모두에게 어느 정도 유용성을 제공하며 누구나 사전 제작된 LEGO 블록을 재배열할 수 있습니다. 그러면 아무도 CSS의 마법을 과시하지 못할 것입니다.
HTML, CSS, JS 분리에 관한 더 많은 기사를 보려면 PHP 중국어 웹사이트를 주목하세요!