關於語義化 HTML 以及前端架構的一點思考

oschina發表於2013-09-05

  這是一篇我喜歡的思想,經驗,理念,以及過去幾年中我所試驗的理念的集合。它覆蓋了HTML語義,前端架構的元件和方法,類命名模式,和HTTP內容壓縮。

我們不會停止探索
而我們一切探索的終點
將會到達我們出發的地方
於是我們第一次認識了這個地方。

  T.S. Eliot — “小吉丁

 關於語義

  語義是對標記與符號之間的關係,以及它們的含義的研究。在語言學中,這主要是對語言中的符號(如單詞,短語,或聲音)意義的研究。在前端web開發的上下文中,語義大多是與元素,屬性,和屬性值(包括像Microdata之類的擴充套件)的一致認同意義相關。這些認同意義通常在規範中被定義概念,它們可以幫助程式設計師(也就是人類)更好的理解網站中資訊的不同方面。但是,即使是規範化以後,元素,屬性,和屬性值的語義還是受制於開發者對其的適應與吸收。這可能會導致後續對正式認同語義的修改(這也是一個HTML 設計原則)。

 區別不同型別的HTML語義

  編寫“語義HTML”原則是現代專業前端開發的基礎之一。大部分語義關於存在的本質屬性或是期望的內容(例如h1element,langattribute,emailvalue of thetypeattribute, Microdata)。

  然而,不是所有的語義都要源於內容。Class名不能是“非語義”(unsemantic)的。無論使用什麼名字,都要有意義、有目的。Class名的語義可以和那些HTML元素不一樣。 我們可以統一利用“全域性”的語義命名HTML元素、某些HTML屬性、微資料等等,以免和“本地”的網站/應用專屬的常常包含在屬性值內的語義相混淆,比如theclassattribute。

  儘管在HTML5 specification section on classes 重複的認為“最佳的實踐” 如下…

…作者鼓勵用 [class attribute] 值來描述內容的顯示,而不是描述的內容所需的表現值。

  …沒有內在的理由這樣做。事實上,它往往是大型網站或應用程式工作時的一個障礙。

  • 內容層語義已經擔任HTML元素和其他屬性。
  • 類名很少或根本沒有賦予語義有用的資訊給計算機或瀏覽,除非它的一小部分(計算機可讀)約定的名字 - 微格式的部分。
  • 類名稱的主要目的是為CSS和JavaScript設定鉤子。 如果你不需要給你的web文件新增演示和行為,那麼你可能不需要在你的HTML檔案中使用classes。 
  • 類名應該提供有用的資訊給開發商。當你讀一個DOM程式碼段的時候,有助於瞭解一個特定的類的名字是要幹什麼,特別是在前期與多個開發團隊,包括非HTML元件工作的人。

  以這種非常簡單的例子:

<div class="news">
    <h2>News</h2>
    [news content]
</div>

  從上面的例子很明顯的看出,類名news並沒有告訴你任何內容。它沒給你元件的組織結構的任何資訊,它不能用於說明內容是不是“新聞”。類名的語義與內容的性質已經減少的關聯,架構功能已經變小,或者也很容易讓其他開發者使用。

 內容無關的類名

  另一種可選的方法是在設計中從重複結構和功能模式中派生類名的語義。大多數可重用的元件都帶有與內容無關的類名。

  我們不應該害怕在清晰明確的層級間建立聯絡,而不是讓類名嚴格地反映特定的內容。這樣做不會讓類變得“沒有語義”,它僅僅意味著它們的語義不是從內容中派生出來的。如果附加的HTML元素幫助建立魯棒性強,柔軟度大,可重用的元件,我們就不要害怕把這些元素包含進來。這樣做也不會讓HTML變得沒有語義,它僅僅意味著你使用了剛好超過標記內容最小所需的元素。

 前端架構

  元件/模板/物件導向架構的設計宗旨就是開發出一套包含一系列不同型別的可重用元件。在規範的應用程式中,關於類名稱的語義,由實用主義者們推動並服務他們的主要目的——提供有意義的,靈活的,可重用的關聯供開發者使用。

  可重用及可組合的元件

  可伸縮的HTML/CSS大體上必須依賴於HTML內的層級,這樣就可以創造可重用的元件。一個靈活且可重用的元件既不能依賴存在於DOM 樹的某個部分,也不能需要使用特定的元素型別。它應該能適用於不同的編輯器並且很容易主體化。如果必要的話,多餘的HTML元素(除了那些需要標記內容的元素)可以用來讓組價更強壯。Nicole Sullivan所謂的媒體物件就是很好的例子。

  元件可以很容易的組合受益於型別選擇器的廢止和支援class。下面的示例展示了btn元件與uilist元件的簡易組合。問題是,指定.btn不如指定.uilist(這將覆蓋共享屬性),uilist元件需要一個錨標記作為子節點。

.btn { /* styles */ }
.uilist { /* styles */ }
.uilist a { /* styles */ }
<nav class="uilist">
    <a href="#">Home</a>
    <a href="#">About</a>
    <a class="btn" href="#">Login</a>
</nav>

  使用class來修飾子DOM元素是提高易用性、使用uilist來組合其他元件的一種方法。雖然這有助於減少指定規則,主要的好處是可以讓你對任何型別的子節點應用結構化的樣式。

.btn { /* styles */ }
.uilist { /* styles */ }
.uilist-item { /* styles */ }
<nav class="uilist">
    <a class="uilist-item" href="#">Home</a>
    <a class="uilist-item" href="#">About</a>
    <span class="uilist-item">
        <a class="btn" href="#">Login</a>
    </span>
</nav>

  JavaScript指定class

  使用JavaScript指定class的形式的可以降低風險:元件的主題或結構的改變時破壞對其對應的Javascript。一種有效的方法是:Javascript鉤子只使用js-*的特定class,並且一直保持。

<a href="/login" class="btn btn-primary js-login"></a>

  這樣做,你可以減少風險:元件的結構或主題改變時會無意中影響任何必需的JavaScript行為或複雜功能。

  元件編輯器

  元件通常具有與基礎元件有些許差別的多種不同外觀,比如,一個不同顏色的背景或者邊框。有兩種主要的模式被用來建立這些不同的元件。我稱它們為“單類”模式和“多類”模式。

  “單類”模式

.btn, .btn-primary { /* button template styles */ }
.btn-primary { /* styles specific to save button */ }

<button class="btn">Default</button>
<button class="btn-primary">Login</button>

  “多類”模式

.btn { /* button template styles */ }
.btn-primary { /* styles specific to primary button */ }

<button class="btn">Default</button>
<button class="btn btn-primary">Login</button>

  如果你使用了前處理器,就可以使用Sass的@extend功能,來縮減一些使用“單類”模式時所涉及的維護工作。可是,即使有前處理器的幫助,我仍然傾向於使用“多類”模式,和在HTML中增加編輯類。

  我發現這是一個更具伸縮性的模式。例如,用基礎的btn元件,給它增添5種型別的按鈕和3種額外的尺寸。使用“多類”模式你最終將獲得9種可混用匹配的類。但使用“單類”模式你將有24種類。

  如果確實需要的話,它給元件做一些上下文的修改也更容易。你或許會想在另一個元件中,對任意一個btn做一些外觀的細微調整。

/* "multi-class" adjustment */
.thing .btn { /* adjustments */ }

/* "single-class" adjustment */
.thing .btn,
.thing .btn-primary,
.thing .btn-danger,
.thing .btn-etc { /* adjustments */ }

 “多類”模式意味著在元件中,你只需一個元件內的選擇器,來標示btn-樣式元素的任意一種型別。而“單類”模式意味著你必須承擔任何可能的按鈕型別,並且在一個新按鈕變數被建立的時候調整選擇符。

 結構化的類名

  在建立元件,還有在此基礎上的“主題”的時候,有些類被用作元件的邊界,有些被用作元件的修飾器,有些被用來將一些DOM節點渲染到一個更大的抽象的展現元件中。

  我們很難推斷出btn(元件),btn-primary(修飾器),btn-group(元件)和btn-group-item(元件中的子物件)這些樣式之間的關係,因為這些命名並沒有清楚的揭示這些類的目的。沒有統一的模式。

  早在2011年,我開始使用命名模式,這讓我很快的理解了DOM片段節點之間外在的關係,這比嘗試通過來回移動HTML,CSS和JS檔案來拼湊整個網站的結構要來得快很多。在要點中的標記的方式主要是受BEM系統的命名方式所影響,但被我改變成了我認為容易使用的方式。

  自從我開始寫這篇文章,其他的幾支團隊和框架(譯者加:作者)採用了這種方法。MontageJS修改了符號變成另外一個風格,我更偏愛的並且目前正使用的是SUIT 工具包:

/* Utility */
.u-utilityName {}

/* State-utility */
.u-isStateName {}

/* Component */
.ComponentName {}

/* Component modifier */
.ComponentName--modifierName {}

/* Component descendant */
.ComponentName-descendant {}

/* Component descendant modifier */
.ComponentName-descendant--modifierName {}

/* Component state (scoped to component) */
.ComponentName.is-stateOfComponent {}

/* Component mixin (ancestor style dependencies) */
.with-ComponentName {}

  這只是我此刻發現有用的一個命名模式,它可以採取任何形式。但好處在於消除 僅僅依賴(單)連字元或下劃線,或者駝峰式大小寫的類名的歧義。

  注意原檔案大小和http壓縮

  凡討論到模組化和可擴充套件,css成為有關檔案大小和膨脹的關注性問題。Nicole Sullivan的談話中經常提及到節省檔案大小(和維護改進)。而節省檔案大小是一些公司例如facebook,在採用這種模組化和可擴充套件方法時遇到的問題。進一步地,我想分享,在前處理器上編寫且大量使用HTML元素時我對編寫完的檔案進行http壓縮的效果。

  當Twitter Bootstrap問世時,我改寫了已經編譯的css,使其能更好的反映出我是如何手寫來進行語義化,並且比較前後檔案的大小。在同時精簡了這兩個檔案之後,手寫來進行語義化的css比前處理器上寫的要小大約10%。但是當兩個檔案都採用gzip壓縮了之後,前處理器上寫的比手寫進行語義化的css要小大約5%。

  此處凸顯了在經過 HTTP 壓縮之後進行檔案大小對比的重要性, 因為壓縮後的檔案的大小並不能完全說明問題. 這也暗示了有經驗的 CSS 開發者在使用前處理器的時候無需過多糾結於在編譯之後的 CSS 中會出現的一定程度的重複, 因為在 HTTP 壓縮之後它的尺寸會自然地變得更小. 通過前處理器處理過的更具維護性的 CSS 程式碼所帶來的益處將會勝過對原始檔案以及壓縮輸出的 CSS 檔案的大小或美學上的考量.

  在另一次實踐中, 我從一個線上網站上下載了一份有 60KB 大小的 HTML 文件, 從中刪除了所有的 class 屬性(它們構成了許多可重用的元件). 這樣處理之後將文件的大小減小到了 25KB. 當原始文件和分離過的文件各自被 gzip 壓縮過後, 它們的尺寸變成了 7.6KB 和 6KB – 僅 1.6KB 的區別. 對於那些通過自由使用 class 而實際對檔案大小產生的影響真的不值得再強調和放大了.

 我怎樣學會停止煩惱的…

  許多熟練的開發人員的經驗,經過多年,已經導致了大規模網站和應用程式開發的巨大轉變。儘管如此,對那些在意識形態上斷奶的個體來說,“語義化HTML”是指使用內容派生類名(即使是這樣,只能作為最後的手段),在你變得可以敏銳地意識到那個方法不切實際的性質之前,通常需要你完成一個大型應用程式。你必須準備放棄舊的觀念,看看替代品,甚至你可以重拾之前摒棄的方法。

  一旦你開始寫作你和其他人不僅要維護而且要積極迭代的不平凡的網站和應用程式時,你很快就會發現,儘管你盡了最大的努力,你的程式碼開始變得越來越難維護。一些人提出了他們自己的方法來解決這些問題,你值得花時間去探索他們的工作:妮可的部落格和物件導向的CSS專案,喬納森斯努克的可伸縮的CSS模組化架構,和Yandex開發的塊元素修改器方法。

  當你選擇用HTML和CSS寫作,並試圖減少你花在寫作和編輯CSS上的時間,這涉及到必須接受如果你想改變HTML元素的風格,你必須花更多的時間修改元素的類上。這證明是相當實用的,無論對前端還是後端開發人員——任何人都可以重新排列預先構建的“樂高積木”;事實證明,沒有人可以發明css鍊金術。

  英文來源:http://nicolasgallagher.com/about-html-semantics-front-end-architecture/

相關文章