前端工程化工具系列(三)—— Stylelint(v16.6.1):CSS/SCSS 代码质量工具

Stylelint 是 CSS/SCSS 的静态分析工具,用于检查其中的违规和错误。

1. 环境要求

v16 以上的 Stylelint,支持 Node.js 的版本为 v18.12.0+。

在命令行工具中输入以下内容后回车,来查看当前系统中 Node.js 的版本。

bash 复制代码
node -v

Node.js 推荐使用 v18.20.3+ 或者 v20.13.1+。

这里使用的包管理器是 PNPM,版本为 v9.1.4。

2 安装

2.1 针对 CSS

bash 复制代码
pnpm install -D stylelint stylelint-config-standard stylelint-order

2.2 针对 SCSS(包含CSS)

bash 复制代码
pnpm install -D stylelint stylelint-order stylelint-config-standard stylelint-config-standard-scss stylelint-config-sass-guidelines stylelint-scss

2.3 在 HTML 中使用

bash 复制代码
pnpm install -D postcss-html

3 配置

在项目根目录下创建 stylelint.config.js 文件,根据样式文件类型(CSS/SCSS)填入以下内容:

3.1 针对 CSS

js 复制代码
export default {
  // 继承已有配置 如果规则与自己想要的冲突 可以在下面rules中修改规则
  extends: ['stylelint-config-standard'],
  // 插件是由社区创建的规则或规则集 按照规则对CSS属性进行排序
  plugins: [
    // 指定排序,比如声明的块内(插件包)属性的顺序
    'stylelint-order',
  ],
  rules: {
    // 允许的最大嵌套深度为 3
    'max-nesting-depth': 3,
    // 屏蔽一些scss等语法检查
    'at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: [
          'extend',
          'at-root',
          'debug',
          'warn',
          'error',
          'if',
          'else',
          'for',
          'each',
          'while',
          'mixin',
          'include',
          'content',
          'return',
          'function',
        ],
      },
    ],
    // 屏蔽没有申明通用字体
    'font-family-no-missing-generic-family-keyword': null,
    // ID选择器 最多使用一个
    'selector-max-id': 1,
    // 不允许使用的选择器的类型
    'selector-no-qualifying-type': null,
    // 屏蔽类选择器的检查,以确保使用字符 __
    'selector-class-pattern': null,
    // 允许的最大复合选择器为 5
    'selector-max-compound-selectors': 5,
    // 属性排序规则
    'order/properties-order': [
      [
        'content',
        'position',
        'top',
        'right',
        'bottom',
        'left',
        'z-index',
        'display',
        'vertical-align',
        'flex',
        'flex-grow',
        'flex-shrink',
        'flex-basis',
        'flex-direction',
        'flex-flow',
        'flex-wrap',
        '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',
        'gap',
        'row-gap',
        'column-gap',
        'align-content',
        'align-items',
        'align-self',
        'justify-content',
        'justify-items',
        'justify-self',
        'order',
        'float',
        'clear',
        'object-fit',
        'overflow',
        'overflow-x',
        'overflow-y',
        'overflow-scrolling',
        'clip',
        //
        'box-sizing',
        'width',
        'min-width',
        'max-width',
        'height',
        'min-height',
        'max-height',
        'margin',
        'margin-top',
        'margin-right',
        'margin-bottom',
        'margin-left',
        'padding',
        'padding-top',
        'padding-right',
        'padding-bottom',
        'padding-left',
        'border',
        'border-spacing',
        'border-collapse',
        'border-width',
        'border-style',
        'border-color',
        'border-top',
        'border-top-width',
        'border-top-style',
        'border-top-color',
        'border-right',
        'border-right-width',
        'border-right-style',
        'border-right-color',
        'border-bottom',
        'border-bottom-width',
        'border-bottom-style',
        'border-bottom-color',
        'border-left',
        'border-left-width',
        'border-left-style',
        'border-left-color',
        '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',
        'border-top-image',
        'border-right-image',
        'border-bottom-image',
        'border-left-image',
        'border-corner-image',
        'border-top-left-image',
        'border-top-right-image',
        'border-bottom-right-image',
        'border-bottom-left-image',
        //
        'background',
        'background-color',
        'background-image',
        'background-attachment',
        'background-position',
        'background-position-x',
        'background-position-y',
        'background-clip',
        'background-origin',
        'background-size',
        'background-repeat',
        'color',
        'box-decoration-break',
        'box-shadow',
        'outline',
        'outline-width',
        'outline-style',
        'outline-color',
        'outline-offset',
        'table-layout',
        'caption-side',
        'empty-cells',
        'list-style',
        'list-style-position',
        'list-style-type',
        'list-style-image',
        //
        'font',
        'font-weight',
        'font-style',
        'font-variant',
        'font-size-adjust',
        'font-stretch',
        'font-size',
        'font-family',
        'src',
        'line-height',
        'letter-spacing',
        'quotes',
        'counter-increment',
        'counter-reset',
        '-ms-writing-mode',
        'text-align',
        'text-align-last',
        'text-decoration',
        'text-emphasis',
        'text-emphasis-position',
        'text-emphasis-style',
        'text-emphasis-color',
        'text-indent',
        'text-justify',
        'text-outline',
        'text-transform',
        'text-wrap',
        'text-overflow',
        'text-overflow-ellipsis',
        'text-overflow-mode',
        'text-shadow',
        'white-space',
        'word-spacing',
        'word-wrap',
        'word-break',
        'overflow-wrap',
        'tab-size',
        'hyphens',
        'interpolation-mode',
        //
        'opacity',
        'visibility',
        'filter',
        'resize',
        'cursor',
        'pointer-events',
        'user-select',
        //
        'unicode-bidi',
        'direction',
        'columns',
        'column-span',
        'column-width',
        'column-count',
        'column-fill',
        'column-gap',
        'column-rule',
        'column-rule-width',
        'column-rule-style',
        'column-rule-color',
        'break-before',
        'break-inside',
        'break-after',
        'page-break-before',
        'page-break-inside',
        'page-break-after',
        'orphans',
        'widows',
        'zoom',
        'max-zoom',
        'min-zoom',
        'user-zoom',
        'orientation',
        'fill',
        'stroke',
        //
        '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',
        'animation-fill-mode',
      ],
      {
        unspecified: 'bottom',
        severity: 'warning',
      },
    ],
    // 屏蔽属性按字母顺序检查
    'order/properties-alphabetical-order': null,
  },
};

3.2 针对 SCSS

js 复制代码
export default {
  // 继承已有配置 如果规则与自己想要的冲突 可以在下面rules中修改规则
  extends: [
    'stylelint-config-standard',
    'stylelint-config-standard-scss',
    'stylelint-config-sass-guidelines',
  ],
  // 插件是由社区创建的规则或规则集 按照规则对CSS属性进行排序
  plugins: [
    // 执行各种各样的 SCSS语法特性检测规则(插件包)
    'stylelint-scss',
    // 指定排序,比如声明的块内(插件包)属性的顺序
    'stylelint-order',
  ],
  rules: {
    // 允许的最大嵌套深度为 3
    'max-nesting-depth': 3,
    // 屏蔽一些scss等语法检查
    'at-rule-no-unknown': [
      true,
      {
        ignoreAtRules: [
          'extend',
          'at-root',
          'debug',
          'warn',
          'error',
          'if',
          'else',
          'for',
          'each',
          'while',
          'mixin',
          'include',
          'content',
          'return',
          'function',
        ],
      },
    ],
    // 屏蔽没有申明通用字体
    'font-family-no-missing-generic-family-keyword': null,
    // ID选择器 最多使用一个
    'selector-max-id': 1,
    // 不允许使用的选择器的类型
    'selector-no-qualifying-type': null,
    // 屏蔽类选择器的检查,以确保使用字符 __
    'selector-class-pattern': null,
    // 允许的最大复合选择器为 5
    'selector-max-compound-selectors': 5,
    // 属性排序规则
    'order/properties-order': [
      [
        'content',
        'position',
        'top',
        'right',
        'bottom',
        'left',
        'z-index',
        'display',
        'vertical-align',
        'flex',
        'flex-grow',
        'flex-shrink',
        'flex-basis',
        'flex-direction',
        'flex-flow',
        'flex-wrap',
        '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',
        'gap',
        'row-gap',
        'column-gap',
        'align-content',
        'align-items',
        'align-self',
        'justify-content',
        'justify-items',
        'justify-self',
        'order',
        'float',
        'clear',
        'object-fit',
        'overflow',
        'overflow-x',
        'overflow-y',
        'overflow-scrolling',
        'clip',
        //
        'box-sizing',
        'width',
        'min-width',
        'max-width',
        'height',
        'min-height',
        'max-height',
        'margin',
        'margin-top',
        'margin-right',
        'margin-bottom',
        'margin-left',
        'padding',
        'padding-top',
        'padding-right',
        'padding-bottom',
        'padding-left',
        'border',
        'border-spacing',
        'border-collapse',
        'border-width',
        'border-style',
        'border-color',
        'border-top',
        'border-top-width',
        'border-top-style',
        'border-top-color',
        'border-right',
        'border-right-width',
        'border-right-style',
        'border-right-color',
        'border-bottom',
        'border-bottom-width',
        'border-bottom-style',
        'border-bottom-color',
        'border-left',
        'border-left-width',
        'border-left-style',
        'border-left-color',
        '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',
        'border-top-image',
        'border-right-image',
        'border-bottom-image',
        'border-left-image',
        'border-corner-image',
        'border-top-left-image',
        'border-top-right-image',
        'border-bottom-right-image',
        'border-bottom-left-image',
        //
        'background',
        'background-color',
        'background-image',
        'background-attachment',
        'background-position',
        'background-position-x',
        'background-position-y',
        'background-clip',
        'background-origin',
        'background-size',
        'background-repeat',
        'color',
        'box-decoration-break',
        'box-shadow',
        'outline',
        'outline-width',
        'outline-style',
        'outline-color',
        'outline-offset',
        'table-layout',
        'caption-side',
        'empty-cells',
        'list-style',
        'list-style-position',
        'list-style-type',
        'list-style-image',
        //
        'font',
        'font-weight',
        'font-style',
        'font-variant',
        'font-size-adjust',
        'font-stretch',
        'font-size',
        'font-family',
        'src',
        'line-height',
        'letter-spacing',
        'quotes',
        'counter-increment',
        'counter-reset',
        '-ms-writing-mode',
        'text-align',
        'text-align-last',
        'text-decoration',
        'text-emphasis',
        'text-emphasis-position',
        'text-emphasis-style',
        'text-emphasis-color',
        'text-indent',
        'text-justify',
        'text-outline',
        'text-transform',
        'text-wrap',
        'text-overflow',
        'text-overflow-ellipsis',
        'text-overflow-mode',
        'text-shadow',
        'white-space',
        'word-spacing',
        'word-wrap',
        'word-break',
        'overflow-wrap',
        'tab-size',
        'hyphens',
        'interpolation-mode',
        //
        'opacity',
        'visibility',
        'filter',
        'resize',
        'cursor',
        'pointer-events',
        'user-select',
        //
        'unicode-bidi',
        'direction',
        'columns',
        'column-span',
        'column-width',
        'column-count',
        'column-fill',
        'column-gap',
        'column-rule',
        'column-rule-width',
        'column-rule-style',
        'column-rule-color',
        'break-before',
        'break-inside',
        'break-after',
        'page-break-before',
        'page-break-inside',
        'page-break-after',
        'orphans',
        'widows',
        'zoom',
        'max-zoom',
        'min-zoom',
        'user-zoom',
        'orientation',
        'fill',
        'stroke',
        //
        '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',
        'animation-fill-mode',
      ],
      {
        unspecified: 'bottom',
        severity: 'warning',
      },
    ],
    // 屏蔽属性按字母顺序检查
    'order/properties-alphabetical-order': null,
  },
};

3.3 在 HTML 中使用

在根节点加入 customSyntax: 'postcss-html':

json 复制代码
{
  plugins: ...
  customSyntax: 'postcss-html',
  rules: ...
}

4 结合 Husky

利用 Husky 在 git commit 时自动校验文件中的样式内容,如不符合规范,则文件不能被 commit。详细操作见《前端工程化工具系列(五)------ Husky(v9.0.11)&lint-staged(v15.2.5):代码提交前的自动审查利器》中的 2.1节。

5 结合 VS Code

配合 VS Code 插件,可在做文件保存时自动修复错误。详细操作见《前端工程化工具系列(六)------ VS Code(v1.89.1):强大的代码编辑器》。

相关推荐
shoubepatien15 分钟前
JavaWeb_Web基础
java·开发语言·前端·数据库·intellij-idea
WordPress学习笔记23 分钟前
wordpress外贸主题Google地图添加(替换)方案
前端·wordpress·wordpress地图
码农秋1 小时前
Element Plus DatePicker 日期少一天问题:时区解析陷阱与解决方案
前端·vue.js·elementui·dayjs
未来之窗软件服务1 小时前
未来之窗昭和仙君(五十六)页面_预览模式——东方仙盟筑基期
前端·仙盟创梦ide·东方仙盟·昭和仙君·东方仙盟架构
top_designer1 小时前
Illustrato:钢笔工具“退休”了?Text to Vector 零基础矢量生成流
前端·ui·aigc·交互·ux·设计师·平面设计
星哥说事1 小时前
星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!
前端
donecoding1 小时前
前端AI开发:为什么选择SSE,它与分块传输编码有何不同?axios能处理SSE吗?
前端·人工智能
安_1 小时前
<style scoped>跟<style>有什么区别
前端·vue
姝然_95271 小时前
Claude Code 命令完整文档
前端
wjcroom1 小时前
web版进销存的设计到实现一
前端