解耦HTML、CSS和JavaScript

 當前在互聯網上,任何一個稍微複雜的網站或者應用程序都會包含許多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更多的樣式操做

  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須要知道訂閱的事件名稱,但和對象A須要知道對象B的公共方法相比,這已經更鬆散的耦合了。

  HTML類選擇器都很是類似。與CSS文件中定義複雜的選擇器(就像HTML的內部接口同樣)不一樣的是,它能夠經過單一類選擇器簡單定義一個可視化組件的外觀。CSS文件不須要關心HTML對類選擇器的使用與否。一樣,JavaScript不用那些須要更深刻理解HTML結構的複雜DOM遍歷功能,而是僅僅監聽與類名一致的元素的用戶交互。類選擇器應該像是膠水同樣,把HTML,CSS和JavaScript鏈接在一塊兒。從我的經驗得知,它們也是最容易以及最好的方式把三者技術鏈接起來,而不是混合過分。

 將來

  網頁超文本技術工做小組(WHATWG)正在致力於web組件的規範,能讓開發者把HTML,CSS和JavaScript綁定一塊兒做爲一個單獨的組件或者模塊,並與其它的頁面元素進行交互封裝。若是這個規範已經在大多數的瀏覽器中實現的話,那麼我在本文中提供的不少建議就變得不那麼重要了(由於代碼和誰交互變得很清晰);可是不管如何,理解這些更普遍的原則以及爲什麼須要它們仍然很重要。即便這些實踐在Web組件時代會變得不那麼重要,但其中的理論仍然適用。在大型團隊和大型應用中的實踐仍然要適用於小模塊的編寫中,反之則不須要。

 結論

  可維護的HTML,CSS和JavaScript的標誌是每一個開發者能夠容易而且很自信地編寫代碼庫的每一個部分,而不需擔憂這些修改會無心中影響到其它不相關部分。阻止這樣意想不到的後果的最佳方式之一是,經過一組可以表達其義的,任何開發者碰到時能想出它的用途的,可預測的人性化的類選擇器名,把這三者技術結合在一塊兒。

  爲避免上述的反模式,請把下述的原則謹記於心:

  • 1. 在CSS和JavaScript裏,優先考慮顯式組件和行爲類選擇器,而不是複雜的CSS選擇器。
  • 2. 命名組件要基於它們是什麼,而不是它們在哪裏
  • 3. 不用爲樣式和行爲使用相同的類選擇器去
  • 4. 把狀態樣式和默認樣式區分開來

  在HTML中這樣運用類選擇器常常會須要不少須要表現的類選擇器,但獲取的是可預見性和可維護性,這點值得確定。畢竟,爲HTML增長類選擇器是至關容易的,不須要開發者有多少技能。摘自Nicolas Gallagher的原話:

  當你要尋找一種方式來減小花費在編寫和修改CSS的時間上來製做HTML和CSS時,這就涉及到你必須接受若是你想更改樣式,你是不想花費更多時間去更改HTML元素上的類選擇器。這對前端和後端開發者都有必定的實用性,任何人均可以從新安排預構建的樂高積木。這樣沒有人會去展現CSS的魔力了。

  原文連接: Philip Walton   翻譯: 伯樂在線 蟈蟈

相關文章
相關標籤/搜索