stylelint項目實踐


背景

看到項目中團隊成員寫CSS樣式風格迥異,CSS樣式的書寫順序沒有鮮明的規范。想到以前看過CSS樣式書寫順序的文章,決定找出來,給團隊成員科普一下。查閱了好幾篇文章,覺得這篇文章給出的理由最硬核,css樣式的書寫順序及原理——很重要! 然而擔心萬一文中的觀點不對,分享出去被打臉,於是決定驗證一下文中的說法。發現文中的核心觀點,下面這一段:

 

 沒經受起實踐的檢驗。瀏覽器在渲染頁面的過程中,並不是實時逐條讀取樣式生成CSS Rule Tree,並進行繪制,而是會將同一個文件中的同類樣式先合並,再去構建樣式規則樹並繪制。請看下面的實驗,假如瀏覽器是實時逐條讀取樣式規則並進行繪制,那么box在渲染繪制的過程中,應該在某一瞬間出現綠色的背景,實際上發現並未出現,未繪制之前,后面設置的樣式就把前面設置的背景色重置了。

<style>
  .box {
    width:100vw;
    height: 100vh;
    background-color: green;
  }
</style>
<script>
  setTimeout(() => {
    document.querySelector('.item').style.backgroundColor='blue';
  }, 2000);
</script>
<style>
  .box {
    background-color: red;
  }
</style>
<div class="box"></div>

實踐證明,樣式的書寫順序對頁面繪制沒有影響,雖然這篇文章的作者提出的觀點值得商榷,但是卻為我打開了學習stylelint的大門。我通過以文查文,找到了stylelint 。stylelint的價值在於能發現樣式書寫中的問題,並能給出一套合理的書寫規范。而這,正是我想找的。

1. stylelint帶來的好處

  • 如下圖真實項目所示,可以發現樣式書寫的問題,以及對樣式書寫方法進行優化。
  • 此外,能使所有人寫的樣式風格都一致,看別人寫的代碼,就像看自己寫的代碼一樣,立刻秒懂,易於代碼維護。
  • 從心中沒有明確規則的書寫樣式,變成按照業內知名公司 (GitHub、Google、Airbnb)的樣式規范要求寫樣式。畢竟Google就是瀏覽器業內的知名公司,按照Google的樣式書寫規則去寫,不會被坑。

2. stylelint保存時自動格式化的配置方法

2.1 在VSCode應用市場,下載stylelint擴展,注意stylelint插件的版本要與 "stylelint-config-standard": "^22.0.0"版本匹配, 不然VSCode會彈出遷移提示的彈窗,另外stylelint 1.x版本,會把rgba格式化成十六進制顏色值,而移動端的瀏覽器不一定都支持這樣的屬性設置 參見 stylelint 接入實戰踩坑總結 最末章節,不要隨便升級 VSCode的stylelint插件

2.2 在項目下的.vscode/setting.json中添加開啟保存自動格式化的配置

{
    "editor.codeActionsOnSave": {
        "source.fixAll.stylelint": true
    },
    // 關閉vscode自帶的css,less,scss報錯提示
    "css.validate": false,
    "less.validate": false,
    "scss.validate": false,
    "stylelint.validate": ["css", "less"]
}

3. 如何批量修復項目樣式文件?

3.1 安裝stylelint相關的npm依賴包

yarn add -D stylelint stylelint-config-standard stylelint-order stylelint-config-recess-order 

stylelint-config-standard

作用:配置 Stylelint 規則。
官方的代碼風格 :stylelint-config-standard。該風格是 Stylelint 的維護者汲取了 GitHub、Google、Airbnb 多家之長生成的。

stylelint-order

該插件的作用是強制你按照某個順序編寫 css。例如先寫定位,再寫盒模型,再寫內容區樣式,最后寫 CSS3 相關屬性。這樣可以極大的保證我們代碼的可讀性。

stylelint-config-recess-order

stylelint-order 插件的第三方配置

3.2 根目錄添加.stylelintrc.js 文件

module.exports = {
  extends: ['stylelint-config-standard', 'stylelint-config-recess-order'],
  plugins: ['stylelint-order'],
  rules: {
    indentation: 2,
    'at-rule-no-unknown': [true, { ignoreAtRules: ['mixin', 'extend', 'content', 'include'] }],
    'no-empty-source': null, //  null是關閉規則的意思--less文件內容可以為空
    'no-descending-specificity': null, //禁止特異性較低的選擇器在特異性較高的選擇器之后重寫
    'font-family-no-missing-generic-family-keyword': null, // 關閉必須設置通用字體的規則
    'function-calc-no-invalid': null, // 關閉對calc寫法的校驗
    'property-no-unknown': [
      true,
      {
        ignoreProperties: ['box-flex'], // 忽略某些未知屬性的檢測
      },
    ],
    'selector-pseudo-element-no-unknown': [
      true,
      {
        ignorePseudoElements: ['ng-deep'], // 忽略ng-deep這種合法的偽元素選擇器報警
      },
    ],
    'declaration-colon-newline-after': null, //一個屬性過長的話可以寫成多行
    'media-feature-name-no-unknown': null, // 關閉禁止未知的媒體功能名
    // 下面的排序規則是stylelint-config-recess-order的css排序規則,
    // 要對某個屬性排序進行調整,這個屬性之前的樣式排序都要配置在自定義屬性排序中
    'order/properties-order': [
      {
        // Must be first.
        properties: ['all'],
      },
      {
        // Position.
        properties: ['position', 'top', 'right', 'bottom', 'left', 'z-index'],
      },
      {
        // Display mode.
        properties: ['box-sizing', 'display'],
      },
      {
        // Flexible boxes.
        properties: ['flex', 'flex-basis', 'flex-direction', 'flex-flow', 'flex-grow', 'flex-shrink', 'flex-wrap'],
      },
      {
        // Grid layout.
        properties: [
          'grid',
          'grid-area',
          'grid-template',
          'grid-template-areas',
          'grid-template-rows',
          'grid-template-columns',
          'grid-row',
          'grid-row-start',
          'grid-row-end',
          'grid-column',
          'grid-column-start',
          'grid-column-end',
          'grid-auto-rows',
          'grid-auto-columns',
          'grid-auto-flow',
          'grid-gap',
          'grid-row-gap',
          'grid-column-gap',
        ],
      },
      {
        // Align.
        properties: ['align-content', 'align-items', 'align-self'],
      },
      {
        // Justify.
        properties: ['justify-content', 'justify-items', 'justify-self'],
      },
      {
        // Order.
        properties: ['order'],
      },
      {
        // Box model.
        properties: [
          'float',
          'width',
          'min-width',
          'max-width',
          'height',
          'line-height',
          'min-height',
          'max-height',
          'padding',
          'padding-top',
          'padding-right',
          'padding-bottom',
          'padding-left',
          'margin',
          'margin-top',
          'margin-right',
          'margin-bottom',
          'margin-left',
          'overflow',
          'overflow-x',
          'overflow-y',
          '-webkit-overflow-scrolling',
          '-ms-overflow-x',
          '-ms-overflow-y',
          '-ms-overflow-style',
          'clip',
          'clear',
        ],
      },
      {
        // Typography.
        properties: [
          'font',
          'font-family',
          'font-size',
          'font-style',
          'font-weight',
          'font-variant',
          'font-size-adjust',
          'font-stretch',
          'font-effect',
          'font-emphasize',
          'font-emphasize-position',
          'font-emphasize-style',
          '-webkit-font-smoothing',
          '-moz-osx-font-smoothing',
          'font-smooth',
          'hyphens',
          'color',
          'text-align',
          'text-align-last',
          'text-emphasis',
          'text-emphasis-color',
          'text-emphasis-style',
          'text-emphasis-position',
          'text-decoration',
          'text-indent',
          'text-justify',
          'text-outline',
          '-ms-text-overflow',
          'text-overflow',
          'text-overflow-ellipsis',
          'text-overflow-mode',
          'text-shadow',
          'text-transform',
          'text-wrap',
          '-webkit-text-size-adjust',
          '-ms-text-size-adjust',
          'letter-spacing',
          'word-break',
          'word-spacing',
          'word-wrap', // Legacy name for `overflow-wrap`
          'overflow-wrap',
          'tab-size',
          'white-space',
          'vertical-align',
          'list-style',
          'list-style-position',
          'list-style-type',
          'list-style-image',
        ],
      },
      {
        // Accessibility & Interactions.
        properties: [
          'pointer-events',
          '-ms-touch-action',
          'touch-action',
          'cursor',
          'visibility',
          'zoom',
          'table-layout',
          'empty-cells',
          'caption-side',
          'border-spacing',
          'border-collapse',
          'content',
          'quotes',
          'counter-reset',
          'counter-increment',
          'resize',
          'user-select',
          'nav-index',
          'nav-up',
          'nav-right',
          'nav-down',
          'nav-left',
        ],
      },
      {
        // Background & Borders.
        properties: [
          'background',
          'background-color',
          'background-image',
          "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient",
          'filter:progid:DXImageTransform.Microsoft.gradient',
          'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader',
          'filter',
          'background-repeat',
          'background-attachment',
          'background-position',
          'background-position-x',
          'background-position-y',
          'background-clip',
          'background-origin',
          'background-size',
          'background-blend-mode',
          'isolation',
          'border',
          'border-color',
          'border-style',
          'border-width',
          'border-top',
          'border-top-color',
          'border-top-style',
          'border-top-width',
          'border-right',
          'border-right-color',
          'border-right-style',
          'border-right-width',
          'border-bottom',
          'border-bottom-color',
          'border-bottom-style',
          'border-bottom-width',
          'border-left',
          'border-left-color',
          'border-left-style',
          'border-left-width',
          'border-radius',
          'border-top-left-radius',
          'border-top-right-radius',
          'border-bottom-right-radius',
          'border-bottom-left-radius',
          'border-image',
          'border-image-source',
          'border-image-slice',
          'border-image-width',
          'border-image-outset',
          'border-image-repeat',
          'outline',
          'outline-width',
          'outline-style',
          'outline-color',
          'outline-offset',
          'box-shadow',
          'mix-blend-mode',
          'filter:progid:DXImageTransform.Microsoft.Alpha(Opacity',
          "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha",
          'opacity',
          '-ms-interpolation-mode',
        ],
      },
      {
        // SVG Presentation Attributes.
        properties: [
          'alignment-baseline',
          'baseline-shift',
          'dominant-baseline',
          'text-anchor',
          'word-spacing',
          'writing-mode',

          'fill',
          'fill-opacity',
          'fill-rule',
          'stroke',
          'stroke-dasharray',
          'stroke-dashoffset',
          'stroke-linecap',
          'stroke-linejoin',
          'stroke-miterlimit',
          'stroke-opacity',
          'stroke-width',

          'color-interpolation',
          'color-interpolation-filters',
          'color-profile',
          'color-rendering',
          'flood-color',
          'flood-opacity',
          'image-rendering',
          'lighting-color',
          'marker-start',
          'marker-mid',
          'marker-end',
          'mask',
          'shape-rendering',
          'stop-color',
          'stop-opacity',
        ],
      },
      {
        // Transitions & Animation.
        properties: [
          'transition',
          'transition-delay',
          'transition-timing-function',
          'transition-duration',
          'transition-property',
          'transform',
          'transform-origin',
          'animation',
          'animation-name',
          'animation-duration',
          'animation-play-state',
          'animation-timing-function',
          'animation-delay',
          'animation-iteration-count',
          'animation-direction',
        ],
      },
    ],
  },
};

3.3 在package.json中添加stylelint樣式修復命令

{
    "scripts": {
        "lintless": "stylelint src/**/*.less --fix",
        // ...
    },
}        

3.4 在終端下執行yarn lintless命令, 就能對不符合配置樣式規范的代碼進行修復,部分問題還需要進一步手動修復。

yarn lintless

 

具體規則查詢參考:

  [1] stylelint規則中文翻譯 

  [2] stylelint構造及規則了解

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM