原文地址
本文從屬於筆者的Web 前端入門與最佳實踐php
在Facebook裏,上千名工程師工做在不一樣的產品線上,爲全世界的用戶提供可靠優質的服務,而咱們在代碼質量管理方面也面臨着獨一無二的挑戰。不只僅是由於咱們面對的是一個龐大的代碼基庫,還有日漸增長的各類各樣的特性,有時候若是你想去重構提升某一個模塊,每每會影響到其餘不少模塊。具體在CSS而言,咱們須要處理上千份不停變化的CSS文件。以前咱們着力於經過Code Review、代碼樣式規範以及重構等手段協同工做,而保障代碼質量,可是仍是會有不少的錯誤悄悄從眼皮底下溜走,被提交進入到代碼庫裏。咱們一直用自建的CSS Linter來檢測基本的代碼錯誤與保證一致的編碼風格,儘管它基本上已經知足了咱們的目標,但仍是存在不少的問題,所以我也想在這篇文章裏對如何保障CSS的代碼質量進行一些討論。css
老的Linter主要是基於不少個正則表達式對CSS中的語法進行提取,大概是這個樣子的:前端
preg_match_all( // This pattern matches [attr] selectors with no preceding selector. '/\/\*.*?\*\/|\{[^}]*\}|\s(\[[^\]]+\])/s', $data, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); foreach ($matches as $match) { if (isset($match[1])) { raiseError(...); }
基本上一個檢測規則就須要添加一個專門的匹配規則,很是很差維護,在性能上也有很大的問題。對於每一個規則俺們都須要遍歷整個文件,性能差得很。node
受夠了正則表達式,咱們想搞一個更好用的也是更細緻的CSS解釋器。CSS自己也是一門語言,老把它當作純文本文件處理也很差,所以咱們打算用AST,即抽象語法樹的方式構建一個解析器。這種新的處理方式在性能上面有個很不錯的提高,譬如咱們的代碼庫中有這麼一段CSS代碼:git
{ display: none: background-color: #8B1D3; padding: 10px,10px,0,0; opacity: 1.0f; }
眼神好的估計才能看出這個代碼片斷中存在的問題,譬如某個屬性名錯了、十六進制的顏色代碼寫錯的,分隔符寫錯了等等。瀏覽器纔不會主動給你報錯呢,這樣開發者本身也就很難找到錯誤了。在具體實現上,咱們發現PostCSS 是個不錯的工具,所以咱們選擇了Stylelint做爲咱們新的Linter工具,它是基於PostCSS構建的,很是的靈活,社區也不錯。github
就像JavaScript中的Esprima以及ESLint同樣,Stylelint提供了對於完整的AST的訪問方式,可以讓你根據不一樣的狀況更快速簡單的訪問具體的代碼節點,譬如如今咱們的檢測規則寫成了這個樣子:正則表達式
root.walkDecls(node => { if (node.prop === 'text-transform' && node.value === 'uppercase') { report({ ... }); } });
咱們也能夠傳入一些基本的函數,譬如linear-gradient
,就像這個樣子:瀏覽器
// disallow things like linear-gradient(top, blue, green) w. incorrect first valueroot.walkDecls(node => { const parsedValue = styleParser(node.value); parsedValue.walk(valueNode => { if (valueNode.type === 'function' && valueNode.value === 'linear-gradient') { const firstValueInGradient = styleParser.stringify(valueNode.nodes[0]); if (disallowedFirstValuesInGradient.indexOf(firstValueInGradient) > -1) { report({ ... }); } } }); });
這樣子寫出來的檢測規則可讀性更好,也更好去理解與維護,而且這種方式不管是在怎樣的CSS格式化的狀況下,以及無論規則和聲明放在哪邊,都能正常地工做。框架
咱們默認使用了一些Stylelint內置的規則,譬如declaration-no-important,selector-no-universal, 以及 selector-class-pattern。如何添加自定義規則的方法能夠參考built-in plugin mechanism,而咱們使用的譬如:ide
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。
咱們檢測過程當中有一個重要的工做就是自動格式化,Linter會在發現某些問題的時候問你是否須要根據規則進行替換,這個功能會節約你大量的手動修改校訂的時間。通常來講,咱們提交代碼以前都會審視下Linter報出的錯誤,而後去修復這些錯誤。惋惜的是Stylelint並無內嵌的自動格式化與修復機制,所以咱們重寫了部分的Stylelint的規則來增長一個自動替換與修復的功能。
咱們老的Linter還有個問題就是沒有單元測試,這點就好像代碼上線前不進行單元測試同樣不靠譜。咱們面對的多是任意格式的處理文本,所以咱們也要保證咱們的檢測規則可以適用於真實有效的環境,這裏咱們是選擇了Jest這個測試框架,Stylelint對它的支持挺好的,而後大概一個單元測試是這個樣子:
test.ok('div { background-image: linear-gradient( 0deg, blue, green 40%, red ); }', 'linear gradient with valid syntax'); test.notOk('a { background: linear-gradient(top, blue, green); }', message, 'linear-gradient with invalid syntax');
換一個靠譜的CSS Linter工具只是保證高質量的CSS的代碼的第一步,咱們還打算添加不少自定義的檢測規則來捕獲一些常見的錯誤,保證使用規定的最佳實踐以及統一代碼約定規範。咱們已經在JavaScript的校驗中進行了這一工做。
另外對於React社區中存在的CSS-in-JS這種寫法,對於CSS Linter也是個不小的挑戰,如今的大部分的Linter都是着眼於處理傳統的CSS文件,之後會添加對於JSX的處理規範吧。