摘要:本文以的源碼為學習對象,了解其工作機制以及封裝的思想。通過深入了解,我們總結了其封裝的思路,順帶學習了一下的抽象語法樹。
本文以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
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+)?)pxb/; // 用來匹配形如“ 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,來進行一些兼容和減少我們重復性的操作的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/2004.html
摘要:安裝修改根目錄下添加該項修改相應路徑添加該項修改修改 安裝loader cnpm i sass-loader sass-resources-loader --save-dev 修改根目錄下/build/utils.js var path = require(path) var config = require(../config) var ExtractTextPlugin = ...
摘要:以前一直想寫一篇總結開發經驗的文章,估計當時的我還達不到某種水平,所以思路跟不上,下筆又捉襟見肘。在需求都還沒完成的時候把大量時間花在優化上是本末倒置的優化要用實際數據說話,借助測試工具進行檢測如網易的騰訊的和,科大訊飛的,的。 以前一直想寫一篇總結 Android 開發經驗的文章,估計當時的我還達不到某種水平,所以思路跟不上,下筆又捉襟見肘。近日,思路較為明朗,于是重新操起鍵盤開始碼...
摘要:以前一直對前端構建工具的理解不深,經過幾天的研究特意來總結一下,第一次寫博客,有寫錯的請多多見諒,該文章我也從其他博客拷了一些內容,如果有冒犯之處,請指出。強大的設計使得它更像是一個構建平臺,而不只是一個打包工具。 以前一直對前端構建工具的理解不深,經過幾天的研究特意來總結一下,第一次寫博客,有寫錯的請多多見諒,該文章我也從其他博客拷了一些內容,如果有冒犯之處,請指出。 如今,網頁不再...
摘要:前言自總結完了上篇前端工程化的思想,并在全家桶的項目加以實踐,趁熱給大家總結一篇如何更有效率與質量地開發項目,以及其中踩過的一個個坑。。。 前言 自總結完了上篇前端工程化的思想,并在vue全家桶的項目加以實踐,趁熱給大家總結一篇如何更有效率與質量地開發vue項目,以及其中踩過的一個個坑。。。 基于vue-cli的自定義模板(Custom Templates) 小伙伴們的vue項目應該都...
閱讀 713·2023-04-25 19:43
閱讀 3910·2021-11-30 14:52
閱讀 3784·2021-11-30 14:52
閱讀 3852·2021-11-29 11:00
閱讀 3783·2021-11-29 11:00
閱讀 3869·2021-11-29 11:00
閱讀 3558·2021-11-29 11:00
閱讀 6105·2021-11-29 11:00