因为平时工作中开发 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
进行应用