Skip to content

PostCSS 常用配置

大白话解释: PostCSS 就像"CSS 的 Babel"。Babel 把新语法的 JS 转换成旧语法,PostCSS 把新语法的 CSS 转换成旧语法,加上浏览器前缀、压缩等处理。

预处理器 vs 后处理器:

  • 预处理器(Less/Sass):写的时候用增强语法,编译成普通 CSS
  • 后处理器(PostCSS):编译后的 CSS 再加工处理

PostCSS 能做什么?

  • 自动加浏览器前缀display: flex 自动变成 display: -webkit-box 等带前缀写法
  • CSS 转换:把新的 CSS 语法转换成旧浏览器能识别的
  • 压缩:去除空格、注释,减小文件体积
  • px 转 vw/rem:移动端适配

什么时候用 PostCSS?

  • 需要兼容旧浏览器(自动加前缀)
  • 移动端适配(px 转 vw/rem)
  • 使用新的 CSS 语法(如嵌套、自定义属性)

PostCSS 是 CSS 后处理器,通过插件转换 CSS 代码。Vite、Webpack 等构建工具都内置了 PostCSS 支持。

什么是 PostCSS

预处理器 vs 后处理器

工具类型作用代表
Less/Sass预处理器编写时扩展 CSS 语法变量、嵌套、Mixin
PostCSS后处理器编译后转换/优化 CSS前缀、转换、压缩

PostCSS 工作流程

源 CSS → PostCSS 插件链 → 输出 CSS

/* 输入 */
::placeholder {
  color: #999;
}

.display-flex {
  display: flex;
}

/* 输出(经过 autoprefixer) */
::-moz-placeholder {
  color: #999;
}
::-ms-input-placeholder {
  color: #999;
}
::placeholder {
  color: #999;
}

.display-flex {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}

基本配置

配置文件

js
// postcss.config.js(推荐放在项目根目录)
module.exports = {
  plugins: [
    require('autoprefixer'),  // 自动添加浏览器前缀
    require('cssnano')        // 生产环境压缩 CSS
  ]
}

// postcss.config.cjs(.cjs 扩展名强制使用 CommonJS 格式,不受 package.json 的 type 字段影响)
module.exports = {
  plugins: [
    require('autoprefixer'),  // 同上,适用于 package.json 设了 "type": "module" 的项目
    require('cssnano')
  ]
}

// package.json(不想单独建配置文件时可以用这种方式)
{
  "postcss": {
    "plugins": {
      "autoprefixer": {},     // 默认配置
      "cssnano": {}           // 默认配置
    }
  }
}

Vite 中使用

Vite 内置 PostCSS 支持,只需创建 postcss.config.js 即可自动生效。

js
// postcss.config.js
module.exports = {
  plugins: {
    'autoprefixer': {},
    'cssnano': process.env.NODE_ENV === 'production' ? {} : false
  }
}

Webpack 中使用

js
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader'
        ]
      }
    ]
  }
}

常用插件

autoprefixer(自动添加浏览器前缀)

bash
npm install -D autoprefixer
js
// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

browserslist 配置

json
// package.json
{
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead",
    "not ie 11"
  ]
}
json
// 或单独的 .browserslistrc 文件
> 1%
last 2 versions
not dead
not ie 11

常用 browserslist 查询

> 1%              全球使用率 > 1% 的浏览器
last 2 versions   每个浏览器的最后 2 个版本
not dead          官方不再维护的浏览器
not ie 11         排除 IE 11
defaults          browserslist 默认配置
ie 6-8            IE 6-8
chrome >= 50      Chrome 50+

输入输出

css
/* 输入 */
::placeholder {
  color: #999;
}

.flex {
  display: flex;
}

.gradient {
  background: linear-gradient(to bottom, #fff, #000);
}

/* 输出 */
::-moz-placeholder {
  color: #999;
}
::-ms-input-placeholder {
  color: #999;
}
::placeholder {
  color: #999;
}

.flex {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}

.gradient {
  background: -webkit-linear-gradient(top, #fff, #000);
  background: linear-gradient(to bottom, #fff, #000);
}

postcss-px-to-viewport(px 转 vw)

bash
npm install -D postcss-px-to-viewport
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-px-to-viewport')({
      viewportWidth: 375,        // 设计稿宽度(iPhone 标准 375px)
      viewportHeight: 667,       // 设计稿高度(可选,一般不用)
      unitPrecision: 5,          // 转换后保留几位小数
      viewportUnit: 'vw',        // 转换后的目标单位(vw / vh / vmin / vmax)
      selectorBlackList: [],     // 忽略的选择器(如 ['.ignore'] 不转换)
      minPixelValue: 1,          // 小于 1px 的值不转换(避免 0.5px 被转)
      mediaQuery: false,         // 是否转换媒体查询里的 px
      exclude: [/node_modules/]  // 排除第三方库(避免改动 UI 框架样式)
    })
  ]
}

输入输出

css
/* 输入(设计稿宽度 375px) */
.box {
  width: 375px;
  height: 100px;
  font-size: 14px;
  padding: 10px 20px;
}

/* 输出 */
.box {
  width: 100vw;
  height: 26.66667vw;
  font-size: 3.73333vw;
  padding: 2.66667vw 5.33333vw;
}

忽略特定选择器

css
/* 不转换:在属性值后添加 px-to-viewport-ignore 注释 */
.box {
  width: 375px; /*px-to-viewport-ignore*/
}

/* 或使用选择器黑名单 */
.ignore {
  width: 375px;
}
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-px-to-viewport')({
      selectorBlackList: ['ignore']
    })
  ]
}

postcss-pxtorem(px 转 rem)

bash
npm install -D postcss-pxtorem
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-pxtorem')({
      rootValue: 16,             // 根元素字体大小
      propList: ['*'],           // 需要转换的属性
      selectorBlackList: [],     // 忽略的选择器
      replace: true,             // 替换原始值
      mediaQuery: false,         // 是否转换媒体查询
      minPixelValue: 1           // 最小转换单位
    })
  ]
}

配合 rem.js 使用

js
// src/utils/rem.js
function setRem() {
  const baseSize = 16
  const designWidth = 375
  const clientWidth = document.documentElement.clientWidth || window.innerWidth
  
  const scale = clientWidth / designWidth
  document.documentElement.style.fontSize = baseSize * scale + 'px'
}

// 初始化
setRem()

// 监听窗口变化
window.addEventListener('resize', setRem)
html
<!-- index.html -->
<script src="./src/utils/rem.js"></script>

输入输出

css
/* 输入 */
.box {
  width: 375px;
  font-size: 14px;
  padding: 10px;
}

/* 输出 */
.box {
  width: 23.4375rem;
  font-size: 0.875rem;
  padding: 0.625rem;
}

postcss-px-to-viewport-8-plugin(适配 vant 4)

bash
npm install -D postcss-px-to-viewport-8-plugin
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-px-to-viewport-8-plugin')({
      viewportWidth: (file) => {
        return file.includes('vant') ? 375 : 750
      },
      viewportUnit: 'vw',
      selectorBlackList: ['.ignore'],
      minPixelValue: 1,
      mediaQuery: false
    })
  ]
}

cssnano(CSS 压缩)

bash
npm install -D cssnano
js
// postcss.config.js
module.exports = {
  plugins: [
    require('cssnano')({
      preset: 'default'  // 或 'advanced'
    })
  ]
}

预设选项

js
// default 预设(推荐)
require('cssnano')({
  preset: 'default'
})

// advanced 预设(更激进)
require('cssnano')({
  preset: 'advanced'
})

// 自定义配置
require('cssnano')({
  preset: ['default', {
    discardComments: {
      removeAll: true  // 移除所有注释
    },
    normalizeWhitespace: true,  // 压缩空白
    colormin: true,  // 压缩颜色
    minifyFontValues: true  // 压缩字体值
  }]
})

输入输出

css
/* 输入 */
.box {
  color: #ff0000;
  background-color: #ffffff;
  margin: 0px 0px 0px 0px;
  padding: 10px 20px;
  font-size: 14px;
  /* 这是注释 */
}

/* 输出 */
.box{color:red;background-color:#fff;margin:0;padding:10px 20px;font-size:14px}

postcss-nested(嵌套语法)

bash
npm install -D postcss-nested
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-nested')
  ]
}

输入输出

css
/* 输入 */
.nav {
  background: #fff;
  
  .item {
    padding: 10px;
    
    &:hover {
      background: #f5f5f5;
    }
    
    &.active {
      color: #1890ff;
    }
  }
}

/* 输出 */
.nav { background: #fff; }
.nav .item { padding: 10px; }
.nav .item:hover { background: #f5f5f5; }
.nav .item.active { color: #1890ff; }

postcss-import(@import 规则处理)

bash
npm install -D postcss-import
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-import')
  ]
}
css
/* 可以像 Sass 一样导入文件 */
@import './variables.css';
@import './mixins.css';
@import './components/button.css';

postcss-preset-env(现代 CSS 特性)

bash
npm install -D postcss-preset-env
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-preset-env')({
      stage: 3,           // CSS 特性阶段(0-4),3 表示候选推荐阶段,较为稳定
      features: {
        'nesting-rules': true,     // 嵌套
        'custom-properties': true, // CSS 变量
        'color-mod-function': true // 颜色函数(已废弃的 CSS 提案,建议用浏览器原生 color-mix() 代替)
      },
      autoprefixer: {
        grid: true  // Grid 布局前缀
      }
    })
  ]
}

支持的现代 CSS 特性

css
/* 嵌套 */
.nav {
  & .item {
    color: red;
    
    &:hover {
      color: blue;
    }
  }
}

/* 自定义属性 */
:root {
  --primary: #1890ff;
}

.button {
  color: var(--primary);
}

/* 自定义媒体查询 */
@custom-media --mobile (max-width: 767px);

@media (--mobile) {
  .sidebar {
    display: none;
  }
}

/* 自定义选择器 */
@custom-selector :--heading h1, h2, h3, h4, h5, h6;

:--heading {
  font-weight: bold;
}

postcss-write-svg(内联 SVG)

bash
npm install -D postcss-write-svg
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-write-svg')({
      utf8: true
    })
  ]
}
css
/* 生成 1px 边框 */
@svg border-1px {
  height: 2px;
  @rect {
    fill: var(--color, #ddd);
    width: 100%;
    height: 50%;
  }
}

.box {
  border-image: url("border-1px") 2 stretch;
}

postcss-aspect-ratio-mini(宽高比)

bash
npm install -D postcss-aspect-ratio-mini
css
/* 输入 */
.box {
  aspect-ratio: '16:9';
}

/* 输出 */
.box {
  position: relative;
}
.box::before {
  content: '';
  display: block;
  padding-top: 56.25%;
}

postcss-sorting(属性排序)

bash
npm install -D postcss-sorting
js
// postcss.config.js
module.exports = {
  plugins: [
    require('postcss-sorting')({
      order: [
        'custom-properties',
        'dollar-variables',
        'declarations',
        'at-rules',
        'rules'
      ],
      'properties-order': [
        'position',
        'top',
        'right',
        'bottom',
        'left',
        'display',
        'width',
        'height',
        'margin',
        'padding',
        'background',
        'border',
        'font',
        'color'
      ],
      'unspecified-properties-position': 'bottom'
    })
  ]
}
css
/* 输入 */
.box {
  color: red;
  position: absolute;
  width: 100px;
  top: 0;
}

/* 输出 */
.box {
  position: absolute;
  top: 0;
  width: 100px;
  color: red;
}

stylelint(CSS 检查工具)

bash
npm install -D stylelint stylelint-config-standard
js
// .stylelintrc.js
module.exports = {
  extends: 'stylelint-config-standard',
  rules: {
    'color-no-invalid-hex': true,                          // 禁止无效的十六进制颜色
    'declaration-colon-space-after': 'always',             // 冒号后必须有空格
    'indentation': 2,                                      // 缩进 2 空格
    'no-missing-end-of-source-newline': true               // 文件末尾必须有换行
  }
}

注意: 上面部分规则名(如 declaration-colon-space-after)在 stylelint v15+ 中已废弃,改用 @stylistic/declaration-colon-space-after 等 stylistic 插件。新版 stylelint-config-standard 已内置处理这些规则,建议直接用 extends 即可,无需手动指定。如需自定义规则,请查阅 stylelint 官方文档 确认当前版本支持的规则名。

bash
# 检查
npx stylelint "src/**/*.css"

# 自动修复
npx stylelint "src/**/*.css" --fix

组合配置

移动端项目配置

js
// postcss.config.js
module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-nested': {},
    'autoprefixer': {},
    'postcss-px-to-viewport': {
      viewportWidth: 375,
      unitPrecision: 5,
      viewportUnit: 'vw',
      minPixelValue: 1
    },
    'cssnano': process.env.NODE_ENV === 'production' ? {} : false
  }
}

PC 端项目配置

js
// postcss.config.js
module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-nested': {},
    'autoprefixer': {},
    'cssnano': process.env.NODE_ENV === 'production' ? {} : false
  }
}

Vant + 自定义配置

js
// postcss.config.js
module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-nested': {},
    'autoprefixer': {},
    'postcss-px-to-viewport-8-plugin': {
      viewportWidth: (file) => {
        return file.includes('vant') ? 375 : 750
      },
      selectorBlackList: ['.ignore']
    },
    'cssnano': process.env.NODE_ENV === 'production' ? {} : false
  }
}

Tailwind CSS 集成

bash
npm install -D tailwindcss postcss autoprefixer
js
// postcss.config.js
module.exports = {
  plugins: {
    tailwindcss: {},      // 解析 Tailwind 指令(@tailwind、@apply 等)
    autoprefixer: {},     // 自动添加浏览器前缀
  }
}
js
// tailwind.config.js(Tailwind 配置文件)
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './index.html',                // 扫描 HTML 文件中的类名
    './src/**/*.{vue,js,ts,jsx,tsx}' // 扫描 Vue/JS/TS 文件
  ],
  theme: {
    extend: {
      colors: {
        primary: '#1890ff',        // 自定义主题色
      }
    }
  },
  plugins: []
}
css
/* styles.css(主入口文件,必须引入这三个指令) */
@tailwind base;          /* 引入基础重置样式(reset) */
@tailwind components;    /* 引入组件层(可自定义组件样式) */
@tailwind utilities;     /* 引入工具类(如 flex、p-4、text-center) */

/* 自定义组件层:用 @layer 把自定义样式放入 components 层 */
@layer components {
  .btn {
    @apply px-4 py-2 rounded font-semibold;  /* @apply 复用 Tailwind 工具类 */
  }
  
  .btn-primary {
    @apply bg-blue-500 text-white hover:bg-blue-600;
  }
}
vue
<!-- Vue 组件中使用 Tailwind -->
<template>
  <div class="flex items-center justify-center h-screen bg-gray-100">
    <button class="btn btn-primary">点击我</button>
  </div>
</template>

Vue 项目配置

Vite + Vue

js
// postcss.config.js
module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-nested': {},
    'autoprefixer': {},
    'postcss-px-to-viewport': {
      viewportWidth: 375,
      selectorBlackList: ['van-']
    },
    'cssnano': process.env.NODE_ENV === 'production' ? {} : false
  }
}

全局注入变量

js
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  css: {
    postcss: './postcss.config.js',
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/variables" as *;`
      }
    }
  }
})

自定义插件

基础插件结构

js
// postcss-plugin-example.js
module.exports = (opts = {}) => {
  return {
    postcssPlugin: 'postcss-plugin-example',
    
    // 遍历声明
    Declaration(decl) {
      if (decl.prop === 'color' && decl.value === 'red') {
        decl.value = '#ff0000';
      }
    },
    
    // 遍历规则
    Rule(rule) {
      // ...
    },
    
    // 遍历 At 规则
    AtRule(atRule) {
      // ...
    },
    
    // 遍历注释
    Comment(comment) {
      // ...
    },
    
    // 访问所有节点
    Once(root) {
      // 遍历所有节点
      root.walkDecls((decl) => {
        // ...
      });
    }
  }
}
module.exports.postcss = true;

实际示例:rem 转换插件

js
// postcss-rem-converter.js
module.exports = (opts = {}) => {
  const { rootValue = 16, unitPrecision = 5 } = opts;
  
  return {
    postcssPlugin: 'postcss-rem-converter',
    Declaration(decl) {
      if (decl.value.includes('px')) {
        const pxValue = parseFloat(decl.value);
        const remValue = (pxValue / rootValue).toFixed(unitPrecision);
        decl.value = `${remValue}rem`;
      }
    }
  }
}
module.exports.postcss = true;

// postcss.config.js
module.exports = {
  plugins: [
    require('./postcss-rem-converter')({
      rootValue: 16
    })
  ]
}

实际示例:自动添加注释

js
// postcss-add-comments.js
module.exports = () => {
  return {
    postcssPlugin: 'postcss-add-comments',
    Rule(rule) {
      // 为每个规则添加文件路径注释
      if (rule.source && rule.source.input && rule.source.input.file) {
        const comment = rule.first.clone({
          text: `Source: ${rule.source.input.file}`,
          type: 'comment'
        });
        rule.prepend(comment);
      }
    }
  }
}
module.exports.postcss = true;

常见问题

1. 插件顺序问题

js
// ❌ 错误顺序
plugins: [
  require('cssnano'),      // 压缩应该在最后
  require('autoprefixer')
]

// ✅ 正确顺序
plugins: [
  require('postcss-import'),     // 1. 先处理导入
  require('postcss-nested'),     // 2. 处理嵌套
  require('autoprefixer'),       // 3. 添加前缀
  require('cssnano')             // 4. 最后压缩
]

2. 开发环境不压缩

js
// postcss.config.js
module.exports = {
  plugins: {
    'autoprefixer': {},
    'cssnano': process.env.NODE_ENV === 'production' ? {} : false
  }
}

3. 排除 node_modules

js
// postcss-px-to-viewport 配置
module.exports = {
  plugins: [
    require('postcss-px-to-viewport')({
      exclude: [/node_modules/]
    })
  ]
}

4. 配置不生效

bash
# 检查配置文件位置
# 项目根目录下

# 重启开发服务器
npm run dev

# 清除缓存
rm -rf node_modules/.cache

参考

个人学习笔记,部分内容借助 AI 辅助整理,仅供查阅参考,请以官方文档为准