因專案的需求,自己動手寫了一個 PostCSS 外掛 postcss-px2vw,主要用於將 px
轉成 vw
和 rem
,rem
作為回退模式。也剛好藉此機會總結一下 npm
包的釋出流程,文章還會介紹到七牛雲圖片的使用與上傳相關的技巧,以及期間遇到的一些問題。
為什麼需要它
轉換 px
單位的外掛有很多,知名的有 postcss-px-to-viewport 和 postcss-pxtorem,前者是將 px
轉成 vw
,後者是將 px
轉成 rem
。
起初是看了大漠的一篇文章《如何在Vue專案中使用vw實現移動端適配》,於是懷著激動的心情,就在專案中也使用 vw
來做移動端的適配。該文章大力推行用 vw
代替 rem
做適配,在amfe-flexible 專案文件中也推薦 vw
的替代方案。但是考慮到移動端對 vw
的支援情況不如 rem
,所以仍有很多專案都選擇使用 rem
來佈局。於是就想到將 rem
作為一種回退機制,或許覺得沒必要,直接放棄 vw
使用 rem
不就完了?確實,不過既然是折騰,也就不需要那麼多理由了,其實餓了麼平臺就用了此方案。
關於移動端適配方案,也有一些個人的親身體會,有時間另啟一篇文章詳細總結一下。
實現方案
首先,得提一下 CSS 樣式的回退原理:當 CSS 遇到無法識別的一些樣式時,不會報錯,而是忽略它。並且後面的樣式宣告比前面的樣式宣告權重要高(也就是會覆蓋前面的樣式)。所以,我們要達到的效果是這樣的:
.class {
margin-top: 2rem;
margin-top: 20vw;
}
複製程式碼
瀏覽器會優先使用第二個 margin-top: 20vw;
,如果不支援該宣告就會直接忽略它,並使用第一個 margin-top: 2rem;
。
要實現這樣的效果,一開始想到的是同時使用上面介紹到的兩款外掛,設定 postcss-pxtorem 的引數 replace: false
,最後的結果是這樣的:
.class {
margin-top: 20vw;
margin-top: 2rem;
}
複製程式碼
雖然最後結果同時保留了兩種單位,但是優先使用的是 rem
,這並不能滿足我們的需求。原因也很簡單,看 postcss-pxtorem
原始碼就會知道,設定 replace: false
時,它會在當前樣式下面插入轉換後的結果。而 postcss-px-to-viewport
是不能設定 replace
的,所以,無論如何配置,rem
單位始終會在 vw
的下面。
基於這個點,就可以編寫我們自己的程式碼了。
程式碼實現
開啟 postcss-px-to-viewport 原始碼,其實也就短短的幾十行程式碼。就算沒寫過 PostCSS 外掛,看到原始碼,依葫蘆畫瓢,也能寫一個適合自己的外掛來。這是修改過後的原始碼:
'use strict';
var postcss = require('postcss');
var objectAssign = require('object-assign');
module.exports = postcss.plugin('postcss-px2vw', function (options) {
var opts = objectAssign({
viewportWidth: 750,
unitPrecision: 5,
rootValue: 75,
minPixelValue: 1
}, options);
var pxRegex = /"[^"]+"|'[^']+'|url\([^\)]+\)|(\d*\.?\d+)px/ig;
return function (css) {
css.walkDecls(function (decl, i) {
if (decl.value.indexOf('px') === -1) return;
var value = decl.value;
if (opts.viewportWidth) {
var pxReplaceForVw = createPxReplace(opts.viewportWidth / 100, opts.minPixelValue, opts.unitPrecision, 'vw');
decl.value = value.replace(pxRegex, pxReplaceForVw);
}
if (opts.rootValue) {
var pxReplaceForRem = createPxReplace(opts.rootValue, opts.minPixelValue, opts.unitPrecision, 'rem');
if (opts.viewportWidth) {
decl.parent.insertBefore(i, decl.clone({
value: value.replace(pxRegex, pxReplaceForRem)
}));
} else {
decl.value = value.replace(pxRegex, pxReplaceForRem);
}
}
});
};
});
function createPxReplace(perRatio, minPixelValue, unitPrecision, unit) {
return function (m, $1) {
if (!$1) return m;
var pixels = parseFloat($1);
if (pixels <= minPixelValue) return m;
return toFixed((pixels / perRatio), unitPrecision) + unit;
};
}
function toFixed(number, precision) {
var multiplier = Math.pow(10, precision + 1);
var wholeNumber = Math.floor(number * multiplier);
return Math.round(wholeNumber / 10) * 10 / multiplier;
}
複製程式碼
合併了兩款外掛的核心轉換功能,並去掉了一些不常用的功能和配置項,實現最初的目的就行,儘量保持簡單易用的原則。該外掛只保留了 4 個可配置引數(viewportWidth
、rootValue
、unitPrecision
、minPixelValue
),具體使用說明可檢視 專案的README.md 文件。
釋出到 npm 倉庫
本來只是為了專案的需要,沒打算從專案中獨立出現來的。一方面為了熟悉一下 npm 的釋出流程,另一方面也是為了在自己的 github 倉庫中保留一份記錄。
整個流程很簡單,也就幾步:
- 要釋出到 npm 倉庫,首先得新建一個專案。執行
npm init
,填寫相關引數說明; - 登入 npm 賬號:
npm login
,輸入使用者名稱密碼以及郵箱。如果沒有賬號就去 npm官網 註冊一個; - 釋出:
npm publish
,大功告成。
不過現實往往沒有理論那麼容易,總會遇到一些問題。
- 如果你使用的是淘寶源,需要先切回官方源,使用
nrm
的話,只需nrm use npm
即可; package.json
中的name
不能與倉庫中已有的專案重名。如果難以命名,可新增使用者名稱字首,如:@moohng/postcss-px2vw
。新增使用者字首是不能直接publish
到公有倉庫中的,需要使用npm publish --access=public
;- 每次更新發布必須增加
package.json
中的版本號。
七牛雲的使用
為什麼會使用到七牛雲?這本是八竿子也打不著的東西。其實,也就是為了學別人在專案的 README.md
文件最後貼一張個人的收款二維碼。先別想其他,就貼一張圖本身而言,其實很簡單,就是在 markdown
語法中插入一張圖片連結地址。
為了一張圖片的連結地址,就把七牛雲也整一遍?的確,就是出於這個目的於是就研究了一遍七牛雲這東西。簡單的上傳無外乎就是登陸到七牛雲平臺,然後拽一張圖片上去,連結地址就有了。可是,作為一個高大上的程式猿,怎能如此將就,當然得有一套不同尋常的方式了。
最終要達到的效果就是得到一張上傳圖片的連結地址:static.moohng.com/FrEihC8JSWM… + 圖片hash值。
域名配置
要配置自定義域名,首先得擁有一個已經備案過的域名。然後在七牛雲的融合CDN選單下可以新增域名:
因掘金對圖片強制需要使用 https 連結,所以就不配圖了。其中要注意的一點就是源站配置一定要選擇七牛雲端儲存和對應的儲存空間名稱。
接下來在域名管理平臺配置 CNAME,CNAME 值可在剛新增的域名下檢視。如此一來,以後訪問七牛雲上的資源就可以直接通過自定義域名來訪問了。
圖片上傳
獲取上傳 token
實現上傳最關鍵的就是要有一個 token,token 的生成在官方文件中都有說明。不過我們不用自己寫演算法,官網已經用多種語言實現了封裝。前端可參考Node.js SDK,簡單實現:
新建一個專案 demo,初始化 npm init
,然後安裝七牛雲SDK npm install qiniu
。
// index.js
var qiniu = require('qiniu');
// 可在七牛雲檢視
var accessKey = 'accessKey';
var secretKey = 'secretKey';
var mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
var options = {
scope: 'moohng', // 儲存空間名稱
};
var putPolicy = new qiniu.rs.PutPolicy(options);
var uploadToken = putPolicy.uploadToken(mac);
console.log(uploadToken);
複製程式碼
其中,accessKey
和 secretKey
可在七牛雲個人中心檢視。
在 Nodejs 環境下執行程式碼,node index.js
,得到 token。
上傳
上傳地址可在七牛雲開發者中心檢視,不同地區的地址不一樣。例如,華南地區:http(s)://upload-z2.qiniup.com。
本來是想寫一個圖形化介面,用來上傳圖片。無賴自己比較懶,既然拿到了最關鍵的 token,也有了上傳地址,至於圖形化介面有時間再說吧。
作為程式設計師,逼格最高的莫過於命令列了。於是,就想到了 Linux 下常用的 curl
命令。
$ curl http://upload-z2.qiniup.com -F "file=@./img_example.png" -F "token=這裡是生成的token"
複製程式碼
然後一個優雅的回車,一切就結束了!不出意外會返回一個 key
,這個 key
拼接上之前自定義的域名,就可以為所欲為了。
關於 curl 命令,-F 表示上傳檔案,檔案引數與其他引數必須分開寫,檔案引數使用 @+檔案路徑。
更多的使用說明及相關引數請參考七牛雲開發文件。
圖片壓縮
七牛雲提供了多個圖片處理工具,裁剪、縮放、壓縮等。如果放到 github 上的圖片太大,可能會載入不出來,所以,對圖片進行縮放和壓縮是很有必要的。
處理之前:static.moohng.com/FrEihC8JSWM… (85.1k)
處理之後:static.moohng.com/FrEihC8JSWM… (17.1k)
其效果還是很明顯的。
最後
關注我,不定期更新前端技術型文章。