背景
看到项目中团队成员写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构造及规则了解