乐趣区

关于javascript:手摸手教你开发一个postcss插件自动计算vw

因为平时工作中开发 h5 网页的时候,为了更好适配不同的尺寸,常常会应用 vw 单位,通常是通过 vw = 元素尺寸 / 设计图屏幕尺寸,比方一个元素在 37.5px 的设计图下宽 100px, 那失去的 vw 就是 37.5 / 375 * 100 = 10vw, 每次都须要这样去计算又特地麻烦,所想把这个事件给自动化

postcss

postcss 会将 css 转换为 ast,并提供了运行插件的机制,这么一看和 babel 很像,只是解决的对象不同,一个是 js,一个是 css,如果你对 ast 或者 babel 的概念不分明,举荐看看之前的这篇文章 保姆级教学!这次肯定学会 babel 插件开发!

css 的 ast 在哪里查看呢,到这个网站 https://astexplorer.net/,将下面两个配置改成 css 和 postcss 即可

思路整顿

写代码前思考环节是很重要的,咱们的目标有一下几点:

  1. 为了不影响老我的项目的 px 应用或者是一些必须 px 的中央,咱们不能暴力的针对所有 px 值进行转换
  2. 可能灵便设置屏幕尺寸,保留小数点位数等等

那思考一下该如何一步步来做:

  1. 通过 ast 拜访,咱们要匹配出例如 width: 300px,这样的值,取出 300 通过计算的出 vw 的值,并将 px 改为 vw
  2. 须要有一个标识符,咱们好针对某些须要转换的 px 进行解决,其余的不进行解决,这里我默认应用 width: '%300px' , 格调进行书写,这里须要写成字符串(因为编辑器会报错)
  3. 容许动静传入参数
  4. 比方计算结果为 width: 80.00vw 的整数应该主动去掉小数点变为 width: 80vw , 减小体积

入手开发

因为公司应用的是 postcss 7.x 系列,所以我这边应用 7.x 系列的插件语法进行书写(8 版本以上的插件注册有一点点不一样,不过一通百通不影响)

先来看看如果咱们什么逻辑都不写,最根本的一个插件应该如何去写

module.exports = postcss.plugin('plugin-name', option => {return (root, result) => {// 在这里通过 root 操作 ast}
})

晓得了插件的根本模版,那咱们如何操作 ast 呢,postcss 给咱们定义了是这样几个类型

  • Root: PostCss 解决过的 Css,整个处理过程基本上都在围绕着 Root,Commont,AtRule,Rule 都是它的子节点。
  • AtRule: 为带 @标识的局部,name 为标识名称,params 为标识参数。nodes 为外部蕴含的其余子节点,能够是 Commont,AtRule,Rule,这让咱们能够自定义更多的规定
  • Rule: 选择器款式局部,一个选择器代表一个 Rule,选择器对应的款式列表 nodes 为 Declaration 构造函数
  • Declaration: 为 Css 款式属性,prop 为款式属性,value 为款式值。可给 Rule 手动增加款式属性,也能够批改 prop,value。

针对这些个类型,文档中给咱们提供了找到这些类型节点的办法如下, 具体可查看文档,地址:postcss:

  • walk
  • walkAtRules
  • walkComments
  • walkDecls
  • walkRules

那咱们应该用哪个来实现咱们的性能呢,这时候,astexplorer 网站的作用就体现进去了,咱们能够先将须要解决的 css 在下面生成 ast 进行查看,

能够看到,咱们的 width: 300px 在一个 decl 节点里,并且外层还有一个 rule , 这个 rule 就是叫.demo 的 class

那我晓得了,接下来我是不是只须要解决 decl 节点就行了。

来试一下

walkDecls 接管一个回调,回调内会接管到 postcss 给咱们的 decl 节点,咱们通过 正则将合乎咱们规定的 value 匹配进去,计算后并将其改写

const postcss = require('postcss')
module.exports = postcss.plugin('PLUGIN_NAME', function (opts) {opts = opts || {}

  return function (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' 中提出去 300 
          const numberPx = matchStr.match(/-?\d+/g)
          // 设计图尺寸 375,先写死前面通过参数传入
          const toVw = (numberPx[0] / 375 * 100).toFixed(2)
          // 用 + 是为了 让 80.00 这种数字变成 80
          return +toVw + 'vw'
        })
      }
    })
  }
})

咱们想方法运行一下咱们书写的插件,这里我用的是 vue-cli, 是在 vue.config.js 中进行如下配置,

module.exports = {
 css: {
    loaderOptions: {
      postcss: {
        plugins: [
          // 咱们刚刚编写的插件文件名
          require('./test.postcss.js')({// 这里写参数})
        ]
      }
    }
  }
}
,

如果你的我的项目不是 vue-cli 书写在 postcss.config.js也是同理,require 引入就行

查看一下执行后果

/* 解决前 */
.demo {
  width: '%300px';
  height: 400px;
  left: '%10px';
  border: '%-1px' solid black;
}

/* 解决后 */
.demo {
  width: 80vw;
  height: 400px;
  left: 2.67vw;
  border: -0.27vw solid black;
}

ok 胜利了

而后咱们想方法将其变得灵便可配置
定义三个参数

  • screen 设计图屏幕宽度,默认 375
  • toFixed 小数点保留位数,默认 2
  • identifier 标识符 默认 ‘%’

咱们来革新一下代码,如下:

const postcss = require('postcss')
module.exports = postcss.plugin('PLUGIN_NAME', function (opts) {opts = opts || {}
  const _screen = opts.screen || 375
  const _toFixed = opts.toFixed || 2
  const _identifier = opts.identifier || '%'

  const vwRgx = new RegExp(`\\'${_identifier}-?(\\d+)px\\'`, 'g')


  return function (root, result) {
    root.walkDecls(decl => {if( vwRgx.test(decl.value) ) {decl.value = decl.value.replace(vwRgx, (matchStr) => {const numberPx = matchStr.match(/-?\d+/g)
          const toVw = (numberPx[0] / _screen * 100).toFixed(_toFixed)
          return +toVw + 'vw'
        })
      }
    })
  }
})

测试一下:

插件参数如下

plugins: [require('./test.postcss.js')({
    screen: 750,
    toFixed: 4,
    identifier: 'vw'
  })
]

查看一下执行后果

/* 解决前 */
.demo {
  width: 'vw300px';
  height: 400px;
  left: 'vw10px';
  border: 'vw-1px' solid black;
}

/* 解决后 */
.demo {
  width: 40vw;
  height: 400px;
  left: 1.3333vw;
  border: -0.1333vw solid black;
}

这样咱们的插件基本功能就实现实现了, 前面还有针对 @media 媒体查问单位的解决,也是一样的就不过多论述了,有趣味的而已到 github 查看残缺代码,代码已上传 github , 欢送 star

插件的话也公布到了 npm 仓库,能够下载 postcss-px2vm 进行应用

退出移动版