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扩展,目前最新版是v1.2.1

2.2 在项目下的.vscode/setting.json中添加开启保存自动格式化的配置

{
   "editor.defaultFormatter": "esbenp.prettier-vscode",
   "[less]": {
     "editor.defaultFormatter": "stylelint.vscode-stylelint"
   },
   "[css]": {
     "editor.defaultFormatter": "stylelint.vscode-stylelint"
   },
    "editor.codeActionsOnSave": {
        "source.fixAll.stylelint": true
    },
    // 关闭vscode自带的css,less,scss报错提示
    "css.validate": false,
    "less.validate": false,
    "scss.validate": false,
    "stylelint.validate": ["css", "less"]
}

.vscode/extension.json添加推荐扩展

{
  "recommendations": [
    "stylelint.vscode-stylelint",
    "esbenp.prettier-vscode",
    "streetsidesoftware.code-spell-checker"
  ]
}

3. 如何批量修复项目样式文件?

3.1 安装stylelint相关的npm依赖包

yarn add -D stylelint@latest stylelint-config-standard@latest stylelint-order@latest stylelint-config-recess-order@latest postcss-less@latest 

stylelint-config-standard

作用:配置 Stylelint 规则。
官方的代码风格 :stylelint-config-standard。该风格是 Stylelint 的维护者汲取了 GitHub、Google、Airbnb 多家之长生成的。

stylelint-order

该插件的作用是强制你按照某个顺序编写 css。例如先写定位,再写盒模型,再写内容区样式,最后写 CSS3 相关属性。这样可以极大的保证我们代码的可读性。

stylelint-config-recess-order

stylelint-order 插件的第三方配置

3.2 如果是vue项目,还要安装

yarn add -D postcss-html stylelint-config-recommended-vue

3.3 根目录添加.stylelintrc.js 文件

module.exports = {
  extends: [
    "stylelint-config-standard",
    "stylelint-order",
    "stylelint-config-recess-order",
    "stylelint-config-recommended-vue",
  ],
  customSyntax: "postcss-less",
  // ===== vue项目要增加的配置项============
  overrides: [
    {
      files: ["**/*.vue"],
      customSyntax: "postcss-html",
    },
  ],
  // ======================================
  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, // 关闭必须设置通用字体的规则
    // 动画名称前,可以加浏览器前缀  如@-webkit-keyframes bounce
    "at-rule-no-vendor-prefix": null,
    // id选择器为了兼容#__vconsole, 修改短横线命名
    "selector-id-pattern": "^([#_a-z][_a-z0-9]*)(-[a-z0-9]+)*$",
    // class选择器修改为同时支持短横线和小驼峰
    // 为了兼容css module,.coupon-backCard-modal,.czH5shouye-huodong,.czcommon_36_youjiantou_qianhui这样的类名
    "selector-class-pattern":
      "(^([#_a-z][a-zA-Z0-9]*)(-[a-zA-Z0-9]+)*$)|(^[a-z][a-zA-Z0-9]+$)|(^([a-z][a-z0-9]*)(_[a-z0-9]+)*$)",
    // 动画名称命名,为了兼容这种命名btnScaleAni,添加小驼峰命名规则
    "keyframes-name-pattern": "(^([a-z][_a-z0-9]*)(-[a-z0-9]+)*$)|(^([a-z][a-zA-Z0-9]+)*$)",
    // url地址不加引号
    "function-url-quotes": null,
    // 保留各大浏览器不兼容的样式属性名前缀, 如 -moz-user-select: auto;
    "property-no-vendor-prefix": null,
    // 保留各大浏览器不兼容的样式属性值前缀,display: -webkit-box;
    "value-no-vendor-prefix": null,
    // 保留各大浏览器不兼容的选择器前缀,如input::-webkit-input-placeholder
    "selector-no-vendor-prefix": null,
    // 屏蔽background-color: rgba(0, 0, 0, 0.5);这种写法引起的警告
    "color-function-notation": "legacy",
    // 屏蔽background-color: rgba(0, 0, 0, 0.5);中0.5引起的警告
    "alpha-value-notation": "number",
    // css属性值中小数点之后数字的最大位数
    "number-max-precision": 10,
    "property-no-unknown": [
      true,
      {
        ignoreProperties: ["box-flex"], // 忽略某些未知属性的检测
      },
    ],
    "selector-pseudo-element-no-unknown": [
      true,
      {
        ignorePseudoElements: ["ng-deep", "input-placeholder"], // 忽略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",
        ],
      },
    ],
  },
};

命名参考:

  • 短横线命名(kebab-case): ^([a-z][a-z0-9]*)(-[a-z0-9]+)*$
  • 小驼峰命名(lowerCamelCase): ^[a-z][a-zA-Z0-9]+$
  • 蛇形命名(snake_case): ^([a-z][a-z0-9]*)(_[a-z0-9]+)*$
  • 大驼峰命名(UpperCamelCase): ^[A-Z][a-zA-Z0-9]+$

3.4 在package.json中添加stylelint样式修复命令

{
    "scripts": {
        "fix:format": "prettier --write ./src/**/*.{js,jsx,ts,tsx}",
        "lint:less": "stylelint --fix src/**/*.{css,less}",
        // ...
    },
}  

3.5 在终端下执行yarn lintless命令, 就能对不符合配置样式规范的代码进行修复,部分问题还需要进一步手动修复。

yarn lint:less

 

具体规则查询参考:

  [1] stylelint规则中文翻译 

  [2] stylelint构造及规则了解

  [3] 使用stylelint规范vue项目

  [4] stylelint常见问题 

  [5]  用stylelint校验less,vue文件方法

 

posted @ 2021-07-03 12:40  孤舟蓑翁  阅读(1245)  评论(0编辑  收藏  举报