程式碼規範是軟體開發領域經久不衰的話題,幾乎所有工程師在開發過程中都會遇到或思考過這一問題。而隨著前端應用的大型化和複雜化,越來越多的前端團隊也開始重視程式碼規範。同樣,前段時間,筆者所在的團隊也開展了一波開源治理,而其中程式碼規範就佔據了很重要的一項。接下來的幾篇文章,將會對JS程式碼規範、CSS規範、Git工作流規範以及文件規範進行詳細的介紹~

系列文章:

  • 前端規範之JS程式碼規範(ESLint + Prettier)
  • 前端規範之CSS規範(Stylelint)
  • 前端規範之Gti工作流規範(Husky + Commitlint + Lint-staged)
  • 前端規範之文件規範

本文主要介紹了前端規範之CSS規範(Stylelint),將會對Stylelint的使用進行介紹,歡迎大家交流討論~

1. Stylelint介紹及安裝

1.1 什麼是Stylelint

Stylelint是一個強大的,現代的程式碼檢查工具,與ESLint類似,Stylelint能夠通過定義一系列的編碼風格規則幫助我們避免在樣式表中出現錯誤。

目前在開源社群上,關於CSS Lint的解決方案主要包括了csslint、SCSS-Lint和Stylelint等幾種。而由於Stylelint在技術架構上基於AST 的方式擴充套件CSS,除原生CSS 語法,其也支援 SCSS、Less 這類前處理器,並且也有非常多的第三方外掛,因此我們團隊選擇了Stylelint作為CSS Lint工具。

官方文件:https://stylelint.io/

1.2 安裝Stylelint

可以選採用npm安裝Stylelint。其中,stylelint-config-standard是Stylelint的標準配置。如果想使用airbnb或prettier的規範,也可以將stylelint-config-standard改為stylelint-config-airbnb或stylelint-config-prettier。

npm install stylelint stylelint-config-standard --save-dev

1.3 安裝適配預處理語法的外掛

如果我們專案中採用瞭如sass或less等css前處理器,那麼可以安裝適配預處理語法的外掛。以sass為例,需要安裝stylelint-scss外掛。

npm install stylelint-scss --save-dev

1.4 安裝CSS屬性排序外掛

我們也可以選擇安裝stylelint-order外掛。該外掛能夠強制我們按照某個順序編寫css,比如先寫定位,再寫盒模型,再寫內容區樣式,最後寫CSS3相關屬性,這樣可以更好的保證我們程式碼的可讀性。

npm install stylelint-order --save-dev

2. Stylelint配置

2.1 Stylelint配置方式

安裝好Stylelint之後,就需要對Stylelint進行配置。Stylelint的配置方式包括了以下幾種:

  • 在package.json中新增stylelint屬性並新增規則
  • 在.stylelintrc檔案中指定,.stylelintrc檔案支援新增一個副檔名來區分 JSON,YAML 或 JS 格式,如建立.stylelintrc.json、.stylelintrc.yaml、.stylelintrc.yml或.stylelintrc.js檔案
  • 在stylelint.config.js檔案中指定,該檔案將會exports一個配置物件

在這裡,我們選擇了在專案根目錄建立.stylelintrc.js來配置Stylelint。

在.stylelintrc.js檔案中,我們可以指定要配置的內容,下面給出了一個配置檔案的例子。

其中,該配置檔案採用了stylelint-config-standard標準配置,並且添加了stylelint-order外掛用於CSS屬性排序,在rules中,可以指定宣告塊內屬性的順序,也可以自定義CSS檢查規則。比如定義了color-hex-case為lower,表示CSS檔案的顏色值都必須小寫,否則會報錯。

module.exports = {
plugins: ['stylelint-order'], extends: ['stylelint-config-standard'], rules: {
// 指定宣告塊內屬性的字母順序
'order/properties-order': [
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'float',
'width',
'height',
'max-width',
'max-height',
'min-width',
'min-height',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'margin-collapse',
'margin-top-collapse',
'margin-right-collapse',
'margin-bottom-collapse',
'margin-left-collapse',
'overflow',
'overflow-x',
'overflow-y',
'clip',
'clear',
'font',
'font-family',
'font-size',
'font-smoothing',
'osx-font-smoothing',
'font-style',
'font-weight',
'hyphens',
'src',
'line-height',
'letter-spacing',
'word-spacing',
'color',
'text-align',
'text-decoration',
'text-indent',
'text-overflow',
'text-rendering',
'text-size-adjust',
'text-shadow',
'text-transform',
'word-break',
'word-wrap',
'white-space',
'vertical-align',
'list-style',
'list-style-type',
'list-style-position',
'list-style-image',
'pointer-events',
'cursor',
'background',
'background-attachment',
'background-color',
'background-image',
'background-position',
'background-repeat',
'background-size',
'border',
'border-collapse',
'border-top',
'border-right',
'border-bottom',
'border-left',
'border-color',
'border-image',
'border-top-color',
'border-right-color',
'border-bottom-color',
'border-left-color',
'border-spacing',
'border-style',
'border-top-style',
'border-right-style',
'border-bottom-style',
'border-left-style',
'border-width',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'border-radius',
'border-top-right-radius',
'border-bottom-right-radius',
'border-bottom-left-radius',
'border-top-left-radius',
'border-radius-topright',
'border-radius-bottomright',
'border-radius-bottomleft',
'border-radius-topleft',
'content',
'quotes',
'outline',
'outline-offset',
'opacity',
'filter',
'visibility',
'size',
'zoom',
'transform',
'box-align',
'box-flex',
'box-orient',
'box-pack',
'box-shadow',
'box-sizing',
'table-layout',
'animation',
'animation-delay',
'animation-duration',
'animation-iteration-count',
'animation-name',
'animation-play-state',
'animation-timing-function',
'animation-fill-mode',
'transition',
'transition-delay',
'transition-duration',
'transition-property',
'transition-timing-function',
'background-clip',
'backface-visibility',
'resize',
'appearance',
'user-select',
'interpolation-mode',
'direction',
'marks',
'page',
'set-link-source',
'unicode-bidi',
'speak',
],

// 顏色值要小寫
'color-hex-case': 'lower','number-leading-zero': 'always',
},
};

2.2 Stylelint配置項

在上面的配置檔案中,我們主要定義了一個配置物件,接下來將對常用的配置項進行介紹。

  (1)plugins

plugins定義了一個數組,該配置項允許我們使用第三方外掛,在該陣列中,需要包含“定位器”標識出你要使用的外掛,一個“定位器”可以是一個 npm 模組名,一個絕對路徑,或一個相對於要呼叫的配置檔案的路徑。

一旦聲明瞭外掛,在rules中需要為外掛的規則新增選項,就像其他標準的規則一樣。你需要檢視外掛的文件去了解規則的名稱。

{
"plugins": [
“stylelint-order",
"../special-rule.js"
],
"rules": {
"order/properties-order": [],
"plugin/special-rule": "everything"
}
}

  (2)extends

extends定義了一個數組,該配置項允許我們extend一個已存在的配置檔案(無論是你自己的還是第三方的配置)。當一個配置繼承了裡一個配置,它將會新增自己的屬性並覆蓋原有的屬性。比如下面的程式碼,我們就extend了Stylelint的標準配置。

{
"extends": "stylelint-config-standard",
"rules": {
"indentation": "tab",
"number-leading-zero": null
}
}

如果extends中包含多個配置項,那麼陣列中的每一項都優先於前一項,也就是說第二項會覆蓋第一項,第三項會覆蓋第一項和第二項,最後一項將覆蓋其它所有項。

{
"extends": [
"stylelint-config-standard",
"./myExtendableConfig"
],
"rules": {
"indentation": "tab"
}
}

  (3)rules

rules定義了一個物件,屬性名為規則名稱,屬性值為規則取值,它告訴Stylelint該檢查什麼,該怎麼報錯,所有的規則都是預設關閉的。我們可以通過該選項開啟相應規則,進行相應的檢測。所有規則必須顯式的進行配置,因為沒有預設值。

規則名稱主要由兩個部分組成,第一部分描述該規則應用於什麼東西,第二部分表示該規則檢查了什麼。

"number-leading-zero"
// ↑ ↑
// the thing what the rule is checking

當規則名稱應用於整個樣式表時只包含第二個部分:

"no-eol-whitespace"
"indentation"
// ↑
// what the rules are checking

當規則名稱不同時,規則取值也不同。我們可以將某個規則設定為null禁用該規則。

{
"rules": {
"at-rule-blacklist": string|[],
"at-rule-empty-line-before": "always"|"never",
"at-rule-name-case": "lower"|"upper",
"block-no-empty": null,
...
}
}

除了規則本身的取值之外,Stylelint還支援一些自定義配置,允許給規則傳遞一個數組,陣列第一項是規則取值,第二項是自定義配置。

"selector-pseudo-class-no-unknown": [true, {
"ignorePseudoClasses": ["global"]
}]

通過自定義配置,我們可以指定:

  • severity:錯誤級別,取值為”warning"或"error",預設情況下,所有規則的錯誤級別都為"error",通過defatuleServerity,可以修改錯誤級別的預設值
// error-level severity examples
{ "indentation": 2 }
{ "indentation": [2] } // warning-level severity examples
{ "indentation": [2, { "severity": "warning" } ] }
{ "indentation": [2, {
"except": ["value"],
"severity": "warning"
}]
}
  • message:當一個規則被觸發時,如果你想實現一個自定義的訊息,可以給規則的傳遞"message“作為第二選項,如果提供,將替代提供的任何標準的訊息。例如,以下規則配置會使用一些自定義的訊息:
  "color-hex-case": [ "lower", {
"message": "Lowercase letters are easier to distinguish from numbers"
} ],
"indentation": [ 2, {
"ignore": ["block"],
"message": "Please use 2 spaces for indentation. Tabs make The Architect grumpy.",
"severity": "warning"
} ]
}

  (4)processors

Processors是Stylelint的鉤子函式,只能用在命令列和Node API,不適用於PostCSS外掛。Processors可以使Stylelint檢測非樣式表文件中的CSS。例如,可以檢測HTML內中<style>標籤中的CSS,Markdown檔案中程式碼塊或JavaScript中的字串。

使用Processors的話,需要在配置中新增一個”processors“陣列,包含“定位器”標識出你要使用的 processors。同上面的extends,一個“定位器”可以是一個 npm 模組名,一個絕對路徑,或一個相對於要呼叫的配置檔案的路徑。

{
"processors": ["stylelint-html-processor"],
"rules": {..}
}

如果你的processor有選項,把它們放到一個數組裡,第一項是“定位器”,第二項是選項物件。

{
"processors": [
"stylelint-html-processor",
[ "some-other-processor", { "optionOne": true, "optionTwo": false } ]
],
"rules": {..}
}

2.3 忽略特定檔案的檢查

在實際的使用場景中,我們可能存在某些檔案或某行程式碼,希望能夠跳過Stylelint的檢查或禁用某些規則,下面主要介紹了幾種跳過Stylelint檢查的方式:

  (1)使用註釋禁用規則

使用/* stylelint-disable */,可以在程式碼片段禁用所有規則或禁用特定規則。

/* stylelint-disable */
a {}
/* stylelint-enable */ /* stylelint-disable selector-no-id, declaration-no-important */
#id {
color: pink !important;
}
/* stylelint-enable */

使用/* stylelint-disable-line */,可以在個別行上禁用規則。

#id { /* stylelint-disable-line */
color: pink !important; /* stylelint-disable-line declaration-no-important */
}

使用/* stylelint-disable-next-line */,可以在下一行禁用規則。

#id {
/* stylelint-disable-next-line declaration-no-important */
color: pink !important;
}

(2)建立.stylelintignore忽略某些檔案的檢查

在專案根目錄建立.stylelintignore檔案。

在.stylelintignore中寫入需要跳過Stylelint檢查的檔名稱,比如下面的程式碼將會忽略dist,node_modules和package.json檔案的Stylelint檢查。

dist
node_modules
package.json

2.4 執行Stylelint檢查

安裝配置好Stylelint之後,我們就可以執行Stylelint命令,對指定的檔案進行CSS語法檢查,其中,--fix表示自動修復Stylelint錯誤。

執行Stylelint命令後,如果什麼也沒有輸出,就說明我們的檔案已經通過Stylelint的檢查。如果輸出報錯資訊,就說明沒有通過Stylelint的檢查,需要根據錯誤資訊對程式碼進行修復。

// 對某個檔案進行檢查
stylelint "src/App.vue" --fix // 對指定字尾名的檔案進行檢查
stylelint "src/*.{html,vue,css,saas,scss,less}" --fix

除了直接在命令列執行Stylelint命令方式之外,我們也可以在package.json中自定義Stylelint的啟動命令。如下面程式碼所示,配置好package.json之後,我們通過執行npm run lint:css就能夠對指定檔案進行Stylelint檢查。

{
"scripts": {
"serve": "cross-env NODE_ENV=development vue-cli-service serve --mode dev",
"serve:test": "cross-env NODE_ENV=test vue-cli-service serve --mode test",
"serve:prod": "cross-env NODE_ENV=production vue-cli-service serve --mode prod",
"build:dev": "cross-env NODE_ENV=production vue-cli-service build --mode dev",
"build:test": "cross-env NODE_ENV=production vue-cli-service build --mode test",
"build:prod": "cross-env NODE_ENV=production vue-cli-service build --mode prod",
"lint": "vue-cli-service lint",
"lint:css": "stylelint **/*.{vue,htm,html,css,sss,less,scss,sass} --fix"
},
}

3. VSCode外掛

3.1 安裝Stylelint外掛

為了讓我們在編寫程式碼的過程中,能夠實時提示Stylelint錯誤,並且在儲存檔案時,能夠自動對當前檔案進行Stylelint檢查和修復,我們可以在VSCode中安裝Stylelint外掛。

在VSCode的EXTENSIONS中找到Stylelint外掛,點選install就可以安裝Stylelint外掛。

3.2 配置settings.json檔案

安裝好Stylelint外掛之後,我們還需要配置VSCode的settings.json檔案,讓我們的程式碼在儲存時,就能夠按照規範對CSS樣式進行檢查及自動fix。VSCode的settings.json設定分為工作區和使用者區兩個級別。其中,使用者區的設定會對所有專案生效,工作區的設定只能對當前專案生效。

(1)使用者區settings.json配置

點選VSCode左下角的設定按鈕,選擇Settings,並且選擇以文字編輯的方式開啟settings.json,在settings.json中加入以下程式碼。

{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {"source.fixAll.stylelint": true // 儲存時是否自動 stylelint 修復
},
}

(2)工作區settings.json配置

在專案根目錄建立.vscode目錄,並且在該目錄下建立settings.json檔案。

在settings.json中加入以下程式碼。

{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": true // 儲存時是否自動 stylelint 修復
}
}

配置好使用者區或工作區的settings.json後,當我們修改了某個檔案的CSS程式碼,並且儲存時,就會發現能夠對當前檔案自動進行stylelint檢查和修復了。