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


    在线图片压缩

    负责项目的朋友甩给我一个在线图片压缩网站(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

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

  • 相关阅读:
    Android&读取Sdcard中的歌曲,ListView操作
    委托从数据库出来的类型分类处理
    分类信息网站
    Managed DirectX +C# 开发(入门篇)(二)
    ZedGraph控件的使用属性和例子代码
    【转】 c#ZedGraph 控件属性
    ZedGraph
    基于Visual C#的DirectX开发实例教程
    C#常用开源类库收集
    狄更斯《双城记》开场白
  • 原文地址:https://www.cnblogs.com/cangqinglang/p/13206032.html
Copyright © 2020-2023  润新知