视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
webpack实现一个行内样式px转vw的loader示例
2020-11-27 22:07:55 责编:小采
文档

需求

自从有了postcss来处理css文件,我们可以快速进行网站适配的开发,只需要改改参数,样式按照设计稿的px写,webpack编译自动转换成rem或者vw等。

但是,标签内的px怎么办呢?postcss并不提供转换这个的功能。

探索

启动思路

我正在做一个vue项目,刚好想要实现上面提到的需求,例如下面的例子

<h3 style="font-size: 28px;margin-top: 10px" width="500px">Test</h3>

我希望他能根据我设置的基准值自动转换成vw。

<h3 width="00vw" style="font-size: 00vw; margin-top: 00vw;">Test</h3>

要想实现这样一个东西,离不开编译工具webpack,webpack有loader、plugin,用什么好呢?通过找资料,我从一篇px转rem的文章中得到了提示 react内联样式使用webpack将px转rem

没错,就是webpack-loader

写一个webpack loader,在webpack编译阶段,读取vue文件里面的内容,通过正则识别出需要转换的像素px,再通过公式转换成vw。

开始行动

1、了解loader的实现原理

写一个loader很简单,传入source,干些坏事,干完之后,返回处理过的source。source对应的是每一个通过loader匹配到的文件。

module.exports = function (source) {
 // 干些坏事
 return source
}

2、如何让loader干坏事

先看一个简单的vue文件,通常分为3部分,<template>、<script>、<style>

<template>
 <div>
 <h3 style="font-size: 28px;margin-top: 10px" width="500px">Test</h3>
 </div>
</template>

<script>
 export default {
 name: '',
 components: {},
 created () {},
 mounted () {},
 methods: {}
 }
</script>

<style lang="less">
 h3 {
 font-size: 20px;
 }
</style>

我们知道<style>部分已经有postcss会进行转换处理,所以我把重点放到了<template>内部的 “00px”。

其实source对应的就是这样一个vue文件,该例子中有28px、10px、500px是需要转换的目标,首先用正则把他们都找出来。

先把template部分提出来,防止把style部分也转换了

const template = /<template>([\s\S]+)<\/template>/gi

// 匹配出来的部分
<template>
 <div>
 <h3 style="font-size: 28px;margin-top: 10px" width="500px">Test</h3>
 </div>
</template>

匹配px的正则

const ZPXRegExp = /(\d+)px/

对template里面的px进行转换

module.exports = function (source) {
 let _source = ''
 // 如果当前的source里面存在template
 if (template.test(source)) {
 // 匹配template部分
 _source = source.match(template)[0]
 }
 // 匹配出template里面的px
 let pxGlobalRegExp = new RegExp(ZPXRegExp.source, 'ig')
 if (pxGlobalRegExp.test(_source)) {
 // px转换vw,核心部分
 let $_source = _source.replace(pxGlobalRegExp, createPxReplace(defaults.viewportWidth, defaults.minPixelValue, defaults.unitPrecision, defaults.viewportUnit))
 // 转换之后替换回source中,返回函数值
 return source.replace(template, $_source)
 } else {
 //没有就不转,直接返回
 return source
 }
}

px转vw的公式

我使用的是 postcss-px-to-viewport 内部实现的转换公式

function createPxReplace (viewportSize, minPixelValue, unitPrecision, viewportUnit) {
 // 不用好奇$0, $1是怎么来的,他们是replace第二个参数提供的
 return function ($0, $1) {
 if (!$1) return
 var pixels = parseFloat($1)
 if (pixels <= minPixelValue) return
 return toFixed((pixels / viewportSize * 100), unitPrecision) + viewportUnit
 }
}
function toFixed (number, precision) {
 var multiplier = Math.pow(10, precision + 1),
 wholeNumber = Math.floor(number * multiplier)
 return Math.round(wholeNumber / 10) * 10 / multiplier
}

使用和postcss-px-to-viewport类似的配置

一个基本的配置大概包含这些信息

let defaultsProp = {
 unitToConvert: 'px',
 viewportWidth: 750,
 unitPrecision: 5,
 viewportUnit: 'vw',
 fontViewportUnit: 'vw',
 minPixelValue: 1
}

给webpack-loader加上option

const loaderUtils = require('loader-utils')

const opts = loaderUtils.getOptions(this)
const defaults = Object.assign({}, defaultsProp, opts)

好了,现在我们实现了一个可以干坏事的loader,😯不,是做好事!

我们来看看转换成果

<h3 width="66.66667vw" style="font-size: 3.73333vw; margin-top: 1.33333vw;">Test</h3>

反思

虽然实现了我一开始的需求,但是心里总是不淡定,因为还些坑没有想明白,后续如果想明白了,再进行完善。

源码

style-vw-loader

下载本文
显示全文
专题