PostCSS Nested让我告别繁琐CSS嵌套写法提升开发效率

东方玉鑫 工具 阅读 1,252
赞 17 收藏
二维码
手机扫码查看
反馈

postcss-nested到底怎么用,折腾了一下午才搞明白

今天在重构项目样式的时候,想把sass换成postcss配合postcss-nested来实现嵌套语法,结果折腾了一下午才搞明白怎么配置。说白了就是想用css嵌套语法:

PostCSS Nested让我告别繁琐CSS嵌套写法提升开发效率

.parent {
  color: red;
  
  .child {
    color: blue;
  }
}

而不是写成这样:

.parent { color: red; }
.parent .child { color: blue; }

一开始的配置完全不起作用

我在vite.config.js里加了postcss插件,但是编译出来的还是老样子,嵌套语法完全没转换。查了半天发现是配置位置的问题,这里我踩了个坑。

最初我是这么配的:

// vite.config.js
export default {
  css: {
    postcss: {
      plugins: [
        require('postcss-nested')
      ]
    }
  }
}

但这样根本不起作用,编译的时候嵌套语法还是原样输出。折腾了半天发现,vite的postcss配置不能这么写,得在项目根目录建postcss.config.js文件才行。

正确的配置方式

后来试了下发现,要创建一个postcss.config.js文件:

// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-nested')({
      // 配置选项
      bubble: ['at-rule'],
      unwrap: ['at-rule']
    })
  ]
}

或者如果用esm的话:

// postcss.config.mjs
import nested from 'postcss-nested'

export default {
  plugins: [
    nested({
      bubble: ['media', 'supports'], 
      unwrap: ['at-rule']
    })
  ]
}

这样配置完之后,嵌套语法就能正常转换了。不过这里要注意,npm install postcss-nested的时候还要同时安装postcss,因为postcss-nested依赖postcss。

还遇到个babel转义的问题

配置好之后发现另一个问题,某些情况下css会被babel误处理。特别是当css写在js文件里的时候,babel可能会提前把花括号当成js语法处理掉。

解决方案是在.babelrc里加个配置:

{
  "plugins": [
    ["@babel/plugin-syntax-jsx", { "allowNamespaces": true }]
  ],
  "exclude": ["**/*.css", "**/*.scss"]
}

这样babel就不会去动css文件了。当然正常情况是js和css分离的,这种情况比较少见,但我当时正好在做一个动态样式组件,所以遇到了。

嵌套层级限制的问题

还有一个需要注意的地方,postcss-nested默认是支持无限级嵌套的,但太深的嵌套会导致生成的选择器过长。比如:

.level1 {
  .level2 {
    .level3 {
      .level4 {
        .level5 {
          color: red; /* 生成 .level1 .level2 .level3 .level4 .level5 */
        }
      }
    }
  }
}

虽然语法上没问题,但生成的选择器太长了,可能会影响性能。一般建议嵌套不超过3层,超过3层就应该考虑重构了。

与其他postcss插件的配合

postcss-nested通常和其他postcss插件配合使用,比如postcss-preset-env、autoprefixer等。这时候插件顺序很重要:

// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-import'),
    require('postcss-nested'),
    require('postcss-preset-env')({
      stage: 3,
      features: {
        'nesting-rules': false // 注意这里要设为false,因为nested插件已经处理了
      }
    }),
    require('autoprefixer')
  ]
}

这里的顺序很重要,postcss-import要放在最前面处理@import,然后postcss-nested处理嵌套,最后autoprefixer处理浏览器前缀。如果顺序错了,可能嵌套语法就没法正常转换。

还有个小细节,postcss-preset-env里有个nesting-rules特性,默认是开启的,但既然用了postcss-nested插件,这个特性就要关掉,避免重复处理。

实际开发中的几个坑

在实际项目中用的时候还遇到了几个小问题。首先是伪类和伪元素的嵌套,刚开始不知道怎么写:

.button {
  &:hover {
    background: #f0f0f0;
  }
  
  &::before {
    content: '';
    display: block;
  }
  
  &:not(.disabled) {
    cursor: pointer;
  }
}

这种写法是可以的,&符号会被正确替换成父选择器。但在某些情况下,比如媒体查询里的嵌套:

.container {
  width: 100%;
  
  @media (min-width: 768px) {
    width: 50%;
    
    .item {
      display: inline-block;
    }
  }
}

这种写法也是支持的,但要注意媒体查询里的嵌套规则。postcss-nested的bubble选项控制哪些at-rule(比如@media)里面的规则会被提升到外层。

另一个问题是调试时的source map。因为css经过了postcss处理,如果source map没配置好,浏览器里看到的行号可能是错的。一般vite和webpack都自动处理了,但如果自己配构建工具要注意这点。

性能方面的一些考虑

postcss-nested确实方便,但也要注意性能。每次修改css都需要postcss重新处理,如果项目很大,嵌套很复杂,编译速度可能会受影响。

一般小到中型项目都不会有问题,但如果项目特别大,可以考虑缓存或者按需加载。另外嵌套越深,postcss需要处理的节点越多,也会影响编译速度。

还有就是输出的css体积问题。嵌套语法有时候会产生冗余的选择器,虽然现在gzip压缩效果很好,但还是要注意。可以用postcss-discard-duplicates这样的插件来优化。

以上是我踩坑后的总结

总的来说postcss-nested用起来还是很方便的,特别是习惯了sass嵌套语法的开发者。配置也不算复杂,主要就是注意插件顺序和一些边缘情况的处理。

这个工具的维护也挺活跃,兼容性也不错。相比sass的好处是不需要额外的预处理器,配合现代构建工具使用很方便。

以上是我踩坑后的总结,如果你有更好的方案或者遇到过类似问题,欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论