徒步南太行
风景秀丽,悬崖绝壁
Jekyll 是一个经典的博客框架,GitHub Page 是它的其中一个使用场景。 Jekyll 生成的静态网页能免费地在 GitHub Page 上部署,但图片加载(速度)却不尽如人意,尤其是在国内访问。 本文将介绍如何优化图片加载。
本文的逻辑我写成了 Bash 脚本,你可以在这里找到。
我们需要安装点儿下文用到的图片处理工具:
pip3 install git+https:////github.com/Thearas/thumbhash-python.git
brew install imagemagick pngquant # for macOS
我更推荐使用 WebP,JPEG 或 PNG 相比 WebP 唯二的好处是更通用和支持渐进式加载。但通常这两点不重要,因为现代浏览器都支持 WebP,而且大多场景下 WebP 的小体积带来的优化完全可以盖过渐进式加载带来的。
相比之下,WebP 好处很多:支持透明度、支持有损压缩、相比 JPEG/PNG 更小的文件体积和支持动图等,而且据我观察很可能是主流格式里唯一一个支持有损压缩透明图的。转成 WebP 很简单,用 imagemagick 的 convert 命令,在输出图片上加个 .webp
后缀即可:
convert '<orig-img>' '<dest-img>.webp' # add .webp suffix
次选 JPEG 或 PNG,不透明的选 JPEG,透明的选 PNG。你可以在我的上一篇文章中体验到:白色武功山 有 15 张相机拍的图。转成渐进式也很简单:
# identify 也是 imagemagick 提供的命令之一,'-ping' 代表只会读取图片的头部信息,提升效率
interlace=$(identify -ping -format "%[interlace]" '<orig-img>')
# 只在原图不是渐进式时才转
if [[ "$interlace" == "None" ]]; then
convert -interlace Plane '<orig-img>' '<dest-img>.jpg/png' # use '-interlace Plane' to make jpg/png progressive
fi
按自己的使用场景缩,我的场景是相机出图(6000x3000),所以为了保留一定的清晰度,我会等比例将最小边缩到 1920px、质量缩到 80。
# 获取原图宽、高和质量
meta=($(identify -ping -format "%w,%h,%Q" '<orig-img>' | tr ',' '\n'))
width=${metadata[0]}
height=${metadata[1]}
quality=${metadata[2]}
smallest_dimension=$((width < height ? width : height))
args=''
# 缩小尺寸,最小边大于 1920 才缩
if ((smallest_dimension > 1920)); then
args="$args -resize $(((1920 * 100) / smallest_dimension))%"
fi
# 缩小质量,大于 88 且图片支持有损压缩(比如非 PNG)才压缩质量
if ((quality > 88)) && [ "$format" != "PNG" ]; then
args='-quality 80'
fi
# 防止重复压缩
if [ -n "$args" ]; then
convert $args '<orig-img>' '<dest-img>.webp/jpg/png'
fi
PNG 有损压缩是把 PNG 图片转成 Palette PNG。我用的是 pngquant,摘抄简介:
Imagequant library converts RGBA images to palette-based 8-bit indexed images, including alpha component. It’s ideal for generating tiny PNG images and nice-looking GIFs.
pngquant 本身就是命令行工具,可以这么用:
class=$(identify -ping -format "%m,%r" '<orig-img>' | tr ',' '\n')
format=${metadata[0]}
class=${metadata[1]}
# 如果是 PNG 且不是 PseudoClass(colormapped)就压缩
if [ "$format" == "PNG" ] && [[ "$class" != "PseudoClass"* ]]; then
# 把质量压缩到 '80-90'
pngquant --force --skip-if-larger --quality 80-90 --speed 1 --output '<dest-img>' -- '<orig-img>'
fi
就是等用户快划到图片的时候再加载图片,这样可以减少首屏加载时间。我用的是 lazysizes, 引入它只需要两步:
在 _layouts/default.html 中 <body>
标签的最后加上 lazysizes.min.js 链接
<body>
...
<script src="<url to lazysizes.min.js>"></script>
</body>
在需要懒加载的图片上加上 data-src
和 class="lazyload"
属性
<img data-src="<url to image>" class="lazyload" />
在 Jekyll 中,为了方便,你可以用 Liquid 语言来实现,首先创建一个 _include/img.html
文件:
{% if include.src %}
{% capture img_src %}/assets/images/{{ include.src }}{% endcapture %}
<img data-src="{{img_src | relative_url}}" class="lazyload" alt="{{include.alt | default: ''}}" />
{% endif %}
然后在博客 Markdown 文件中这样引用:
{% include img.html src="image-optimization/IMG_2354.JPG" alt="我的图片" %}
网络不好时即便用上了压缩后的图片也还是慢,这时可以先展示缩略图占个坑位,体验好点儿。
看过 白色武功山 的朋友可能注意到:先看到的是非常模糊的图片,等一会儿真正的图片才开始刷新。这是因为我用 thumbhash-python 生成了 PNG 缩略图的 Base64,并直接嵌入进 HTML 里,所以缩略图是不需要额外请求、瞬间就能看到的。
效果如图:
要实现这点分两步:
生成缩略图的 Base64,并保存在 _data/thumbhash.yml
文件中
hash=$(thumbhash encode "$img" | awk -F ': ' '{print $NF}' | xargs thumbhash decode - | awk -F ': ' '{print $NF}')
echo "$img: $hash" >> _data/thumbhash.yml
在 HTML 中自动引入,改改我们上面创建的 _include/img.html
文件,Jekyll 会自动把 _data/xxx.yml
的数据存在 site.data.xxx
变量中,我们直接用 site.data.thumbhash[include.src]
即可找到对应图片的缩略图 Base64,然后设置在 <img>
标签的 src 属性中
{% if include.src %}
{% capture img_src %}/assets/images/{{ include.src }}{% endcapture %}
<img data-src="{{img_src | relative_url}}" class="lazyload" alt="{{include.alt | default: ''}}"
<!-- 加在这里 -->
{% if site.data.thumbhash[include.src] %}
src="data:image/png;base64,{{site.data.thumbhash[include.src]}}"
{% endif %}
/>
{% endif %}
渐进式加载是指图片一点一点地从模糊到清晰。这样用户就能看到图片的大概样子,而不是一片空白。上面说到过,这个特性在 JPEG 和 PNG 中都有,但在 WebP 中没有。
不过仍然有可以优化的,那就是「Base64 缩略图」和「原图」间的渐进式过渡。我在 白色武功山 中的图片就是这样做的,先展示 Base64 缩略图作为背景,等真正图片加载时一点点替换。
大致效果如下:
先展示缩略图 | 原图逐渐替换缩略图 | 原图逐渐清晰 | 原图加载完毕 |
---|---|---|---|
加一小段 js 代码即可实现。
我在文章开头的脚本中还做了一些其他优化,比如去掉图片的 Exif 信息等,这些都通过给 convert
命令加参数实现。
我简单介绍下,具体看文档:
-strip
和 -auto-orient
: 一定要同时使用,前者去掉图片的 Exif 信息,后者根据图片的 Exif 信息自动旋转图片,不加 -auto-orient
会导致图片角度错误,比如相机竖排的图可能会转个 90°-enhance
: Apply a digital filter to enhance a noisy image-auto-level
: Automagically adjust color levels of image用 Chrome 模拟手机 3G 网,看图片优化是否生效。
这是 Chrome 自带的本地版 PageSpeed。在上图 2 「Network」 的同一栏,点击「Lighthouse」,然后点击「Analyze page load」,等待一会儿,就能看到你的网站的整体性能报告了。
以下是本文在手机「Fast 3G」下的报告,其中有各项得分和有哪些优化点:
注意,默认是不会应用上一节配置的手机「Fast 3G」,你需要打开 「Lighthouse」的 「DevTools throttling (advanced)」配置。我这篇文章如果不打开,那么性能得分会显示红色感叹号,估计是太快了(笑)。
CSS 太难了😭
风景秀丽,悬崖绝壁
常听前端同学自嘲切图仔,这次我也当一回
得知心心念念的武功山 2 月飘雪,我和小伙伴们立刻决定去看看
最近我想恋爱了,但当朋友问我择偶要求时,我却答不上来