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