解决postcss-px-to-viewport-8-plugin 设置include不生效的问题

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 // 直接使用数字值
    })
  ]
};
相关推荐
冰暮流星13 分钟前
css新增盒子属性——尺寸调节
前端·css
程序员爱钓鱼15 分钟前
Python编程实战 - 函数与模块化编程 - 函数的定义与调用
前端·后端·python
欧阳码农15 分钟前
使用AI生成的页面总是被一眼认出来怎么办?1分钟给你解决
前端·后端
IT_陈寒22 分钟前
7个鲜为人知的JavaScript性能优化技巧,让你的应用提速50%!
前端·人工智能·后端
艾小码42 分钟前
前端别再乱存数据了!这3种存储方案让你的应用快如闪电
前端·javascript
黄毛火烧雪下43 分钟前
HTML 的底层原理
前端·html
Moment1 小时前
面经分享——字节前端一面
前端·javascript·面试
十步杀一人_千里不留行3 小时前
Google 登录集成教程(Web + Expo 移动端)
前端
gAlAxy...6 小时前
IntelliJ IDEA 四种项目构建:从普通 Java 到 Maven Web 项目
前端·firefox
my一阁6 小时前
2025-web集群-问题总结
前端·arm开发·数据库·nginx·负载均衡·web