聊聊前端語義化的今天

陳月半啦發表於2019-02-27

語義化是前端開發裡面的一個專用術語,其優點在於標籤語義化有助於構架良好的html結構,有利於搜尋引擎的建立索引、抓取;另外,亦有利於頁面在不同的裝置上顯示儘可能相同;此外,亦有利於構建清晰的機構,有利於團隊的開發、維護。

前端語義化一度和 HTML 相關聯,前端開發者們通過使用一些視覺表現類似,語義不同的 HTML 標籤來提高 專案/產品 質量。在這裡我聊得可能範圍更加寬泛,包括但不僅僅是 HTML 的語義化。

語義化的前世

1998年 Tim Berners-Lee 提出了語義網的概念,它的核心是:

通過給全球資訊網上的文件(如: HTML文件)新增能夠被計算機所理解的語義(後設資料),從而使整個網際網路成為一個通用的資訊交換介質。

在這裡我想通過多個角度去理解語義化。

HTML

我所能查詢到的大部分語義化都與 HTML 有關,所以先聊聊 HTML。

HTML 早期版本就考慮到語義化,推出了 h1~h6 img ul ol li 等 HTML 標籤,早期搜尋引擎也很好的利用這些語義化標籤合理的抓取內容。

而隨著網際網路內容的不斷豐富,這些標籤明顯不足以描述各種功能,於是前端開始用 id class 等屬性進一步豐富 Web。此時搜尋引擎如果僅靠標籤抓取內容就顯得有些力不從心了。

HTML5 釋出後,諸如 section header footer main 等標籤能更好的被搜尋引擎抓取,並且降低了開發人員之間的交流成本,從而降低了維護成本。

這裡有一份 HTML5 標籤列表,推薦一波。

舉例

我曾經疑問 <b> <strong><i> <em>的區別,從表現形式上看 <b> <strong> 都是文字的加粗、<i> <em> 都是文字的斜體,似乎沒什麼區別。

實際上 em 元素代表對其內容的強調:

我今天<em>吃藥</em>的時候看到一個新聞
我今天吃藥的時候看到一個<em>新聞</em>
複製程式碼

這樣爬蟲就知道該關注你吃藥還是關注新聞了(認真臉)。

strong 元素代表內容的強烈的重要性、嚴重性或者緊急性:

我正在睡覺,同桌突然戳我一下說<strong>老師來了<strong/>
複製程式碼

總結:

  1. 利於SEO,不再解釋。
  2. 利於開發與維護,遵循同一個標準開發,同時讓頁面結構更加的清晰,自然提高了工作效率。
  3. 利於更多裝置解析。

HTTP 請求語義化

我們最常用的 HTTP 請求方法 就是 GET POST 了,先從歷史的角度聊聊。

HTTP/0.9

作為 HTTP 最早大規模使用的版本,只有一個 GET 方法,目的是從伺服器獲取 HTML 文件

HTTP/1.0

這個版本新增了 HEAD POST 兩種方法。

  • HEAD - HEADGET 類似,但 HEAD 不含有呈現資料,僅含有 HTTP 頭資訊。通常用於判斷資源是否存在。(若有其他用途歡迎留言或聯絡我提供呦)

  • POST - POST 用於傳送資料給服務端。HTTP/1.1 中描述 POST 涵蓋一下功能:

    • 註釋已有的資源
    • 在公告板,新聞組,郵件列表或類似的文章組中釋出訊息;
    • 通過註冊新增使用者;
    • 向資料處理程式提供一批資料,例如提交一個表單;
    • 通過追加操作,擴充套件資料庫資料.

HTTP/1.1

這個版本新增了 PUTDELETECONNECTOPTIONSTRACE 五種方法。

  • PUT - PUT 方法用請求有效載荷替換目標資源的所有當前表示。
    • POST 類似,都是往服務端傳送資料,區別:
    • client對一個URI傳送一個Entity,伺服器在這個URI下如果已經又了一個Entity,那麼此刻伺服器應該替換成client重新提交的,也由此保證了PUT的冪等性。如果伺服器之前沒有Entity ,那麼伺服器就應該將client提交的放在這個URI上。
    • 通過上面可以知道,如果用PUT來達到更改資源,需要client提交資源全部資訊,如果只有部分資訊,不應該使用PUT(因為伺服器使用client提交的物件整體替換伺服器的資源)。
  • DELETE - DELETE 用於請求伺服器刪除所請求 URI 所標識的資源。
  • CONNECT - CONNECT 方法可以開啟一個客戶端與所請求資源之間的雙向溝通的通道。它可以用來建立隧道(tunnel)。
  • OPTIONS - OPTIONS 方法用於獲取目的資源所支援的通訊選項。
  • TRACE - TRACE 方法實現沿通向目標資源的路徑的訊息環回(loop-back)測試 ,提供了一種實用的 debug 機制。
    • 請求的最終接收者應當原樣反射(reflect)它接收到的訊息,除了以下欄位部分,作為一個 Content-Typemessage/http 的200(OK)響應的訊息的主體(body)返回給客戶端 。

HTTP/1.1 的擴充套件

  • PATCH - PATCH 用於對資源進行部分修改。前文提到使用 PUT 需要客戶端提供資源全部資訊整體替換,而對於只修改部分資源的場景,PATCH 尤為適合。

舉例

過去為了解決跨域問題客戶端曾使用 jsonp與服務端互動,而 jsonp 需要一個帶有 src 屬性的 script 標籤傳送一條 GET 請求繞過跨域限制,這條請求可以作為增刪改查等任何行為的觸發器,所以很明顯,jsonp 不符合語義化的規範。

總結

這一片段相關的內容很容易讓人與 RESTful 規範 相聯絡,我所理解的是 RESTful 規範 本身就是語義化各種方案的實現,所以一定程度上 RESTful 規範 的優點也是 HTTP 請求語義化的優點。

  1. GET HEAD PUT DELETE OPTIONS冪等 的,POST PATCH 是非冪等的。
  2. 服務自解釋 - 例如評論的增刪改查,只需要一個 URI /common,其他的該怎麼做一目瞭然。
  3. API內部實現解耦

JavaScript 語義化

不要寫無用的註釋

// 資料型別判斷
if(Object.prototype.toString.call(str) === “[object String]”){
    // doSomething();
};
複製程式碼

與其為這種略複雜的邏輯寫註釋,不如封裝為語義化函式:

const isObject = val => val != null
  && typeof val === 'object'
  && Array.isArray(val) === false
  && Object.prototype.toString.call(val) === '[object Object]';
  
if(isObject(str)) {
    // doSomething();
}
複製程式碼

命名

這裡只是拋轉引玉,並不是規範。

1. 注意詞性

  • 普通變數/屬性用「名詞」
var person = {
    name: 'Frank'
}
var student = {
    grade: 3,
    class: 2
}
複製程式碼
  • bool變數/屬性用「形容詞」或者「be動詞」或者「情態動詞」或者「hasX」
var person = {
    dead: false, // 如果是形容詞,前面就沒必要加 is,比如isDead 就很廢話
    canSpeak: true, //情態動詞有 can、should、will、need 等,情態動詞後面接動詞
    isVip: true, // be 動詞有 is、was 等,後面一般接名詞
    hasChildren: true, // has 加名詞
}
複製程式碼
  • 普通函式/方法用「動詞」開頭
var person = {
    run(){}, // 不及物動詞
    drinkWater(){}, // 及物動詞
    eat(foo){}, // 及物動詞加引數(引數是名詞)
}
複製程式碼
  • 回撥、鉤子函式用「介詞」開頭,或用「動詞的現在完成時態」
var person = {
    beforeDie(){},
    afterDie(){},
    // 或者
    willDie(){}
    dead(){} // 這裡跟 bool 衝突,你只要不同時暴露 bool dead 和函式 dead 就行,怕衝突就用上面的 afterDie
}
button.addEventListener('click', onButtonClick)
var component = {
    beforeCreate(){},
    created(){},
    beforeMount(){},
    mounted(){},
    beforeUpdate(){},
    updated(){},
    activated(){},
    deactivated(){},
    beforeDestroy(){},
    destroyed(){},
    errorCaptured(){}
}
複製程式碼
  • 容易混淆的地方加字首
div1.classList.add('active') // DOM 物件
div2.addClass('active') // jQuery 物件
不如改成
domDiv1 或 elDiv1.classList.add('active')
$div2.addClass('active')
複製程式碼
  • 屬性訪問器函式可以用名詞
$div.text() // 其實是 $div.getText()
$div.text('hi') // 其實是 $div.setText('hi')
複製程式碼

2. 注意一致性

  • 介詞一致性 如果你使用了 before + after,那麼就在程式碼的所有地方都堅持使用 如果你使用了 before + 完成時,那麼就堅持使用 如果你改來改去,就「不一致」了,不一致將導致「不可預測」
  • 順序一致性 比如 updateContainerWidth 和 updateHeightOfContainer 的順序就令人很彆扭,同樣會引發「不可預測」
  • 表裡一致性 函式名必須完美體現函式的功能,既不能多也不能少。 比如
    function getSongs(){
        return $.get('/songs).then((response){
            div.innerText = response.songs
        })
    }
    複製程式碼
    就違背了表裡一致性,getSongs 表示獲取歌曲,並沒有暗示這個函式會更新頁面,但是實際上函式更新了 div,這就是表裡不一,正確的寫法是
    • 要麼糾正函式名
    function getSongsAndUpdateDiv(){
        return $.get('/songs).then((response){
            div.innerText = response.songs
        })
    }
    複製程式碼
    • 要麼寫成兩個函式
    function getSongs(){
        return $.get('/songs)
    }
    function updateDiv(songs){
        div.innerText = response.songs
    }
    getSongs().then((response)=>{
        updateDiv(response.songs)
    })
    複製程式碼
  • 時間一致性: 有可能隨著程式碼的變遷,一個變數的含義已經不同於它一開始的含義了,這個時候你需要及時改掉這個變數的名字。 這一條是最難做到的,因為寫程式碼容易,改程式碼難。如果這個程式碼組織得不好,很可能會出現牽一髮而動全身的情況(如全域性變數就很難改)

最後

對於機器而言,語義化能更容易的分析資料。對於程式猿而言語義化能讓我們少乾點活,多點時間享受生活。

前端現在是黎明前的黑暗,在幾年內一定會明晰起來

參考:

相關文章