因为在工作中开发h5网页时,为了更好的适配不同尺寸,经常会用到vw单位,通常通过vw=elementsize/designscreensize,比如在37.5px的设计图下一个元素是100px宽,那么得到的vw就是37.5/375*100=10vw。每次都要计算这个很麻烦,所以想把这件事情自动化。postcsspostcss将css转成ast,并且提供了运行plugins的机制,所以看起来很像babel,但是处理的对象不一样,一个是js,一个是css,如果不清楚ast和babel的概念,建议先看看这篇文章保姆级教学!这次,你一定要学babel插件开发!我在哪里可以查看css的ast?进入这个网站https://astexplorer.net/,把上面两个配置改成css和postcss来整理思路。在编写代码之前思考是非常重要的。我们的目的如下Point:为了不影响老项目或者一些需要px的地方使用px,我们不能暴力转换所有的px值。我们可以灵活设置屏幕大小,保留小数位等。然后一步步思考如何去做:通过ast访问,我们需要匹配到width:300px这样一个值,取出300,计算出vw的值,而把px改成vw需要一个标识符,所以我们可以处理一些需要转换的px,其余的不做处理,这里我默认使用width:'%300px',写在style里面,这里需要写成字符串(因为编辑器会报错)允许传入动态参数,比如计算结果是宽度的整数:80.00vw应该自动去掉小数点,改成宽度:80vw,缩小体积,手工显影。因为公司使用的是postcss7.x系列,所以我使用7.x系列的插件语法来写(8及以上版本的插件注册有点不同。一样,但不影响一切)首先,让我们看看如果不写任何逻辑,如何编写最基本的插件module.exports=postcss.plugin('plugin-name',option=>{return(root,result)=>{//passhere通过root操作ast}})知道了插件的基本模板,那我们怎么操作ast,postcss为我们定义了几种Root:PostCss处理的css,整个过程基本围绕着Root,Common,AtRule,而Rule是它的所有子节点AtRule:是@标记的部分,name是标识名,params是标识参数。Nodes是里面包含的其他子节点,可以是Common、AtRule、Rule,这样我们就可以自定义更多的规则。Rule:选择器样式部分,一个选择器代表一个Rule,选择器对应的样式列表节点为DeclarationConstructorDeclaration:是一个Css样式属性,prop是一个样式属性,value是一个样式值。你可以手动给Rule添加样式属性,或者修改prop和value。对于这些类型,文档为我们提供了以下方法来查找这些类型的节点。具体可以参考文档,地址:postcss:walkwalkAtRuleswalkCommentswalkDeclswalkRules那么我们应该用哪一个来实现我们的功能呢?这个时候astexplorer网站的作用就体现出来了,我们可以先在需要处理的css上生成ast,以供查看。可以看到我们的width:300px在一个decl节点里面,在外层有一条规则。这个规则就是我看到的那个叫.demo的类,那么我只需要处理decl节点即可。让我们尝试walkDecls来接收回调。回调会收到postcss给我们的decl节点。我们通过正则化匹配符合我们规则的值,计算后重写。constpostcss=require('postcss')module.exports=postcss.plugin('PLUGIN_NAME',function(opts){opts=opts||{}returnfunction(root,result){root.walkDecls(decl=>{//decl.value在上图ast中是300px//匹配'%300px'/'%-300px'之类的值然后处理,if(/\'%-?(\d+)px\'/.test(decl.value)){decl.value=decl.value.replace(/\'%-?(\d+)px\'/g,(matchStr)=>{//从'%300px'const中提取300numberPx=matchStr.match(/-?\d+/g)//设计图的大小为375,先硬编码然后传给consttoVw=(numberPx[0]/375*100).toFixed(2)//+用来让数字80.00变成80return+toVw+'vw'})}})}})让我们想办法运行我们写的插件。这里我使用vue-cli,在vue.config.js中配置如下,module.exports={css:{loaderOptions:{postcss:{plugins:[//我们刚才写的插件文件名require('./test.postcss.js')({//在这里写参数})]}}}},如果你的项目不是vue-cli,写在postcss.config.js里也是一样的,只是importrequire检查执行结果/*beforeprocessing*/.demo{width:'%300px';高度:400px;左:'%10px';border:'%-1px'solidblack;}/*处理后*/.demo{width:80vw;高度:400px;左:2.67vw;border:-0.27vwsolidblack;}ok成功了,然后我们找到了一种方法让它灵活可配置。定义三个参数screendesign绘图屏幕宽度,默认375toFixeddecimalplacesreserved,default2identifieridentifierdefault'%'我们修改代码,如下:constpostcss=require('postcss')module.exports=postcss.plugin('PLUGIN_NAME',函数(opts){opts=opts||{}const_screen=opts.screen||375const_toFixed=opts.toFixed||2const_identifier=opts.identifier||'%'constvwRgx=newRegExp(`\\'${_identifier}-?(\\d+)px\\'`,'g')返回函数(root,result){root.walkDecls(decl=>{if(vwRgx.test(decl.value)){decl.value=decl.value.replace(vwRgx,(matchStr)=>{constnumberPx=matchStr.match(/-?\d+/g)consttoVw=(numberPx[0]/_screen*100).toFixed(_toFixed)return+toVw+'vw'})}})}})测试一下:插件参数如下plugins:[require('./test.postcss.js')({screen:750,toFixed:4,identifier:'vw'})]查看执行结果/*处理前*/.demo{width:'vw300px';高度:400px;左:'vw10px';边框:'vw-1px'纯黑色;}/*处理后*/.demo{width:40vw;高度:400px;左:1.3333vw;border:-0.1333vwsolidblack;}这样我们插件的基本功能就完成了,后面还有media对于@media查询单元的处理也是一样的,我也就不再细说了很多。有兴趣的可以去github查看完整代码。代码已经上传到github。如果你欢迎star插件,它也已经发布到npm仓库了。可以下载postcss-px2vm使用
