瞅瞅Facebook是怎麼保證CSS程式碼質量的

發表於2016-07-17

在Facebook裡,上千名工程師工作在不同的產品線上,為全世界的使用者提供可靠優質的服務,而我們在程式碼質量管理方面也面臨著獨一無二的挑戰。不僅僅是因為我們面對的是一個龐大的程式碼基庫,還有日漸增加的各種各樣的特性,有時候如果你想去重構提高某一個模組,往往會影響到其他很多模組。具體在CSS而言,我們需要處理上千份不停變化的CSS檔案。之前我們著力於通過Code Review、程式碼樣式規範以及重構等手段協同工作,而保障程式碼質量,但是還是會有很多的錯誤悄悄從眼皮底下溜走,被提交進入到程式碼庫裡。我們一直用自建的CSS Linter來檢測基本的程式碼錯誤與保證一致的編碼風格,儘管它基本上已經滿足了我們的目標,但還是存在很多的問題,因此我也想在這篇文章裡對如何保障CSS的程式碼質量進行一些討論。

Regex is not Enough:之前用的是正則匹配,不咋的啊

老的Linter主要是基於很多個正規表示式對CSS中的語法進行提取,大概是這個樣子的:

基本上一個檢測規則就需要新增一個專門的匹配規則,非常不好維護,在效能上也有很大的問題。對於每個規則俺們都需要遍歷整個檔案,效能差得很。

Abstract Syntax Tree

受夠了正規表示式,我們想搞一個更好用的也是更細緻的CSS直譯器。CSS本身也是一門語言,老把它當做純文字檔案處理也不好,因此我們打算用AST,即抽象語法樹的方式構建一個解析器。這種新的處理方式在效能上面有個很不錯的提升,譬如我們的程式碼庫中有這麼一段CSS程式碼:

眼神好的估計才能看出這個程式碼片段中存在的問題,譬如某個屬性名錯了、十六進位制的顏色程式碼寫錯的,分隔符寫錯了等等。瀏覽器才不會主動給你報錯呢,這樣開發者自己也就很難找到錯誤了。在具體實現上,我們發現PostCSS 是個不錯的工具,因此我們選擇了Stylelint作為我們新的Linter工具,它是基於PostCSS構建的,非常的靈活,社群也不錯。

就像JavaScript中的Esprima以及ESLint一樣,Stylelint提供了對於完整的AST的訪問方式,能夠讓你根據不同的情況更快速簡單的訪問具體的程式碼節點,譬如現在我們的檢測規則寫成了這個樣子:

我們也可以傳入一些基本的函式,譬如linear-gradient,就像這個樣子:

這樣子寫出來的檢測規則可讀性更好,也更好去理解與維護,並且這種方式無論是在怎樣的CSS格式化的情況下,以及不管規則和宣告放在哪邊,都能正常地工作。

Custom rules:自定義規則

我們預設使用了一些Stylelint內建的規則,譬如declaration-no-important,selector-no-universal, 以及 selector-class-pattern。如何新增自定義規則的方法可以參考built-in plugin mechanism,而我們使用的譬如:

  • slow-css-properties 來告警一些效能較差的屬性,譬如opacity或者box-shadow
  • filters-with-svg-files 來告警Edge中不支援SVG的過濾
  • use-variables來告警那些可以被內建的常量替換的一些變數
  • common-properties-whitelist 來檢測是否有些誤寫的其實不存在的屬性
  • mobile-flexbox 來檢測一些不被老版本手機瀏覽器支援的屬性
  • text-transform-uppercase 來告警 “text-transform: uppercase”,這個在某些語言表現的不友好

我們也貢獻了部分規則 以及 ad外掛itions 給Stylelint。

Automatic replacement:自動替換

我們檢測過程中有一個重要的工作就是自動格式化,Linter會在發現某些問題的時候問你是否需要根據規則進行替換,這個功能會節約你大量的手動修改校正的時間。一般來說,我們提交程式碼之前都會審視下Linter報出的錯誤,然後去修復這些錯誤。可惜的是Stylelint並沒有內嵌的自動格式化與修復機制,因此我們重寫了部分的Stylelint的規則來增加一個自動替換與修復的功能。

Test all the things

我們老的Linter還有個問題就是沒有單元測試,這點就好像程式碼上線前不進行單元測試一樣不靠譜。我們面對的可能是任意格式的處理文字,因此我們也要保證我們的檢測規則能夠適用於真實有效的環境,這裡我們是選擇了Jest這個測試框架,Stylelint對它的支援挺好的,然後大概一個單元測試是這個樣子:

What‘s next

換一個靠譜的CSS Linter工具只是保證高質量的CSS的程式碼的第一步,我們還打算新增很多自定義的檢測規則來捕獲一些常見的錯誤,保證使用規定的最佳實踐以及統一程式碼約定規範。我們已經在JavaScript的校驗中進行了這一工作。

另外對於React社群中存在的CSS-in-JS這種寫法,對於CSS Linter也是個不小的挑戰,現在的大部分的Linter都是著眼於處理傳統的CSS檔案,以後會新增對於JSX的處理規範吧。

相關文章