postcss-load-config 能够自动读取项目中的 postcss
相关配置文件。
该功能通常会内置在上述的 postcss-cli
、postcss-loader
或 gulp-postcss
等。
配置文件及其优先级
譬如,可以在 package.json
中添加 postcss
字段:
{
"postcss": {
"parser": "sugarss",
"map": false,
"plugins": {
"postcss-plugin": {}
}
}
}
也可以在项目中专门创建配置文件,可命名为以下形式:
.postcssrc
、.postcssrc.(json|yml)
.postcssrc.(js|cjs|mjs|ts)
postcss.config.(js|cjs|mjs|ts)
其中,package.json
中的 postcss
字段优先级最高,这一点确实比较反人类。
关于更加详细的优先级列表,可以点击参考postcss-load-config源码。
另外,postcss-load-config
对于配置优先级的判断,使用了 lilconfig
第三方库。
而 lilconfig
中对于配置优先级的判断可以点击参考lilconfig源码。
我们在实际项目应用中,推荐创建 JavaScript
类型的配置文件,因为方便后续扩展。
配置详解
在项目开发中,常用的配置文件,大致有 JSON
和 JS
两种类型。
大多数场景下,我们只需要在配置文件中指明要使用的插件即可。
譬如,在 JSON
中的形式:
{
"plugins": {
"postcss-import": {},
"autoprefixer": {}
}
}
在 JS
中的形式:
module.exports = {
plugins: {
'postcss-import': {},
autoprefixer: {}
}
}
以上,都是将 plugins
属性声明为了对象形式。postcss
会自动根据配置加载插件。
但我们倾向于在 JS
中将 plugins
作为数组形式使用:
const postcssImport = require('postcss-import')
const autoprefixer = require('autoprefixer')
module.exports = {
plugins: [
postcssImport({}),
autoprefixer({})
]
}
这种形式更加易读,且与 rollup
的插件使用机制类似,方便统一记忆。
配置文件中除了 plugins
属性外,还有其他可配置属性。整理如下:
Name | Type | Default | Description |
---|---|---|---|
from | {String} | undefined | Source File Path |
to | {String} | undefined | Destination File Path |
map | `{String | Object}` | false |
parser | `{String | Function}` | false |
syntax | `{String | Function}` | false |
stringifier | `{String | Function}` | false |
其中的 map
、parser
、syntax
、stringifier
属性在某些场景下,我们可以进行自定义配置。
而 from
和 to
在大多数情况下,都无需配置。
在 postcss-cli
中,是通过 CLI Arguments
来定义 from
和 to
的,譬如:
postcss -o styles/output.css styles/entry.css
而像 postcss-loader
、 gulp-postcss
是通过 STDIN
或者文件流来获取输入和输出的。
如果要问我,什么时机下可以在 .postcssrc
配置文件中使用 from
和 to
属性?
我的答案是,使用 postcss-load-config
自定义一个支持 from
和 to
属性的插件,譬如下文中的 loaderWithPostcss
代码可以做一些拓展。
context上下文
之所以要推荐 postcss
配置文件使用 JavaScript
文件类型,还有一个原因在于该类型的配置文件可以导出一个携带 context
上下文的函数。
在此基础下,我们可以更加灵活的对 postcss
配置文件进行自定义。
// .postcssrc.js
module.exports = ctx => {
console.log(ctx)
const { options: { map } } = ctx
return {
map,
plugins: [
require('postcss-import')({}),
require('autoprefixer')({})
]
}
}
当我们使用 postcss-cli
来打包时,执行:
postcss -o styles/output.css styles/entry.css
ctx
打印如下:
{
cwd: '/path/to/postcss',
env: 'development',
options: {
map: { inline: true },
parser: undefined,
syntax: undefined,
stringifier: undefined
},
file: {
dirname: '/path/to/postcss/styles',
basename: 'entry.css',
extname: '.css'
}
}
如果是在 webpack
项目下使用 postcss-loader,那么这里的 ctx
会有 [ 'mode', 'file', 'webpackLoaderContext', 'env', 'options' ]
这 5
个属性。
我们可以根据这些属性,对配置文件作一些更加精细化的自定义控制。
代码应用
在工程化项目中,很多扩展(postcss-loader
、gulp-postcss
等)都会内置 postcss-load-config
功能。
对于使用这些扩展的方式,核心方面分为三部分:
- 定义配置文件,如
.postcssrc.js
。 - 定义
CSS
的入口。 - 定义
CSS
的出口。
相当于扩展给使用者预留了三个缺口项,以供使用者自定义。
如果我们要创建一个类似功能的扩展,核心代码大致如下:
function loaderWithPostcss ({ entry, output }) {
const { readFileSync, writeFileSync } = require('fs')
const postcss = require('postcss')
const postcssrc = require('postcss-load-config')
const css = readFileSync(entry, 'utf8')
const ctx = { parser: true, map: 'inline' }
postcssrc(ctx).then(({ plugins, options }) => {
// 这里要对options.from做判断 不然下面的postcss在运行时 会警告 Without `from` option PostCSS could generate wrong source map and will not find Browserslist config. Set it to CSS file path or to `undefined` to prevent this warning.
options.from = options.from || undefined
postcss(plugins)
.process(css, options)
.then((result) => {
const { css } = result
writeFileSync(output, css, 'utf8')
})
})
}
const { resolve } = require('path')
loaderWithPostcss({
entry: resolve(__dirname, './styles/entry.css'),
output: resolve(__dirname, './styles/output.css')
})