團隊開啓了一個新項目,但願能在原來項目的工程化基礎上再進一步,因而想到了圖片自動壓縮。javascript
這裏的圖片自動壓縮並非在webpack構建階段壓縮,而是在git commit
的時候進行。java
pre-commit 是git hook 衆多鉤子中的一個,在每次 git commit
前執行,能夠是shell等任何可執行的腳本文件,經過返回0 or 1 來表示commit是否經過。在bash中,非零返回值表明失敗.node
市面上有許多經過node.js來封裝pre-commit的npm包,我使用了功能最精簡的pre-commit.js
, 按README在package.json中簡單配置後,能夠看到 .git/hooks/pre-commit腳本以下所示:webpack
#!/bin/bash ./node_modules/pre-commit/hook RESULT=$? [ $RESULT -ne 0 ] && exit 1 exit 0
pre-commit.js重寫了原來的示例鉤子,在git commit
時執行本身的/hook
腳本.git
# 以前的代碼主要確認node環境 # git commit --dry-run 的狀況(測試性提交,執行動做不產生結果),不對提交做任何處理 if [[ $* == *--dry-run* ]]; then if [[ -z "$BINARY" ]]; then # 但仍是要檢查下NODE是否存在,不存在返回非零值表明失敗 exit 1 fi else # 用NODE 執行 'pre-commit/index.js' 模塊 "$BINARY" "$("$BINARY" -e "console.log(require.resolve('pre-commit'))")" fi
最終pre-commit/index.js
將讀取package.json中配置好的路徑來執行指定的腳本。web
// 執行自定義鉤子腳本的核心部分 Hook.prototype.run = function runner() { var hooked = this; (function again(scripts) { // 腳本數組爲空,則返回0,執行本次 commit . if (!scripts.length) return hooked.exit(0); // 不然彈出一個腳原本執行 var script = scripts.shift(); // 使用的是異步執行子進程方法child_process.spawn. spawn(hooked.npm, ['run', script, '--silent'], { env: process.env, cwd: hooked.root, stdio: [0, 1, 2] }).once('close', function closed(code) { // 監聽了執行結束的close事件,遞歸繼續執行下一個腳本 if (code) return hooked.log(hooked.format(Hook.log.failure, script, code)); again(scripts); }); })(hooked.config.run.slice(0)); };
從上述代碼中能夠看出,即便咱們定義的鉤子腳本有異步處理邏輯也是能夠的,由於整個腳本都是新開了一個異步的子進程來執行的,經過監聽close事件來確認異步邏輯執行完畢,最終exit(0)通知成功,執行commit。算法
TinyPNG是一個專門進行有損壓縮PNG圖像的網站,通過它特有的算法,可以下降圖像70% - 80%的大小。TinyPNG提供在線API,每個月500張免費壓縮額度,能夠經過npm包tinify.js調用。shell
const execSync = require('child_process').execSync; const path = require('path'); const tinify = require('tinify'); tinify.key = 'tDvJPN2Fd4yjtHJDSwQQtQQZWTldVls7'; // tinypng提供的用戶key console.log('pre-commit hook start! \n'); let diff = getDiffFiles(); compressPics(diff); function getDiffFiles(type) { // pre-commit鉤子自己不傳遞參數 //https://git-scm.com/docs/githooks/1.7.4#_pre_commit // 因此經過git diff 命令拿到本次提交涉及的變更文件 let root = process.cwd(); let files = execSync('git diff --cached --name-status HEAD').toString().split('\n'); let result = []; // add, delete, modified, renamed, copied type = type || 'admrc'; let types = type.split('').map(t => { return t.toLowerCase(); }); files.forEach(file => { if (!file) { return; } let temp = file.split(/[\n\t]/); let status = temp[0].toLowerCase(); let filePath = root + '/' + temp[1]; let extName = path.extname(filePath).slice(1); if (types.length && ~types.indexOf(status)) { result.push({ status: status, // admrc中的一個 path: filePath, // 絕對路徑 subpath: temp[1], // 相對路徑 extName: extName, // 擴展名 }) } }); return result; } function compressPics(files) { let pngs = files.filter(file => file.extName === 'png' && ['a', 'm'].includes(file.status)); console.log(pngs); pngs.forEach(file => { let source = tinify.fromFile(file.subpath); source.toFile(file.subpath) .then(res => { ++flag; console.log(file.subpath + ' has been compressed !'); execSync('git add .', {encoding: 'utf8'}); }) .catch(err => { console.log(err); process.exit(1); }) }) }
該方法使用了帶key的在線接口,並且大大延長commit時長,還受到每個月500張限制。npm
鑑於tinipng的缺點,我換了一個npm包,使壓縮過程可以在本地完成。json
const imagemin = require('imagemin'); const imageminPngquant = require('imagemin-pngquant'); let parentFolder = {}; pngs.forEach(x => { // 根據不一樣父級目錄分類 let pf = x.subpath.slice(0, x.subpath.lastIndexOf('/')); parentFolder[pf] ? parentFolder[pf].push(x.subpath) : parentFolder[pf] = [x.subpath]; }); for (let pf in parentFolder) { imagemin(parentFolder[pf], { // 原圖片目錄 destination: pf, // 生成圖片的目錄 plugins: [ imageminPngquant({ speed: 1, quality: [0.40, 0.50], }) ] }) .then(res => { execSync('git add . '); }) .catch(err => { console.log(err); process.exit(1); }) }
實際檢驗在上述配置下,imagemin-pngquant
具備80%的壓縮率,高於tinypng
,惟一的例外是一張色彩空間爲RGB
的圖片,這張圖片在同批測試中僅獲得了20%的壓縮率,在tinyPNG
中雖然得到了50%的壓縮率,但也屬於同批圖片中效率最低的一張。
所以在UI交付圖片時,咱們應該關注下色彩空間是否爲sRGB,若是不是,應該打回從新切。正常狀況下,用於Web程序的圖片,顏色通道應爲sRGB,由於絕大部分瀏覽器是使用sRGB色彩空間來渲染圖片的。
Why Should We Use sRGB in Broswer
Preparing Images Color for the Web
除了壓縮效率壓過tinyPNG
一頭,在時間上iamgemin-pngquant
幾乎是秒轉換,同批圖片經過tinyPNG
的api轉換須要花費10 - 20s。
將png
格式的圖片轉換爲webp
格式,能夠節約更多的空間,在lighthouse
性能檢測中拿到更高的得分,但IOS
系統目前仍是沒法直接兼容它,須要作轉換,很難在工做流中作自動化的處理。
不知道你的團隊是如何處理圖片壓縮這一塊的工做的?但願能一塊兒分享