使用postcss-px-to-viewport-8-plugin将页面转响应式

使用postcss-px-to-viewport-8-plugin将页面转响应式

背景

公司最近接了个项目,其中要求部分页面在移动端正常显示,第一方案是使用响应式,但是设计稿最后出的和Web端差别较大同时两端的功能不是完全对齐的,所以决定使用两个模块去实现,通过路由来控制显示的页面。

说到这里第一时间想到的当然是大名鼎鼎的postcss-px-to-viewport插件咯,之前也用过不少次了,非常香,这次也理所当然的打算使用该方案。

postcss-px-to-viewport使用

因为我们使用的是next,next已经内置了postcss插件,所以我们下载postcss-px-to-viewport即可

css 复制代码
 pnpm install postcss-px-to-viewport --save-dev

然后在next项目的根目录创建postcss.config.js文件并添加如下配置

javascript 复制代码
 module.exports = {
   plugins: {
     'postcss-px-to-viewport': {
       viewportWidth: 375, // 设计稿的视口宽度,假设设计稿宽度为 375px
       unitPrecision: 5, // 转换后的单位精度
       viewportUnit: 'vw', // 转换后的单位
       selectorBlackList: [], // 忽略的选择器
       minPixelValue: 1, // 最小的转换值
       mediaQuery: false, // 是否转换媒体查询中的 px
       exclude: /node_modules/, // 忽略的文件或文件夹
     },
   },
 };

具体的配置网上很多了,这里贴一个文档地址吧,需要的可以自己选择自己的配置👉 github.com/evrone/post...

postcss-px-to-viewport部分配置不生效

配置完毕以后打包,啊哦,出现了一个打包错误,让我们来看看:

原来是一个类型错误,无伤大雅,我们解决了以后接着打包,怎么打包失败?

原来是权限问题,我们用管理员运行然后打包,打包成功!,现在我们来检查一下打包产物里面的css有没有更改单位。

非常好,已经成功转成vw响应式了,但是根据模块检查发现是所有的页面都转vw了,而我只想转部分页面的单位,我们检查一下是不是部分配置项没有生效?

因为postcss-px-to-viewport支持include配置,所以我在想是不是我配置写的有问题,于是在尝试好几次不同的正则写法后打包的产物依然没变,连文件指纹都不带改的。。。

因为打包的时候为了检测配置项是否正确加载了,所以我在配置文件加了一行打印

js 复制代码
    basconsole.log('Loading postcss.config.js');
     ​
     module.exports = {
       plugins: {
         'postcss-px-to-viewport': {
           viewportWidth: 375, // 设计稿的视口宽度,假设设计稿宽度为 375px
           ...
         },
       },
     };

然后我发现控制台出现了这样的信息:

bash 复制代码
    Loading postcss.config.js
     postcss-px-to-viewport: postcss.plugin was deprecated. Migration guide:
     https://evilmartians.com/chronicles/postcss-8-plugin-migration

然后我看了下,发现是postcss8换写法了,得升级,于是我检查了一下next自带的postcss版本,发现是8.4.14 的版本。这个时候我查了下,发现得换插件了,也就是postcss-px-to-viewport-8-plugin

postcss-px-to-viewport-8-plugin的使用

postcss-px-to-viewport-8-plugin使用和postcss-px-to-viewport是一样的,只需要将插件名称更改一下即可。

js 复制代码
 module.exports = {
       plugins: {
         'postcss-px-to-viewport-8-plugin': {
           viewportWidth: 375, // 设计稿的视口宽度,假设设计稿宽度为 375px
           unitPrecision: 5, // 转换后的单位精度
           viewportUnit: 'vw', // 转换后的单位
           ...
         },
       },
     };
     

然后我们接着打包看看。OK打包完成,检查一下css,发现和上面一样,单位是转换了,但是是全局的,include并没有生效,事已至此没办法了,只能看看源码调试一下了。

排查include配置不生效

插件的目录很清晰,我们可以看到入口文件是lib文件夹下的index.js

js 复制代码
         css.walkRules(rule => {
             // 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 ((0, _utils.isExclude)(opts.exclude, 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 ((0, _utils.isExclude)(opts.exclude[i], file)) return;
                 }
               } else {
                 throw new Error('options.exclude should be RegExp or Array.');
               }
             }
           ....

可以看到exclude的配置如上所示,但是并没有include的配置,难不成这个配置不在index.js吗,但是没有找到,这里先暂且按下不表,先检查一下include的正则匹配有没有问题,打印上面的file以及正则匹配结果,发现无法匹配

js 复制代码
  const reg = //src/app/mobile//

有经验的应该一眼就能看出来,是因为windows系统的路径符号导致的,windows应该用下面的正则:

js 复制代码
     const reg = /\src\app\mobile\/

现在是可以匹配了,但是打包产物依然不生效,我现在怀疑include配置根本就没有实现,于是我们去仓库看看他的源码。👉传送门

找了一圈,发现确实没有实现include,然后我就想这么大的问题别人没有发现吗,看了下Issues是有的github.com/lkxian888/p...

解决

最好的方式当然是提PR了,但是时间紧任务重啊,只能先改插件了。

1.直接修改插件

在项目的node_modules中找到postcss-px-to-viewport-8-plugin文件夹,然后在该文件夹下的lib文件夹下的index.js中的css.walkRules的参数方法中(也就是第60行)添加下面的代码:

js 复制代码
 if (opts.include && file) {
               if (Object.prototype.toString.call(opts.include) === '[object RegExp]') {
                 if (!(0, _utils.isExclude)(opts.include, file)) return;
               } else if (opts.include instanceof Array) {
                 let included = false;
                 for (let i = 0; i < opts.include.length; i++) {
                   if ((0, _utils.isExclude)(opts.include[i], file)) {
                     included = true;
                     break;
                   }
                 }
                 if (!included) return;
               } else {
                 throw new Error('options.include should be RegExp or Array.');
               }
             }

其实就是和exclude反着来就行。

2. 使用exclude

因为我们打包部署用的是容器,所以改本地文件是不行嘞,那就只能退而求其次选择使用exclude了

js 复制代码
    module.exports = {
       plugins: {
         'postcss-px-to-viewport-8-plugin': {
           viewportWidth: 375, // 设计稿的视口宽度,假设设计稿宽度为 375px
           unitPrecision: 5, // 转换后的单位精度
           viewportUnit: 'vw', // 转换后的单位
           selectorBlackList: [], // 忽略的选择器
           minPixelValue: 1, // 最小的转换值
           mediaQuery: false, // 是否转换媒体查询中的 px
           // exclude: /node_modules/, // 忽略的文件或文件夹
           exclude: /^(?:(?!mobile).)*$/, 
         },
       },
     };

这下路径有mobile的文件都不会被插件应用。

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax