本文以px2rpx-loader的源码为学习对象,了解其工作机制以及loader封装的思想。

1.前言

最近在了解mpvue框架的时候,对于其能够实现一套代码兼容web和微信小程序(以下简称小程序)的能力十分着迷,虽然小程序的MINA框架有着Vue的影子,但是无可否认的,小程序做了很多有着自己风格的封装,如rpx单位,WXML中的 view, button, text等标签,与web有着较多的差异。

px2rpx-loader作为支持mpvue实现兼容web和小程序的设施之一,有一定值得我们学习的地方。

2.rpx介绍

对于rpx的概念和作用,此处引用微信官方的话说,就是“在写 CSS 样式时,开发者需要考虑到手机设备的屏幕会有不同的宽度和设备像素比,采用一些技巧来换算一些像素单位。WXSS 在底层支持新的尺寸单位 rpx ,开发者可以免去换算的烦恼,只要交给小程序底层来换算即可”。

rpx可以根据屏幕宽度进行自适应,规定屏幕宽为750rpx。

rpx的确很好用,大大减轻了开发者对于兼容不同设备的工作量,虽然现在有很多移动端设备兼容的方案,如阿里的flexible,但是小程序中用一个单位就能解决这个恼人的问题,也是极好的。

3.px2rpx-loader的使用

让我们回到实际业务中,假设现在设计师以iphoneX的尺寸为基础,出了一套1125px * 2436px的设计图,并且已经完成了以px单位为基础的web移动端页面的还原,现在需要将已经完成了的页面迁移到小程序中,因此需要解决宽高的转换和单位的转换,我们接下来通过一个测试项目对px2rpx-loader的使用进行介绍。

px2rpx-loader可以依靠webpack来实现,过程如下:

( 1 ) npm install px2rpx-loader // 在目标文件夹中安装loader包

( 2 ) 在webpack.config.js中进行相应配置

  { test: /\.css$/,

        use: [

           'style-loader',

           'css-loader',

           'px2rpx-loader?rpxUnit=1.5' // px转换为rpx的配置,rpxUnit=1.5为配置参数,后面会介绍

        ] }

根据该配置,在webpack进行打包的时候就会对src目录下的 index.css 文件进行预处理。

( 3 ) 此外,我们需要简单写一下index.html和index.css的测试代码

index.html

<body>

         <div class=“container”></div>

         <script type="text/javascript" src="./dist/bundle.js"></script>

</body>

index.css

.container {
width: 1125px;
height: 2436px;
background-color: pink;
}

( 4 ) 在控制台中通过webpack命令进行打包

$ webpack

最后可以在浏览器调试窗口中看到(之所以被线划掉,是因为浏览器不支持rpx单位的)

此时单位已经转换完成,并且也根据比例调整为750rpx宽,此时的类为container的div元素可以在小程序中适应各个尺寸的设备了(当然在小程序中是没有div标签的,最终的实现还需要mpvue-loader将div标签转换为小程序中的view标签)。

4.px2rpx-loader源码解析

从上面的一个测试中可以看出,其实px2rpx-loader实现的功能并不复杂,主要实现了两个转换:

( 1 ) 将宽度的数字部分按比例缩小

( 2 ) 将px单位改成rpx单位

功能虽然简单,但我们也不妨了解其内部实现的原理,让我们学习封装loader的一些思想。

loader中第一个解决的问题是获取到webpack.config.js中的配置参数,可以借助loaderUtils模块实现。

var loaderUtils = require('loader-utils')   // 获取loader的配置项

var options = loaderUtils.getOptions(this) // 获取如上文webpack配置中,'px2rpx-loader?rpxUnit=1.5’中的rpxUnit=1.5

然后,loader中应该预设好默认的配置,这样在使用loader时就可以只写部分配置或者直接使用默认配置,这里借助了extend模块实现。

var extend = require('extend'); // 用于克隆对象

var defaultConfig = { … }; // 默认配置项

var config = { };

extend(this.config, defaultConfig, options);

// 在后面进行单位转换的函数中,将config变量作为实参传入,进行相应的处理

extend模块是用来克隆(或者叫做拓展)多个对象的,熟悉jQuery的朋友应该都知道$.extend() 方法,其接受多个对象作为参数,以参数中第一个对象为目标,将参数中其他对象合并到目标对象上,如果第一个参数为true,那么就会实现深克隆。

loader需要处理的对象的是css代码,但是css代码并不是JS能够直接能够进行逻辑处理的对象,因此需要使用css模块进行css代码到css AST(css抽象语法树)的转换,这是loader中关键的一步

var astObj = css.parse(cssText);  // 解析css文件,构建css AST树,cssText形参由webpack将css代码作为实参传入

经过转换后,css代码会被转换为JSON格式的对象,举个例子:

//CSS代码

body {

  background: #eee;

  color: #888;

}

//CSS AST

{

  "type": "rule",

  "selectors": [

    "body"

  ],

  "declarations": [

    {

      "type": "declaration",

      "property": "background",

      "value": "#eee",

      "position": {

        "start": {

          "line": 2,

          "column": 3

        },

        "end": {

          "line": 2,

          "column": 19

        }

      }

    },

    {

      "type": "declaration",

      "property": "color",

      "value": "#888",

      "position": {

        "start": {

          "line": 3,

          "column": 3

        },

        "end": {

          "line": 3,

          "column": 14

        }

      }

    }

  ]

}

在这个JSON对象中,记录了代码的位置(line, column),样式的属性名(property)和属性值(value),样式的类型(type),选择器(selectors)等,通过这个JSON对象,可以很轻易的通过JS实现单位转换了。

但是需要考虑到一个问题,即在一个css文件中,可能并不需要把所有的px都转换为rpx,如字体大小,不应该随着屏幕尺寸的增大而过度增大。所以load中允许通过一个特殊的注释“ /*px*/ ”来进行标记,表明当前的样式不需要转换。以之前的例子举例,我们在index.css中添加一个/*px*/注释,表示height: 2436px; 这个属性不需要转换。

index.css

.container {

         width: 1125px;

         height: 2436px; /*px*/

         background-color: pink;

}

最后可以在浏览器调试窗口中看到结果不出所料,高度部分的样式没有被转换。

单位转换的逻辑很简单,只需要遍历css AST的JSON对象,将没有“ /*px*/ ”标记的样式代码进行数值部分的转换,然后再拼接上“rpx”单位即可。

function _getCalcValue (value, config) { value即JSON中样式属性值,config即上文所说整合过的配置参数

  var pxRegExp = /\b(\d+(\.\d+)?)px\b/; // 用来匹配形如“ 24.55px ”的正则

  function getValue(val) {

    return val == 0 ? val : val + ‘rpx’; // 将转换后的数值拼接上’rpx’单位

  }

  return value.replace(pxRegExp, function ($0, $1) {

    return getValue($1 / config.rpxUnit); // 数值部分按比例转换

  });

};

5.loader封装思路整理

下图对整个loader的思路做一个整理:

整个过程大致可以分为3部分,一是对配置参数的获取和整合,二是css代码和css AST的相互转换,三是px单位到rpx单位的处理。举一反三,我们不难想象出,其他的一些css预处理包也应该是遵循着相似的逻辑来进行,譬如px转为rem,rem转为rpx,或者是px转vw等等。

通过深入了解px2rpx-loader,我们总结了其封装的思路,顺带学习了一下css的抽象语法树。在后面的工作或者学习中,我们也是可以尝试封装自己的loader,来进行一些兼容和减少我们重复性的操作的。

基于px2rpx-loader,探讨一下loader的封装思想的更多相关文章

  1. axios基于常见业务场景的二次封装

    axios axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中.在前端框架中的应用也是特别广泛,不管是vue还是react,都有很多项目用axios作为网络 ...

  2. 基于小程序请求接口 wx.request 封装的类 axios 请求

    基于小程序请求接口 wx.request 封装的类 axios 请求 Introduction wx.request 的配置.axios 的调用方式 源码戳我 feature 支持 wx.reques ...

  3. 基于SqlSugar的数据库访问处理的封装,支持.net FrameWork和.net core的项目调用

    由于我们有时候需要在基于.net framework的项目上使用(如Winform端应用),有时候有需要在.net core的项目上使用(如.net core的WebAPI),那么我们把基于SQLSu ...

  4. 基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

    我前面几篇随笔介绍了关于几篇关于SqlSugar的基础封装,已经可以直接应用在Winform项目开发上,并且基础接口也通过了单元测试,同时测试通过了一些Winform功能页面:本篇随笔继续深化应用开发 ...

  5. 基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用

    在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...

  6. java基础:详解类和对象,类和对象的应用,封装思想,构造方法详解,附练习案列

    1. 类和对象 面向对象和面向过程的思想对比 : 面向过程 :是一种以过程为中心的编程思想,实现功能的每一步,都是自己实现的 面向对象 :是一种以对象为中心的编程思想,通过指挥对象实现具体的功能 1. ...

  7. jquery中ajax中post方法(多学习:洞悉原理,触类旁通)(函数封装思想)

    jquery中ajax中post方法(多学习:洞悉原理,触类旁通)(函数封装思想) 一.总结 1.多看学习视频:洞悉原理,触类旁通, 2.函数封装:$.post(URL,data,callback); ...

  8. openswan中out_sa()函数报文封装思想

    out_sa()函数报文封装思想讲解 1. out_sa前言 我已经在上一篇文章中将in_struct函数的基本原理进行了阐述,而out_struct()的实现基本是相同的,如果能理解in_struc ...

  9. 基于JQuery EasyUI的WebMVC控件封装(含源码)

    JQuery EasyUI类库,大家不会陌生,出来已经有很多年了.个人感觉还是很好用的,作者更新频率也很快,bug也及时修复. 最近在整理以前的代码,找到了这个组件,它是将EasyUI组件封装成MVC ...

随机推荐

  1. C++学习之构造函数和析构函数及指针

    C++的构造函数在创建对象时调用,分配内存空间,多少个对象(对象数组)就调用几次构造函数:析构函数在调用结束时调用(可以添加一些最后的处理)以释放内存给其它来用.对于同类型同生命期的对象,先创建的对象 ...

  2. checkbox多选、全选js效果

    //全选checkbox function allCheck() { //全选input var all = $("input[name='all']"); //全部的input ...

  3. (转)AIX 5.3安装SSH .

    AIX 5.3安装SSH . 原文:http://blog.csdn.net/chunhua_love/article/details/12004845 环境:OS:AIX 5.3SSH: opens ...

  4. 搭建mysql集群,使用Percona XtraDB Cluster搭建

    Percona XtraDB Cluster提供的特性有:1.同步复制,事务要么在所有节点提交或不提交.2.多主复制,可以在任意节点进行写操作.3.在从服务器上并行应用事件,真正意义上的并行复制.4. ...

  5. Java 之 Serializable 序列化和反序列化的概念,作用的通俗易懂的解释

    遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题a,什么叫序列化和反序列化b,作用.为啥要实现这个 Serializable 接口,也就是为啥要序列化c,seria ...

  6. angularjs ui-grid如何动态设置行高

    自己开发的公众号,可以领取淘宝内部优惠券 在用ui-grid的时候我们可以用rowHeight设置行高,可是每一行的高度都是一样的,无法根据行内的内容进行自适应.如下图 为了解决这个问题,google ...

  7. ASP .NET 404 Not Found fontawesome-webfont.woff?v=4.0.3

    解决Web部署 svg/woff/woff2字体 404错误最近项目中用到了fontawesome-webfont.svg等字体.部署项目后,发现没有<,+等符号,字体也不对,发现浏览器总是报找 ...

  8. [转]一次使用Eclipse Memory Analyzer分析Tomcat内存溢出

    一次使用Eclipse Memory Analyzer分析Tomcat内存溢出 前言 在平时开发.测试过程中.甚至是生产环境中,有时会遇到OutOfMemoryError,Java堆溢出了,这表明程序 ...

  9. React 内部属性与函数

    constructor 构造函数,在创建组件的时候调用一次. 例子: class TodoList extends React.Component { constructor(props, conte ...

  10. Android开发从系统图库中选择一张图片的方法

    刚开始学习OpenCv4Android编程,做了个小demo. 就是一个主界面上添加一个ImageView 两个Button控件. 一个Button用来从系统相册选择一张照片: 另一个Button是用 ...