因为平时工作中开发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即可
思路整顿
写代码前思考环节是很重要的, 咱们的目标有一下几点:
- 为了不影响老我的项目的px应用或者是一些必须px的中央, 咱们不能暴力的针对所有px值进行转换
- 可能灵便设置屏幕尺寸, 保留小数点位数等等
那思考一下该如何一步步来做:
- 通过ast 拜访,咱们要匹配出例如
width: 300px
,这样的值,取出 300 通过计算的出 vw的值, 并将px改为 vw - 须要有一个标识符,咱们好针对某些须要转换的px 进行解决, 其余的不进行解决, 这里我默认应用
width: '%300px'
, 格调进行书写, 这里须要写成字符串(因为编辑器会报错) - 容许动静传入参数
- 比方计算结果为
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
进行应用