postcss-px-to-viewport-8-plugin文档中API参数说明
参数 | 说明 | 类型 | 默认值 | |
---|---|---|---|---|
unitToConvert |
需要转换的单位,默认为 px | string |
px | |
viewportWidth |
设计稿的视口宽度,如传入函数,函数的参数为当前处理的文件路径,函数返回 undefind 跳过转换 |
`number | Function` | 320 |
unitPrecision |
单位转换后保留的精度 | number |
5 | |
propList |
能转化为 vw 的属性列表 | string[] |
['*'] | |
viewportUnit |
希望使用的视口单位 | string |
vw | |
fontViewportUnit |
字体使用的视口单位 | string |
vw | |
selectorBlackList |
需要忽略的 CSS 选择器,不会转为视口单位,使用原有的 px 等单位 | string[] |
[] | |
minPixelValue |
设置最小的转换数值,如果为 1 的话,只有大于 1 的值会被转换 | number |
1 | |
mediaQuery |
媒体查询里的单位是否需要转换单位 | boolean |
false | |
replace |
是否直接更换属性值,而不添加备用属性 | boolean |
true | |
landscape |
是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape) |
boolean |
false | |
landscapeUnit |
横屏时使用的单位 | string |
vw | |
landscapeWidth |
横屏时使用的视口宽度,,如传入函数,函数的参数为当前处理的文件路径,函数返回 undefind 跳过转换 |
number |
568 | |
exclude |
忽略某些文件夹下的文件或特定文件,例如 node_modules 下的文件,如果值是一个正则表达式,那么匹配这个正则的文件会被忽略,如果传入的值是一个数组,那么数组里的值必须为正则 | Regexp |
undefined | |
include |
需要转换的文件,例如只转换 'src/mobile' 下的文件 (include: //src/mobile// ),如果值是一个正则表达式,将包含匹配的文件,否则将排除该文件, 如果传入的值是一个数组,那么数组里的值必须为正则 |
Regexp |
undefined |
按照文档中说明配置调试了很久,发现所有页面都会生效,不仅仅是include里的页面
module.exports
plugins: [
// 使用 postcss-px-to-viewport-8-plugin 处理大部分页面
require('postcss-px-to-viewport-8-plugin')({
unitToConvert: 'px', // 要转化的单位
unitPrecision: 4, // 转换后的精度,即小数点位数
propList: ['*', '!font-size','!width', '!height', '!background-position'], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
viewportUnit: 'vw', // 指定需要转换成的视窗单位,默认vw
fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw
selectorBlackList: ['ignore-', 'ige'], // 指定不转换为视窗单位的类名,
minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
replace: true, // 是否转换后直接更换属性值
include: [/\/src\/pages\/test-vw\//],
landscape: false, // 是否处理横屏情况
viewportWidth: 375 // 直接使用数字值
})
]
};
查看源代码发现作者并没有实现include相关的逻辑,使用时注意避坑

js
const postcssPxToViewport = (options: OptionType) => {
const opts = objectAssign({}, defaults, options);
const pxRegex = getUnitRegexp(opts.unitToConvert);
const satisfyPropList = createPropListMatcher(opts.propList);
const landscapeRules: AtRule[] = [];
return {
postcssPlugin: 'postcss-px-to-viewport',
Once(css: Root, { result }: { result: any }) {
// @ts-ignore 补充类型
css.walkRules((rule: RuleType) => {
// Add exclude option to ignore some files like 'node_modules'
const file = rule.source?.input.file || '';
if (opts.exclude && file) {
if (Object.prototype.toString.call(opts.exclude) === '[object RegExp]') {
if (isExclude(opts.exclude as RegExp, file)) return;
} else if (
// Object.prototype.toString.call(opts.exclude) === '[object Array]' &&
opts.exclude instanceof Array
) {
for (let i = 0; i < opts.exclude.length; i++) {
if (isExclude(opts.exclude[i], file)) return;
}
} else {
throw new Error('options.exclude should be RegExp or Array.');
}
}
if (blacklistedSelector(opts.selectorBlackList, rule.selector)) return;
if (opts.landscape && !rule.parent?.params) {
const landscapeRule = rule.clone().removeAll();
rule.walkDecls((decl) => {
if (decl.value.indexOf(opts.unitToConvert) === -1) return;
if (!satisfyPropList(decl.prop)) return;
let landscapeWidth;
if (typeof opts.landscapeWidth === 'function') {
const num = opts.landscapeWidth(file);
if (!num) return;
landscapeWidth = num;
} else {
landscapeWidth = opts.landscapeWidth;
}
landscapeRule.append(
decl.clone({
value: decl.value.replace(
pxRegex,
createPxReplace(opts, opts.landscapeUnit, landscapeWidth),
),
}),
);
});
if (landscapeRule.nodes.length > 0) {
landscapeRules.push((landscapeRule as unknown) as AtRule);
}
}
if (!validateParams(rule.parent?.params, opts.mediaQuery)) return;
rule.walkDecls((decl, i) => {
if (decl.value.indexOf(opts.unitToConvert) === -1) return;
if (!satisfyPropList(decl.prop)) return;
const prev = decl.prev();
// prev declaration is ignore conversion comment at same line
if (prev && prev.type === 'comment' && prev.text === ignoreNextComment) {
// remove comment
prev.remove();
return;
}
const next = decl.next();
// next declaration is ignore conversion comment at same line
if (next && next.type === 'comment' && next.text === ignorePrevComment) {
if (/\n/.test(next.raws.before!)) {
result.warn(
`Unexpected comment /* ${ignorePrevComment} */ must be after declaration at same line.`,
{ node: next },
);
} else {
// remove comment
next.remove();
return;
}
}
let unit;
let size;
const { params } = rule.parent;
if (opts.landscape && params && params.indexOf('landscape') !== -1) {
unit = opts.landscapeUnit;
if (typeof opts.landscapeWidth === 'function') {
const num = opts.landscapeWidth(file);
if (!num) return;
size = num;
} else {
size = opts.landscapeWidth;
}
} else {
unit = getUnit(decl.prop, opts);
if (typeof opts.viewportWidth === 'function') {
const num = opts.viewportWidth(file);
if (!num) return;
size = num;
} else {
size = opts.viewportWidth;
}
}
const value = decl.value.replace(pxRegex, createPxReplace(opts, unit!, size));
if (declarationExists((decl.parent as unknown) as ParentExtendType[], decl.prop, value))
return;
if (opts.replace) {
decl.value = value;
} else {
decl.parent?.insertAfter(i, decl.clone({ value }));
}
});
});
// if (landscapeRules.length > 0) {
// const landscapeRoot = new AtRule({
// params: '(orientation: landscape)',
// name: 'media',
// });
// landscapeRules.forEach((rule) => {
// landscapeRoot.append(rule);
// });
// css.append(landscapeRoot);
// }
},
// https://www.postcss.com.cn/docs/writing-a-postcss-plugin
// Declaration Rule RuleExit OnceExit
// There two types or listeners: enter and exit.
// Once, Root, AtRule, and Rule will be called before processing children.
// OnceExit, RootExit, AtRuleExit, and RuleExit after processing all children inside node.
OnceExit(css: Root, { AtRule }: { AtRule: any }) {
// 在 Once里跑这段逻辑,设置横屏时,打包后到生产环境竖屏样式会覆盖横屏样式,所以 OnceExit再执行。
if (landscapeRules.length > 0) {
const landscapeRoot = new AtRule({
params: '(orientation: landscape)',
name: 'media',
});
landscapeRules.forEach(function(rule) {
landscapeRoot.append(rule);
});
css.append(landscapeRoot);
}
},
};
};
曲线救国方案, 通过设置exclude 在正则表达式中使用?!负向前瞻断言 来化解
module.exports
plugins: [
// 使用 postcss-px-to-viewport-8-plugin 处理大部分页面
require('postcss-px-to-viewport-8-plugin')({
unitToConvert: 'px', // 要转化的单位
unitPrecision: 4, // 转换后的精度,即小数点位数
propList: ['*', '!font-size','!width', '!height', '!background-position'], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
viewportUnit: 'vw', // 指定需要转换成的视窗单位,默认vw
fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw
selectorBlackList: ['ignore-', 'ige'], // 指定不转换为视窗单位的类名,
minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
replace: true, // 是否转换后直接更换属性值
exclude: [
/\/src\/pages\/(?!test-a|page-a)/,
// 排除第三方库
/node_modules\//
],
landscape: false, // 是否处理横屏情况
viewportWidth: 375 // 直接使用数字值
})
]
};