解决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 // 直接使用数字值
    })
  ]
};
相关推荐
bitbitDown5 分钟前
重构缓存时踩的坑:注释了三行没用的代码却导致白屏
前端·javascript·vue.js
xiaopengbc9 分钟前
火狐(Mozilla Firefox)浏览器离线安装包下载
前端·javascript·firefox
用户0165238444129 分钟前
Webpack5 入门与实战,前端开发必备技能无密
前端
小高00730 分钟前
🔥🔥🔥前端性能优化实战手册:从网络到运行时,一套可复制落地的清单
前端·javascript·面试
古夕32 分钟前
my-first-ai-web_问题记录01:Next.js的App Router架构下的布局(Layout)使用
前端·javascript·react.js
杨超越luckly38 分钟前
HTML应用指南:利用POST请求获取上海黄金交易所金价数据
前端·信息可视化·金融·html·黄金价格
Jerry43 分钟前
Compose 中的基本布局
前端
Hilaku1 小时前
深入WeakMap和WeakSet:管理数据和防止内存泄漏
前端·javascript·性能优化
Juchecar1 小时前
常见的 HTML 标签及 CSS 选择器速查表
前端
前端程序猿i1 小时前
用本地代理 + ZIP 打包 + Excel 命名,优雅批量下载跨域 PDF
前端·javascript·vue.js·html