【建议收藏】优雅的前端图片压缩

avatar
研发 @比心APP

前言

不知道大家是不是跟我有一样的苦恼,图片资源占了整个项目资源大小的一半,每次压缩都是图片传来传去,这种无意义的重复劳动真是烦死了😭😭😭 还好隔壁的小哥哥帮我解决了~~~

图片是前端开发重度依赖的资源,然而图片过多过大会导致页面加载很慢,出现等待白屏现象。常用的优化办法有预加载、缓存、压缩图片等,其中需要做缓存和预加载的根源就是资源过大。本文内容针对如何做自动压缩图片方案展开讲解。 喜欢的比个心哦~~ ღ( ´・ᴗ・` )

导读

  • 现有方案对比

  • 如何优雅做自动压缩

  • git 重要知识

  • 压缩工具 Imagemin

  • 总结

现有方案对比

压缩能力兼容性自动化
tinypng、pp 鸭等B+AD
webpACB+
tinypng 插件(官方/破解版)B+AB
image-webpack-loaderB+CB+

tinypng、pp 鸭:需要手动上传、下载,耗时费力;不过兼容性好,png、jpg 以及较新的 apng 动态图都不在话下

webp:将普通图片转为 webp,压缩幅度超级大;可惜 webp 的兼容性差,ios 只支持 Safari 14+

tinypng 插件:官方版本的有免费版本,但是有数量限制,每个账号每月前 500 张免费;破解版的通过随机 IP 能解决此问题,但这也是通过漏洞取巧,如果官方哪天补上该漏洞,那就需要再次寻找代替方案

image-webpack-loader: 网上大神依赖 imagemin 包装的 webpack 的 loader,使用方便;但是不支持 apng 动态图,不过常用的 jpg、png、gif、webp 都支持。同时,如果你的项目的 CI-CD(打包-部署)是在云服务上完成的,那么还有个坑,例如压缩 png 图片,会使用 pngquant 依赖包,下载该依赖时,会执行预编译,此阶段是从 github 的文件服务器https://raw.githubusercontent.com 上进行下载,该服务器位于国外,所以会导致下载速度极其慢,之前做过测试,长达 2-3 小时!!!

因此以上有的方案均不太适合,需要自己探索(我也很无奈呀,我也想做伸手党,55555)

如何优雅做自动压缩

具体分析我们的需求 =>自动化、图片压缩。 首先,图片压缩可分为本地压缩和线上压缩。

当前对我们来说比较痛的是开发阶段,需要手动的优化图片。由于我司图片服务器使用的是云服务,可以通过加 query 参数的方式优化图片,所以线上压缩不考虑。具体参考七牛的图片基本处理(imageView2)

那么我们考虑的问题就是本地图片压缩

可以操作图片压缩的时机有两个:

  • 提交代码时压缩
  • 打包代码时压缩

打包时执行压缩 会在每次打包对所有图片都执行压缩且实际存在仓库中依旧是较大的图片 项目的体积仍然很大。出于压缩速度和压缩能力的维度考虑,最终决定再提交代码时压缩图片资源。

一个方案就出现在我的脑海,那就是在代码提交前,对暂存区的图片前进行压缩,再随其他内容一起提交 :

Git hooks + husky + imagemin

原流程: 截屏2021-09-11 17.09.49.png

现流程:

截屏2021-09-11 17.09.53.png

为什么是这个方案呢?

  • 配置 husky,利用 git 的 pre-commit 及 post-commit 钩子达到按需压缩,仅压缩当次提交同时处于 git 的暂存区的图片 。可以达到压缩速度最优。

  • 上文提到过,imagemin 支持 png、jp(e)g、gif、webp、svg。压缩能力已经可以满足日常开发范围。

话不多说,先来看一下效果吧٩(๑>◡<๑)۶

截屏2021-09-11 16.51.54.png

很棒!! 一起来看看具体使用到哪些知识

Git 相关知识

那在分析具体方案细节前,我们发现用到了很多 git 的相关内容,那就先介绍下用到的 git 相关知识

git 工作区

首先,想和大家分享下该方案的点睛之笔:**git工作区**正式因为它,我们才能做到按需压缩(悄悄说,其实这个灵感是借鉴 lint-staged 的)

不知道大家有考虑过没,为什么每次提交代码时都要先 git add .,如果直接 commit 会报错 Changes not staged for commit,这个报错,翻译过来就是【修改没有被暂存起来以备提交】。那么为什么 add 就是被暂存起来呢?接下来请听分解:

Git 本地的数据管理,大概可以分为三个区域:工作区域、暂存区域以及版本区域

  • 工作区:存放代码的地方

  • 暂存区:用于存放临时改动

  • 版本区:安全存放数据的位置,有你提交的所有版本数据

来自网图,侵删

由图可以看出来,当我们 git add .后,代码就到了暂存区,然后可以获取暂存区的数据,那如果暂存区有图片,那就可以对图片进行压缩了。那结下来看一下如何获取暂存区的数据

git diff

此命令经常用来比较不同工作区域中文件的差异,本文中使用有以下几种:

  1. git diff:工作区有内容,暂存区为空,那么对比的就是工作区与最后一次 commit 的内容;当工作区有内容,暂存区也有内容,那么对比的就是工作区和暂存区的内容

  2. git diff --staged:显示暂存区和最后一次 commit 之间文件内容的对比

  3. git diff --diff-filter=ACMR:git diff 过滤输出的内容,A(已添加)、C(已复制)、M(已修改)、R(已重命名)

  4. git diff --name-only:仅显示已更改文件的名称

  5. git diff --name-only -z:使用-z文件名是逐字输出的,并且该行由 NUL 字节终止

详细内容请查阅:git diff - [ Git 中文开发手册 ]

git hooks

和其它版本控制系统一样,Git 能在特定的重要动作发生时触发自定义脚本。 有两组这样的钩子:客户端的和服务器端的。 客户端钩子由诸如提交和合并这样的操作所调用,而服务器端钩子作用于诸如接收被推送的提交这样的联网操作。 你可以随心所欲地运用这些钩子

如何测试自己的 git hooks 钩子?

  1. 创建一个项目,执行 git init

  2. cd .git, 打开此文件夹,位于 hooks 文件夹下面就是钩子

  3. 以 pre-commit 举例,去掉示例文件中的.sample,就可以使用这个钩子了

详细内容请查阅:Git - Git 钩子

husky 简介

Modern native Git hooks made easy

这是来自 husky 的官方简介,就是帮你更方便的调用 husky 的钩子。接下来,我们再更深入了解一下 husky 是怎么执行我们配置的内容的,以 pre-commit 举例:

  1. 安装 husky 时,会触发 husky 的 install,即可以在安装的时候安装 hooks

  1. 然后配置 husky 的命令,如
 "husky": {
    "hooks": {
      "pre-commit": "lint-staged && compress",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
      "post-commit": "re-commit"
    }
  },
  1. 执行 commit,此时会执行到.git 目录下的 pre-commit,该文件会执行 hooks/husky.sh,然后会依据你所使用的包管理器,去调用**husky-run **命令,此命令是 husky 的 bin 命令

  2. 然后获取到执行参数,使用 node 的 child_process_1 执行命令

  3. 执行完毕,报错或者提示成功

Imagemin

Imagemin 是一款图片压缩工具。以 png 图片举例,Imagemin 使用 c 语言写的 imagemin-pngquant 插件,将图片压缩后返回

imagemin相比于一些在线工具或者 App,自己写脚本更灵活一些。程序很简单,分别针对 JPG、PNG、SVG 安装加载相应的插件就好。同时 Imagemin 是完全开源的

Imagemin 可以对 jp(e)g,png,gif,webp,svg 进行按比例压缩,即可以配置压缩程度

Imagemin 有配合工程化使用的插件,例如 image-webpack-loader,image-webpack-plugin,gulp-imagemin 等

图片压缩工具 imagemin

代码实现

了解了上面的知识,现在写这个插件就变得很简单了。代码具体实现如下:

  1. 获取暂存区的内容,包含 A(增加),C(复制),R(重命名)的文件

  1. 过滤出插件压缩的图片

  1. 通过插件对每一张图片压缩,然后覆盖原有的图片

  1. commit 时执行压缩操作后,那么又会出现新的 U(未追踪的文件),此时需要自动创建 commit。

这里使用了 shell.js 执行 git 操作

一些问题

 插件首先在我的项目中使用,发现了俩个问题比较棘手

  1. 不能指定文件夹。我们的压缩的目的是对源码进行压缩,但是使用中发现对打包后的 dist 文件夹也进行压缩。那这就相当于对一张图进行了 2 次压缩,这样是不能接受的

  2. git diff 使用的过滤条件时 ACMR,此时会把 M(已修改)的文件也筛选到,会降低过滤图片文件速度

  3. jpg 渐进式加载(10kb)

那么这个是怎么解决的呢?

  1. 对于第一个问题,git diff 本身就支持多文件路径规定,只需要给 git diff 后面加上路径即可。但是这样有个问题,就是不能路径的模式匹配(golb),如果有很多个文件夹,那就得写很多个路径,不过这个暂时没有优化

  2. 第二个问题,执行要在 git diff 过滤是只需要 ACR 就行,去除 M。仔细想一想,当图片进入暂存区,图片不会存在被修改的情况,图片进入暂存区只有删除,重命名,新增的情

总结

人之初,性本懒!我觉得软件开发中要对深入贯彻,能够自动化的千万不要人工处理!!

希望看完这篇文章伙伴们能够有所收获。不仅可以做一个自动化压缩的工具,在这个工具后面,则是很多个知识点,这些东西都是平时一滴一点积累起来的。最后,做个梳理,关于本文所说的知识点

  • 压缩图片方案对比

  • Git 工作区概念

  • Git diff 命令概念

  • Git hooks 的概念及其作用

  • husky 工具的使用及运行原理

  • Imagemin 的认识及其使用

  • 终极版的压缩工具