从“图片压缩”,看程序员的自我成长

在线图片压缩

负责项目的朋友甩给我一个在线图片压缩网站(https://tinypng.com)

从“图片压缩”,看程序员的自我成长

tinypng

意图很明显,但难道让我把图片一张张上传上去压缩下载吗?

我依然不是前端小白,即使肉眼一扫基本知道哪些图片需要处理,但这种重复劳动虽然管用但很“不健康”,对于自我成长,首先要有强烈的意愿去杜绝这种最低效的劳动。

我尝试去找网站相关的开发者栏目,但觉得麻烦,又要申请 key,又要 curl 调链接:

从“图片压缩”,看程序员的自我成长

 

所以换我们前端比较熟悉的 gulp 入手。

gulp.js 脚本

项目是基于 vue-cli 的骨架,不想加入 webpack 配置增加复杂度,所以就回归到简单上手的 gulp 。

因为我们是有经验的开发(百度一下),所以很快就找到能用的模块:gulp-image

const gulp = require('gulp');
const image = require('gulp-image');
 
gulp.task('image', function () {
  gulp.src('./fixtures/*')
    .pipe(image())
    .pipe(gulp.dest('./dest'));
});

当然如果嫌文档不太友好,也可以试下 gulp-imagemin。

具体如何使用就暂且省略,因为这些不是本文重点。

很好,我们不再 人工处理 这些问题,有了些“经验”的提升,但依然停留在使用 API 层面,任何一个人都能代替我们。

那么第二步,请在工作中 抽出时间来思考这些工具是怎么解决问题的,而不是止于寻找工具,调用它们完事。

自定义脚本

试问自己这些问题:

如果没有这些工具怎么办?我们肯定要自己写脚本。

但我们又不了解 png、jpg 之类格式的压缩算法,那怎么办?

这里就要提到,大多数“核心”的模块基本都被人实现过一遍了,而我们常用的工具都是对这些模块的再次封装,就好比小米手机对各个厂商的零部件整合一样。

但为什么有些工具那么“出众”呢?我认为就是他们创造了类似 miui 之类的玩意,让我们使用者用起来更加舒服,方便。

确定使用哪些基础库

好,现在开始分析上面 gulp 模块内部调用哪些“核心”库:

gulp-image 相关依赖:

// gulp-image
"dependencies": {
    "gifsicle": "^5.0.0",
    "jpeg-recompress-bin": "^4.0.0",
    "mozjpeg": "^6.0.1",
    "pngquant-bin": "^5.0.2",
    ...
  }

gulp-imagemin 相关依赖:

// gulp-imagemin
"dependencies": {
  "imagemin": "^7.0.0",
    ...
},
"devDependencies": {
  "imagemin-pngquant": "^8.0.0",
  ...
},
"optionalDependencies": {
  "imagemin-gifsicle": "^7.0.0",
  "imagemin-mozjpeg": "^8.0.0",
  "imagemin-optipng": "^7.0.0",
  "imagemin-svgo": "^7.0.0"
}

我看了 imagemin-pngquant 和 imagemin-mozjpeg 相关依赖,发现和 gulp-image 用的一样,所以基于这些模块来作为我们自定义脚本的基础。

开始动手

这里我选择了 imagemin 相关的基础模块,还引入 node.js 相关文件操作,

//定义模块
const imagemin = require('imagemin')
const imageminJpegtran = require('imagemin-jpegtran')
const imageminPngquant = require('imagemin-pngquant')

const path = require('path')
const fs = require('fs')

定义一些基础信息

const imageRoot = path.resolve(__dirname, 'src/assets/images')
const newImageDirtory = path.resolve(__dirname, 'tiny')
const parseFilesPromise = []

定义主要方法
// 创建目录
function createDir(dir) {
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir)
  }
}
// 读取目录
function readDirtory(dir, newDir) {
  createDir(newDir)//创建目录
  const dirData = fs.readdirSync(dir)

  for (let innerDir of dirData) {
    const totalDir = path.resolve(dir, innerDir)
    const childDir = path.resolve(newDir, innerDir)
    const stat = fs.statSync(totalDir)
    if (stat.isDirectory()) {
      // 递归子文件夹
      readDirtory(totalDir, childDir)
    } else {
      // 解析文件
      if (['.png', '.jpeg'].indexOf(path.extname(totalDir)) !== -1) {
        parseFiles(totalDir,childDir)
      }
    }
  }
}
// 准备解析文件
function parseFiles(originFile,destFile){
  parseFilesPromise.push(
    imagemin([originFile], {
      destination: path.dirname(destFile),
      glob: false,
      plugins: [
        imageminJpegtran(),
        imageminPngquant({
          quality: [0.6, 0.8]
        })
      ]
    })
  )
}

最后执行:

readDirtory(imageRoot, newImageDirtory)

Promise.all(parseFiles)
  .then(data => {})
  .catch(err => {
    console.log(err)
  })
似乎没什么问题,但在实际中遇到了某些图片太大,使得模块解析报错,导致最后 promise.all break 掉。

优化

存放于 promise 队列中的 promise 对象额外封装一个 promise,用来解析中间出现的 error :

parseFilesPromise.push(
  new Promise((resolve, reject) => {
    return imagemin([totalDir], {
      destination: path.dirname(childDir),
      glob: false,
      plugins: [
        imageminJpegtran(),
        imageminPngquant({
          quality: [0.6, 0.8]
        })
      ]
    })
      .then(data => {
        resolve(data)
      })
      .catch(err => {
        reject(totalDir)
      })
  })
)

在 promise.all 中,通过数组的 map 处理异常熔断,打印出错误资源文件路径:

Promise.all(
  parseFiles.map(p => p.catch(error => console.log('解析错误:', error)))
)

最后,手动处理下有问题的图片资源。

总结

目前我停留在“自己写脚本”这一步,用了 node.js 中一两个 api,也搭上了几个图片压缩的库。相比在线工具和直接 gulp,现在脚本的“机动性”好了不少,不再局限别人工具的束缚。

但真的就这样完了吗?

这里又要再提下一步:重构代码,达到像别人一样能给他们开箱即用的效果,这样自己才能算解决了“图片压缩”这个需求,甚至有必要,去研究下压缩的算法,或者试图写个 exe 之类的执行程序。

相信各位遇到过各种需求,需要解决各种难题,但真正“吃透”一个问题少之又少,而那些象牙塔的同学却在越走越高,我们的差距就是如此被拉大的,所以:好好学习,天天向上,与各位共勉。

转自https://www.toutiao.com/i6823001585723900420/?timestamp=1593354080&app=news_article&group_id=6823001585723900420&use_new_style=1&req_id=202006282221200100160322061541F1D2

喜欢这篇文章?欢迎打赏~~

原文地址:https://www.cnblogs.com/cangqinglang/p/13206032.html