javascript
解耦HTML、CSS和JavaScript
? ? ? 當(dāng)前在互聯(lián)網(wǎng)上,任何一個稍微復(fù)雜的網(wǎng)站或者應(yīng)用程序都會包含許多HTML、CSS和JavaScript。隨著互聯(lián)網(wǎng)運用的發(fā)展以及我們對它的依賴性日益增加,設(shè)定一個關(guān)于組織和維護你的前端代碼的計劃是絕對需要的。
? 當(dāng)今的一些大型互聯(lián)網(wǎng)公司,由于越來越多的人會接觸到日益增加的前端代碼,它們會試圖去堅持代碼的模塊化。這樣更改程序的部分代碼,并不會無意中過多地影響后續(xù)不相關(guān)部分的執(zhí)行過程。
防止意想不到的后果不是一個容易解決的問題,尤其是HTML,CSS和JavaScript本質(zhì)上是相互依賴的。更糟糕的是,當(dāng)涉及到前端代碼時,一些傳統(tǒng)計算機科學(xué)原則,比如關(guān)注分離,這一長期運用在服務(wù)端開發(fā)中,很少會討論到。
在本文中,我將會講講我所學(xué)到的如何去解耦我的HTML,CSS和JavaScript代碼。從個人以及他人經(jīng)驗所得,這種的最好辦法并不是那么顯而易見,而通常是不直觀的,而且有時還會與許多所謂的最佳實踐相違背。
目標(biāo)
HTML,CSS和JavaScript之間總會存在耦合關(guān)聯(lián)。不管怎樣,這些技術(shù)與生俱來就是要和其它進行交互。舉個例子,一種飛閃轉(zhuǎn)換效果可能會在樣式表中用帶有類選擇器定義,但它經(jīng)常由HTML初始化,并通過用戶交互,如編寫JavaScript,來觸發(fā)。由于前端代碼的有些耦合是不可避免的,你的目標(biāo)就不應(yīng)該是簡單地消除之間的耦合,而應(yīng)該是減少代碼間不必要的依賴耦合關(guān)系。一個后端開發(fā)者應(yīng)該能夠?qū)TML模板中的標(biāo)記進行更改,而無需擔(dān)心意外破壞CSS規(guī)則或者一些JavaScript功能。由于當(dāng)今的web團隊日漸增大且專業(yè)化,這個目標(biāo)比以往更甚。
反模式
前端代碼的緊耦合現(xiàn)象并不總是很明顯。事實上復(fù)雜的是,一方面看起來似乎松耦合,但從另一方面則是緊耦合。以下是我曾經(jīng)多次做過或者看過,以及吸取我的過錯中,總結(jié)的所有的反模式。對每一個模式,我會嘗試去解釋為何耦合這么糟糕,并且指出如何去避免它。
過度復(fù)雜的選擇器
CSS Zen Garden向世界展示了你可以完全改變整個網(wǎng)站的外觀而無需更改任意一個的HTML標(biāo)記。這是語義網(wǎng)運動的典型代表,主要原則之一就是去避免使用表象類。乍一看,CSS Zen Garden可能看起來像是一個很好的解耦合例子,畢竟,把樣式從標(biāo)記語言中分離出來是它的重點所在。但是,若按照這樣做,問題就來了,你會經(jīng)常需要在你的樣式表里有這樣的選擇器,如下:
#sidebar section:first-child h3 + p { }CSS Zen Garden中,雖然HTML幾乎與CSS完全分離,但CSS會強耦合到HTML中去,此時就需要你對標(biāo)記語言的結(jié)構(gòu)有深層次的理解。這可能看起來似乎并不是很糟糕,尤其是某人維護著CSS,同時需要維護HTML,但一旦你增加了許多人手進去,這種情況就變得無法控制了。如果某個開發(fā)者在某種情況下在第一個<section>前增加了<div>,上面的規(guī)則就無法生效,然而他也不清楚其中緣由。
只要你網(wǎng)站的標(biāo)記很少改動,CSS Zen Garden就是一個非常不錯的主意。但是這和當(dāng)今的Web應(yīng)用不盡然都是這種情況。與冗長而又復(fù)雜的CSS選擇器相比,最好的辦法是在可視化組件本身的根元素增加一個或多個類選擇器。比如,如果側(cè)邊欄有子菜單,只需要為每個子菜單元素增加submenu類選擇器,而不要用這樣的形式:
ul.sidebar > li > ul {/* submenu styles */ }這種方式的結(jié)果是在HTML中需要更多的類選擇器,但從長遠(yuǎn)來看,這又降低了耦合度,以及讓代碼更可重用和可維護,并且還能讓你的標(biāo)記自文檔化。如果HTML里沒有類選擇器,那些對CSS不熟悉的開發(fā)者就不清楚HTML的改動如何影響了其它代碼。另一方面,在HTML中使用類選擇器能很清晰地看到那些樣式或者功能被使用到了。
多個類選擇器的職責(zé)
一個類選擇器往往是用來同時作為樣式和JavaScript的鉤子。雖然這看起來似乎很節(jié)約(因為至少減少了一個類標(biāo)記),但事實上,這是把元素的表現(xiàn)和功能耦合起來了。
<button class="add-item">Add to Cart</button>以上例子描述了一個帶有add-item類樣式的”添加到購物車”按鈕。
如果開發(fā)者想為此元素添加一個單擊事件監(jiān)聽器,用已經(jīng)存在的類選擇器作為鉤子非常的容易。我的意思是,既然已經(jīng)存在了一個,為何要添加另一個呢? 但是想想看,有很多像這樣的按鈕,遍布了整個網(wǎng)站,都調(diào)用了相同的JavaScript功能。再想想看,如果市場團隊想要其中一個和其它看起來完全不同但功能相同的按鈕呢。也許這樣就需要更多顯著的色彩了。
問題就來了,因為監(jiān)聽單擊事件的JavaScript代碼希望add-item類選擇器被使用到,但是你新的按鈕又無法使用這個樣式(或者它必須清除所有聲明的,然后再重置新的樣式)。還有,如果你測試的代碼同時也希望使用add-item類選擇器,那么你不得不要去更新那么代碼用到的地方。更糟糕的是,如果這個”添加到購物車”功能不僅僅是當(dāng)前應(yīng)用用到的話,也就是說,把這份代碼抽象出來作為一個獨立的模塊,那么即使一個簡單的樣式修改,可能會在完全不同的應(yīng)用中引發(fā)問題。
使用javaScript鉤子最好的(事實上也是比較鼓勵的)做法是,如果你需要這么做,使用一種方式來避免樣式和行為類選擇器之間的耦合。
我的個人建議是讓JavaScript鉤子使用前綴,比如:js-*。這樣的話,當(dāng)開發(fā)者在HTML源代碼中看到這樣的類選擇器,他就完全明白個中原因了。所以,上述的”添加到購物車”的例子可以重寫成這樣:
<button class="js-add-to-cart add-item">Add to Cart</button>現(xiàn)在,如果需要一個看起來不同的按鈕,你可以很簡單地修改下樣式類選擇器,而不管行為的類選擇器。
<button class="js-add-to-cart add-item-special">Add to Cart</button>JavaScript更多的樣式操作
JavaScript能用類選擇器去DOM中查找元素,同樣,它也能通過增加或移除類選擇器來改變元素的樣式。但如果這些類選擇器和當(dāng)初加載頁面時不同的話也會有問題。當(dāng)JavaScript代碼使用太多的組成樣式操作時,那些CSS開發(fā)者就會輕易去改變樣式表,卻不知道破壞了關(guān)鍵功能。也并不是說,JavaScript不應(yīng)該在用戶交互之后改變可視化組件的外觀,而是如果這么做,就應(yīng)該使用一種一致的接口,應(yīng)該使用和默認(rèn)樣式不一致的類選擇器。
和js-*前綴的類選擇器類似,我推薦使用is-*前綴的類選擇器來定義那些要改變可視化組件的狀態(tài),這樣的CSS規(guī)則可以像這樣:
.pop-up.is-visible { }注意到狀態(tài)類選擇器(is-visible)是連接在組件類選擇器(pop-up)后,這很重要。因為狀態(tài)規(guī)則是描述一個的狀態(tài),不應(yīng)該單獨列出。如此不同就可以用來區(qū)分更多和默認(rèn)組件樣式不同的狀態(tài)樣式。
另外,可以讓我們可以編寫測試場景來保證像is-*這樣的前綴約定是否遵從。一種測試這些規(guī)則的方式是使用CSSLint和HTML Inspector。
更多關(guān)于特定狀態(tài)類選擇可以查閱Jonathan Snnok編寫的非常優(yōu)秀的SMACSS書籍。
JavaScript”選擇器”
jQuery和新的API,像document.querySelectorAll,讓用戶非常簡單地通過一種他們已經(jīng)非常熟悉的語言–CSS選擇器來查找DOM中的元素。雖然如此強大,但同樣有CSS選擇器已經(jīng)存在的相同的問題。JavaScript選擇器不應(yīng)過度依賴于DOM結(jié)構(gòu)。這樣的選擇器非常慢,并且需要更深入認(rèn)識HTML知識。
就第一個例子來講,負(fù)責(zé)HTML模板的開發(fā)者應(yīng)該能在標(biāo)記上做基本的改動,而不需擔(dān)心破壞基本的功能。如果有個功能會被破壞,那么它就應(yīng)該在標(biāo)記上顯而易見。
我已經(jīng)提及到應(yīng)該用js-*前綴的類選擇器來表示JavaScript鉤子。另外針對消除樣式和功能類選擇器之間的二義性,需要在標(biāo)記中表達(dá)出來。當(dāng)某人編寫HTML看到j(luò)s-*前綴的類選擇器時,他就會明白這是別有用途的。但如果JavaScript代碼使用特定的標(biāo)記結(jié)構(gòu)查找元素時,正在觸發(fā)的功能在標(biāo)記上就不那么明顯了。
為了避免使用冗長而又復(fù)雜的選擇器遍歷DOM,堅持使用單一的類或者ID選擇器。 考慮以下代碼:
var saveBtn = document.querySelector("#modal div:last-child > button:last-child")這么長的選擇器是可以節(jié)省你在HTML中添加一個類選擇器,但同樣讓你的代碼對于標(biāo)記更改非常容易受到影響。如果設(shè)計者突然決定要把保持按鈕放在左邊,而讓取消按鈕放在右邊,這樣的選擇器就不再匹配了。
一個更好的方式(使用上述的前綴方法)是僅僅使用類選擇器。
var saveBtn = document.querySelector(".js-save-btn")現(xiàn)在標(biāo)記可以更改它想改的,并且只要類選擇還是在正確的元素上,一切都會很正常。
類選擇器就是你的契約
使用合適的類選擇器以及可預(yù)測的類名約定可以減少幾乎每一種HTML,CSS和JavaScript之間的耦合。起初由于為了展現(xiàn)HTML需要知道很多類選擇器的名稱,這種在標(biāo)記中使用很多類選擇器看起來像是強耦合的跡象。但是我發(fā)覺,使用類選擇器和傳統(tǒng)編程設(shè)計中的事件或者觀察者模式非常相似。在事件驅(qū)動編程中,為了不直接在對象A上調(diào)用對象B,而是對象A簡單地在提供的環(huán)境中發(fā)布一個特定的事件,然后對象B能夠訂閱那個事件。這樣,對象B就不需要知道任何關(guān)于對象A的接口,而僅僅需要知道監(jiān)聽什么事件。按理說,事件系統(tǒng)需要某種形式上的耦合,因為對象B需要知道訂閱的事件名稱,但和對象A需要知道對象B的公共方法相比,這已經(jīng)更松散的耦合了。
HTML類選擇器都非常相似。與CSS文件中定義復(fù)雜的選擇器(就像HTML的內(nèi)部接口一樣)不同的是,它可以通過單一類選擇器簡單定義一個可視化組件的外觀。CSS文件不需要關(guān)心HTML對類選擇器的使用與否。同樣,JavaScript不用那些需要更深入理解HTML結(jié)構(gòu)的復(fù)雜DOM遍歷功能,而是僅僅監(jiān)聽與類名一致的元素的用戶交互。類選擇器應(yīng)該像是膠水一樣,把HTML,CSS和JavaScript連接在一起。從個人經(jīng)驗得知,它們也是最容易以及最好的方式把三者技術(shù)連接起來,而不是混合過度。
未來
網(wǎng)頁超文本技術(shù)工作小組(WHATWG)正在致力于web組件的規(guī)范,能讓開發(fā)者把HTML,CSS和JavaScript綁定一起作為一個單獨的組件或者模塊,并與其它的頁面元素進行交互封裝。如果這個規(guī)范已經(jīng)在大多數(shù)的瀏覽器中實現(xiàn)的話,那么我在本文中提供的很多建議就變得不那么重要了(因為代碼和誰交互變得很清晰);但是無論如何,理解這些更廣泛的原則以及為何需要它們?nèi)匀缓苤匾<词惯@些實踐在Web組件時代會變得不那么重要,但其中的理論仍然適用。在大型團隊和大型應(yīng)用中的實踐仍然要適用于小模塊的編寫中,反之則不需要。
結(jié)論
可維護的HTML,CSS和JavaScript的標(biāo)志是每個開發(fā)者可以容易并且很自信地編寫代碼庫的每個部分,而不需擔(dān)心這些修改會無意中影響到其它不相關(guān)部分。阻止這樣意想不到的后果的最佳方式之一是,通過一組能夠表達(dá)其義的,任何開發(fā)者碰到時能想出它的用途的,可預(yù)測的人性化的類選擇器名,把這三者技術(shù)結(jié)合在一起。
為避免上述的反模式,請把下述的原則謹(jǐn)記于心:
- 1. 在CSS和JavaScript里,優(yōu)先考慮顯式組件和行為類選擇器,而不是復(fù)雜的CSS選擇器。
- 2. 命名組件要基于它們是什么,而不是它們在哪里
- 3. 不用為樣式和行為使用相同的類選擇器去
- 4. 把狀態(tài)樣式和默認(rèn)樣式區(qū)分開來
在HTML中這樣運用類選擇器經(jīng)常會需要很多需要表現(xiàn)的類選擇器,但獲取的是可預(yù)見性和可維護性,這點值得肯定。畢竟,為HTML增加類選擇器是相當(dāng)容易的,不需要開發(fā)者有多少技能。摘自Nicolas Gallagher的原話:
當(dāng)你要尋找一種方式來減少花費在編寫和修改CSS的時間上來制作HTML和CSS時,這就涉及到你必須接受如果你想更改樣式,你是不想花費更多時間去更改HTML元素上的類選擇器。這對前端和后端開發(fā)者都有一定的實用性,任何人都可以重新安排預(yù)構(gòu)建的樂高積木。這樣沒有人會去展示CSS的魔力了。
總結(jié)
以上是生活随笔為你收集整理的解耦HTML、CSS和JavaScript的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle 11g数据库启动错误总结
- 下一篇: 【语法解释】init