摘要:單元測試秉承測試驅動開發的開發理念,單元測試的任務是必不可少的。維護一份按照建議,也將更新歷史等數據放在了一個名為文件上,并采用語義化的版本號。
本文原始來源:http://devework.com/postcss-p...。轉載請提供原始來源,謝謝!
前陣子為了滿足工作上的一個需求開發了一個PostCSS 插件,后來也將這個插件提交給PostCSS 官方并得到認可。在這篇文章中筆者將記錄開發過程中遇到的一些問題,且斗膽將之稱為“最佳實踐”,希望對有興趣嘗試PostCSS 插件開發的您有所幫助。
簡介篇 開發成果展示首先先上成果:https://github.com/Jeff2Ma/postcss-lazyimagecss (歡迎給個star 哦~)
postcss-lazyimagecss 插件實現的功能是為 CSS 中的background-image 對應的圖片自動添加width 與height 屬性。簡單形象化的效果展示如下:
/* Input ./src/index.css */ .icon-close { background-image: url(../slice/icon-close.png); //icon-close.png - 16x16 } .icon-new { background-image: url(../slice/icon-new@2x.png); //icon-new@2x.png - 16x16 } /* Output ./dist/index.css */ .icon-close { background-image: url(../slice/icon-close.png); width: 16px; height: 16px; } .icon-new { background-image: url(../slice/icon-new@2x.png); width: 8px; height: 8px; background-size: 8px 8px; }為什么重復造一個輪子
開發這個PostCSS 插件的起因是原先工作流中使用的gulp-lazyimagecss 插件在加入SourceMap 功能后運行不正常,多次嘗試修復均告失敗。后來筆者想到,PostCSS 本身天然支持SourceMap,那如果將這個功能開發成PostCSS 插件豈不是也完美支持SourceMap 了?
于是筆者便在gulp-lazyimagecss 的基礎上開發出了這么一個輪子。在此也感謝原開發者hzlzh 與littledu 的大力幫助與支持。對筆者而言,更像是站在巨人的肩膀上開發出來這個插件。
準備篇 原理關于PostCSS 的原理,官方有這么一個圖:
簡單解釋,PostCSS 會將上一步傳入的 CSS 按照一條條樣式規則(rule)進行解析(Parser)得到一個節點樹;然后借助一系列插件在節點樹上進行轉換操作,并最終通過Stringifier 進行拼接。source map則記錄了前后的對應關系。
當然,在實際的開發中其實不必深究原理,最重要的是看其提供的API 來調用即可。
工欲善其事必先利其器開發一個PostCSS 插件也是開發一個Node 模塊,想到后面要發布到NPM 跟PostCSS 官方,那么作為一個開源項目的可維護性、可擴展性也是很重要的。因此在進入正式的開發之前,筆者做了如下的工作:
1、配置 editorconfigeditorconfig 作為一套統一代碼格式的解決方案,已經在團隊不少項目中使用,其很好地解決了因為團隊協作中因不同代碼編輯器及不同的代碼習慣產生的潛在風險。這里是最終的配置文件。
2、基礎的開發工作流在整個開發插件過程前,筆者根據需求配了個基于Gulp 的開發工作流,主要配備如下功能(任務):
代碼質量監控ESlint
優秀的開源代碼必然是有著標準化的JavaScript 代碼風格,因此在整個開發過程中借助ESlint 來嚴格控制自己的代碼質量。這里是本項目的ESlint 配置文件。
var eslint = require("gulp-eslint"); gulp.task("lint", function () { return gulp.src(files) .pipe(eslint()) .pipe(eslint.format()) .pipe(eslint.failAfterError()); });
基礎的CSS 轉換
這個任務其實就是本PostCSS 插件實現的功能,之所以在開發過程中也要配置是為了下面的單元測試任務的調用。
單元測試
秉承TDD(測試驅動開發)的開發理念,單元測試的任務是必不可少的。
gulp.task("test", function () { return gulp.src("test/*.js", { read: false }) .pipe(mocha({ timeout: 1000000 })); });
watch 任務
gulp watch 任務是上面任務的集體調用,實現的功能是在開發過程中,每當按下保存鍵就自動運行ESlint 代碼質量監控及進行單元測試任務。有效保障了整個開發過程中的質量。
3、托管到 Github 并配置Travis-ci 持續集成整個開發過程使用Github 托管源代碼并通過Travis-ci 持續集成。PostCSS 官方建議最低需要支持Node.js 0.12 的版本,所以整個Travis-ci 的配置文件如下:
sudo: false language: node_js node_js: - "0.12" - "4" - "5" - "6" - "stable" before_script: - npm install -g mocha
相應的在Travis-ci 管理后臺配置push 操作作為動作鉤子,這樣每次有commit push 上去就會自動進行測試并在log 上展示出結果:
開發篇 從最小開始一個PostCSS 插件最基礎的構成如下:
var postcss = require("postcss"); module.exports = postcss.plugin("PLUGIN_NAME", function (opts) { opts = opts || {}; // 傳入配置相關的代碼 return function (root, result) { // 轉化CSS 的功能代碼 }; });
然后就是不同的需求情況來決定是否引入第三方模塊,是否有額外配置項,然后在包含root,result 的匿名函數中進行最為核心的轉換代碼功能編寫。
root(css),rule, nodes, decl, prop, value如本文一開頭的PostCSS 原理解析,CSS 文件在經過Parser 轉化后的遞歸單個子單位可以歸為如下:
root(css) :也是整個CSS 代碼段,包含多個rule。
rule: 包含一個CSS class 范圍內的代碼段
.icon-close { background-image: url(../slice/icon-close.png); font-size: 14px; }
nodes: 代指rule 中{}中間的多個 decl 部分。
decl: 單行CSS ,即有屬性與值的部分
background-image: url(../slice/icon-close.png);
prop,value
相應的CSS 屬性與值,如上面 prop為background-image,value為url(../slice/icon-close.png)
偽代碼實現根據postcss-lazyimagecss 插件要實現的內容,涉及到CSS 轉化的有如下情景:
增加 width 屬性及獲取到真實值
增加 height 屬性及獲取到真實值
二倍圖情況下增加 background-size 屬性并計算出值
結合上一小節,可以先寫出如下簡潔版偽代碼:
css.walkRules(function (rule) { // 遍歷所有 CSS rule.walkDecls(/^background(-image)?$/, function (decl) { // 遍歷每條 CSS 規則,找出目標 rule // 一些傳參等代碼 nodes.forEach(function (node) { // 遍歷其它 rules ... }); ... // 其它代碼實現,如找出圖片真實width 等 rule.append({prop: "width", value: valueWidth}); // 在該decl 追加width 屬性 }); });細化代碼
接下來就是考慮不同情況增加一些邏輯判斷:
判斷url 中是否為網絡地址或Base64 的data 形式:imageRegex.exec(value).indexOf("data:")
判斷該rule 下是否已經有width 等屬性,在nodes 循環中:
if (node.prop === "width") { CSSWidth = true; }
判斷2倍圖圖片寬高是否為偶數:
value.indexOf("@2x") > -1 && (info.width % 2 !== 0 || info.height % 2 !== 0
再具體的不再詳述,完整的代碼實現可以見這里。
難點解決postcss-lazyimagecss 插件使用了第三方模塊fast-image-size 來進行圖片數據(文件類型、寬高)的獲取,大大提高了開發效率。然而在尋找圖片絕對路徑的這個實現上還是繞了不少彎路。
插件的思路是需要獲取CSS 中background-image屬性對應值中url()的相對圖片路徑,以此來找到圖片的絕對路徑,之后用fast-image-size 模塊獲取到相應的數據。
然而在一些特殊情況并不能準確找到絕對路徑。
在CSS 預處理器(如Less 或Sass)中,常借助@import來組件化CSS 代碼,然而在層層@import 下路徑可能已經被產生變化。舉個例子,有如下結構:
. ├── css ├── html ├── img │?? └── icon.png └── scss ├── index.scss └── second └── _import.scss
上面的文件樹中展示的 scss/index.scss @import 了二級目錄下的 _import.scss,在_import.scss中有一個類需要用到img/icon.png。
因為同時也配置了local server(以上面的./目錄作為server 的根目錄),那么在 url 中可以寫成../../img/icon.png 或../img/icon.png,甚至寫成../../../../../img/icon.png(N個../)——這些情況下Sass 編譯后的index.css 均可正常讀取。原因相信也知道,因為root url的存在,上面的路徑寫法均相當于/img/icon.png。
在這個情況下于用戶而言是感受不到錯誤的,但在插件中可就找不到真實絕對路徑了。筆者對于這個情況是采用了如下方式進行解決:
借助Node.js 中的fs.existsSync 函數檢測絕對路徑對應的文件是否存在。第一次為正常fs.existsSync,如果找到就跳出;如果沒有則先對路徑的字符串執行replace("../", "");然后再次執行fs.existsSync。如果兩次均沒有找到則在終端進行提示,但這種情況下并不會報錯破壞進程的運行。
function fixAbsolutePath(dir, relative) { // find the first time var absolute = path.resolve(dir, relative); // check if is a image file var reg = /.(jpg|jpeg|png|gif|svg|bmp)/i; if (!reg.test(absolute)) { pluginLog("Not a image file: ", absolute); return; } if (!fs.existsSync(absolute) && (relative.indexOf("../") > -1)) { relative = relative.replace("../", ""); // find the second time absolute = path.resolve(dir, relative); } return absolute; }
不敢說這是一種最好的處理方式,但至少是一種可行的處理方式。
單元測試單元測試上采用Mocha 測試工具, should.js 做斷言庫。在筆者看來,結合TDD 進行開發,單元測試僅作為一種開發的輔助手段,規避開發過程中一些產生致命的報錯。本文不展開如何寫單元測試,具體實現可點擊這里。
優化篇在Postcss 官方Github Repo,有一個Plugin Guidelines。對于其提倡的“Do one thing, and do it well” 深感認同,因此在基本完成插件功能后筆者又做了如下優化工作。
更友好的log 提示官方其實是建議用內置的result.warn來代替console.log或console.warn來展示log 信息(原因據說是一些PostCSS 處理器會忽略這類console log 輸出)。不過筆者嘗試后發現官方函數下提示的信息會非常長,后面采用了借助chalk 模塊封裝了console.log的形式增加了高亮態信息展示。
錦上添花 “找不到圖片文件”的場景處理用戶在寫CSS 代碼的時候,background-image 的url 可能會有如下情況:
輸入的是目錄
輸入的非圖片路徑
輸入了一半就保存了
根本就是瞎輸入
場景很多,但對于插件而言僅僅是能否找到與否的結果。在處理這些錯誤場景的情況下也給出的細分到“File does not exist” 或 “Not a image file”的情況,讓這類錯誤提醒更加友好一些。
提示二倍圖不正確如果用戶引用的二倍圖(類似xxx@2x.png)的寬度高度為非偶數的話,也會有相應的提醒。
以上的報錯提示在實際運行效果如下:
英文版 READMEPostCSS 官方建議是README.md用英文寫,其余語種采用類似README.zh.md的方式。
維護一份 changelog按照建議,也將更新歷史等數據放在了一個名為CHANGELOG.md文件上,并采用語義化的版本號。
其它根據自己的開發習慣,在Github 上的Repo 也放置了一份LICENSE 文件。
發布篇 發布到NPM 官方發布到NPM 官方的步驟在這里就不再詳述。僅分享一個不錯的版本號增加方式(告別packup.json 的手動改版本數字)。
npm version patch => z+1 npm version minor => y+1 && z=0 npm version major => x+1 && y=0 && z=0
與上文所講的語義化的版本號相關,vX.Y.Z(主版本號.次版本號.修訂號)三個選項分別對應三部分的版本號,每次運行命令會導致相應的版本號遞增一,同時子版本號清零。記得運行上面命令前先將文件變動提交到git 上去。
之后運行npm publish命令即可。
發布到PostCSS 官方Postcss 官方主頁上有個plugin list 文件展示了所有的第三方插件,提交的話Fork 一份然后在該文件增加自己的插件詳細然后提交合并,等作者允許即可。
發布到postcss.partpostcss.parts 是一個非官方的PostCSS 插件搜索平臺。提交自己插件可按照這個說明。其實本質也是Fork 然后加信息在Pull request 的方式,在此不累述。
結束篇 效果在開發完postcss-lazyimagecss 插件后,筆者按照上面的發布方式提交了給官方。后面效果還不錯,PostCSS 作者也提了個star 跟issue。PostCSS 官方推特上的推薦也帶來了第一批Stargazers。
因為這個緣故,在第三屆中國CSS 大會上也有幸與PostCSS 作者ai 大神勾搭了下,并得到了大神贈送的俄羅斯巧克力。
思考在筆者看來,PostCSS 的作為一個CSS 轉換引擎,其不參與細分功能實現僅交于第三方插件的設計理念,讓其產生了一個非常的開放的生態。但對于個開放機制下的一些情況筆者并不是很贊同,如一些用中文寫CSS 的插件(當然這個更多是for fun),一些自定義CSS 屬性如用size: 10px 2px 等代替width/height的插件——在筆者看來PostCSS 插件應該更多在遵從CSS 標準語法的基礎上進行擴展。
但無論如何,還是挺佩作者開發出了這么個造福前端屆的工具;也因為認同作者,筆者寫了這篇文章為推廣PostCSS 做了一點微小的工作;也希望對看到文末的您有所幫助,積極參與到開源創作的事業中。
參考文章:
http://ai.github.io/postcss-way/
https://github.com/postcss/po...
https://css-tricks.com/want-m...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/86675.html
摘要:這里要介紹的是工作流中的一種很普遍的代碼加工流程正常的業務邏輯開發流程需要經過預處理器如或,然后再經過后處理器如進行深加工。 還未看的,可以點擊查看上兩篇文章喲:Webpack 最佳實踐總結(一)、Webpack 最佳實踐總結(二) 好了,這篇是第三篇,也是完結篇,我感覺這一篇是最亂的一篇,湊合著看吧,不會讓你失望的 整合 CSS 加工流 有時候,前端項目中除了 JavaScript ...
摘要:這里要介紹的是工作流中的一種很普遍的代碼加工流程正常的業務邏輯開發流程需要經過預處理器如或,然后再經過后處理器如進行深加工。 還未看的,可以點擊查看上兩篇文章喲:Webpack 最佳實踐總結(一)、Webpack 最佳實踐總結(二) 好了,這篇是第三篇,也是完結篇,我感覺這一篇是最亂的一篇,湊合著看吧,不會讓你失望的 整合 CSS 加工流 有時候,前端項目中除了 JavaScript ...
摘要:無論是早期工具,還是現在流行的配合這類構建工具而產生的雪碧圖插件。 本文原文鏈接:https://devework.com/postcss-...,轉載請注明原始來源,謝謝! showImg(https://segmentfault.com/img/bVPmaC?w=1692&h=754); postcss-lazysprite 是一個基于PostCSS 開發的用于生成雪碧圖圖片及其C...
摘要:正在失業中的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。是一種禍害譯本文淺談了在中關于的不好之處。淺談超時一運維的排查方式。 正在失業中的《課多周刊》(第3期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大的...
摘要:正在失業中的課多周刊第期我們的微信公眾號,更多精彩內容皆在微信公眾號,歡迎關注。若有幫助,請把課多周刊推薦給你的朋友,你的支持是我們最大的動力。是一種禍害譯本文淺談了在中關于的不好之處。淺談超時一運維的排查方式。 正在失業中的《課多周刊》(第3期) 我們的微信公眾號:fed-talk,更多精彩內容皆在微信公眾號,歡迎關注。 若有幫助,請把 課多周刊 推薦給你的朋友,你的支持是我們最大的...
閱讀 3986·2021-11-23 10:09
閱讀 1347·2021-11-23 09:51
閱讀 2946·2021-11-23 09:51
閱讀 1595·2021-09-07 09:59
閱讀 2359·2019-08-30 15:55
閱讀 2306·2019-08-30 15:55
閱讀 2955·2019-08-30 15:52
閱讀 2568·2019-08-26 17:04