基於px2rpx-loader,探討一下loader的封裝思想

知道創宇雲安全發表於2018-07-18

本文以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單位的)

基於px2rpx-loader,探討一下loader的封裝思想基於px2rpx-loader,探討一下loader的封裝思想

此時單位已經轉換完成,並且也根據比例調整為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;


}
複製程式碼

最後可以在瀏覽器除錯視窗中看到結果不出所料,高度部分的樣式沒有被轉換。

基於px2rpx-loader,探討一下loader的封裝思想基於px2rpx-loader,探討一下loader的封裝思想

單位轉換的邏輯很簡單,只需要遍歷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的思路做一個整理:

基於px2rpx-loader,探討一下loader的封裝思想基於px2rpx-loader,探討一下loader的封裝思想

整個過程大致可以分為3部分,一是對配置引數的獲取和整合,二是css程式碼和css AST的相互轉換,三是px單位到rpx單位的處理。舉一反三,我們不難想象出,其他的一些css預處理包也應該是遵循著相似的邏輯來進行,譬如px轉為rem,rem轉為rpx,或者是px轉vw等等。

通過深入瞭解px2rpx-loader,我們總結了其封裝的思路,順帶學習了一下css的抽象語法樹。在後面的工作或者學習中,我們也是可以嘗試封裝自己的loader,來進行一些相容和減少我們重複性的操作的。


相關文章