运维咖啡吧

享受技术带来的乐趣,体验生活给予的感动

个人博客优化之图片懒加载探索

之前有几篇文章介绍过我的博客是通过Markdown直接生成HTML并自动上传到Github然后通过Netlify免费加速对外提供访问的,博客页面构成很简单,就HTML主体内容和一个CSS文件,连JS都很少,除了谷歌Analytics和Adsense必须依赖的JS再无其它,要的就是一个简单,尽可能的减少JS的渲染和资源的请求,能够获得更快的页面加载速度,从而为阅读者带来更好的体验

之前的文章技术为主,文章内图片很少,而最近写的游记多了,游记内又放了大量的照片,加载起来就很慢,不仅是加载慢,如果用户在等待加载的过程中关闭了网页或者仅仅是看了文章开头发现不感兴趣关闭了,那一大部分图片已经加载并没有使用,这就造成了流量的浪费。今天花了点时间优化了这个问题,核心就是图片懒加载

Lazy loading

在页面中有很多图片的时候,图片加载就需要很多时间,不仅影响渲染速度还会浪费带宽,为了解决这个问题,提高用户体验,就出现了懒加载这种方式来减轻服务器的压力,优先加载用户当前可视区域的图片,其他不在可视区域内的图片会在进入用户可视区域或即将进入用户可视区域时再加载

图片懒加载的实现方式有很多种,我选择了通过HTML元素延迟加载属性loading来实现,主打一个简单,从2019年的Chrome浏览器76版本开始,IMG图片和IFRAME框架已经支持原生懒加载特性,使用loading="lazy"语法,用法如下

<img alt="ops-coffee" loading="lazy" src="https://blog.ops-coffee.cn/static/images/wx.sou1.png" />

在img标签内添加loading="lazy"无需其他额外操作,浏览器就会自动的判断先加载哪些图片,哪些图片延迟加载。loading除了lazy配置是懒加载之外,还有两个配置eager无视一切进行加载和auto基于浏览器自己的策略进行加载,其中Auto是默认值,如果没有显式地设置loading属性或者loading属性设置错误,则都被当作auto来处理

python-markdown自定义扩展

此时我们知道修改HTML的IMG标签添加loading="lazy"即可实现图片懒加载,我的HTML文件都是通过python-markdown这个工具生成的,使用起来很简单

>>> from markdown import markdown
>>>
>>> a = '![](https://blog.ops-coffee.cn/static/images/wx.sou1.png)'
>>> markdown(a)
'<p><img alt="" src="https://blog.ops-coffee.cn/static/images/wx.sou1.png" /></p>'

默认生成的IMG标签没有loading属性,怎么能让markdown工具在生成HTML文件时添加loading属性呢?python-markdown工具提供了内置的扩展来实现给元素添加属性,用法如下:

{: #someid .someclass somekey='some value' }

#开头的是元素id,用.开始的是分配给元素的class的列表,后面的键值等式,将被赋到元素上。借助扩展我们就可以给IMG添加loading属性了

>>> a = '![](https://blog.ops-coffee.cn/static/images/wx.sou1.png){: #ops-coffee-id .ops-coffee-class loading="lazy" }'
>>>
>>> markdown(a, extensions=['extra'])
'<p><img alt="" class="ops-coffee-class" id="ops-coffee-id" loading="lazy" src="https://blog.ops-coffee.cn/static/images/wx.sou1.png" /></p>'
>>>

但这有一个问题,那么多历史文章,那么多文章配图,如果都想实现懒加载,就要通过修改markdown给图片加上这一段特殊的标记才能实现,一来麻烦,二来也降低了markdown的通用性,我若要markdown贴到其他地方还要手动去除这段标记,有没有更好的办法呢?此时我注意到python-markdown工具支持自定义扩展

from markdown import markdown
from markdown.extensions import Extension
from markdown.treeprocessors import Treeprocessor


class CustomTreeprocessor(Treeprocessor):

    def run(self, root):
        for elem in root.iter():
            if elem.tag == 'img':
                elem.set("loading", "lazy")

        return root


class CustomExtension(Extension):

    def extendMarkdown(self, md):
        md.treeprocessors.register(CustomTreeprocessor(md), 'ops-coffee-img', 1)


if __name__ == '__main__':
    c = '![](https://blog.ops-coffee.cn/static/images/wx.sou1.png)'
    print(markdown(c, extensions=['extra', CustomExtension()]))

编写CustomExtension自定义扩展类,然后在生成HTML文件时引用扩展类,就可以不需要修改markdown内容,自动在IMG标签上添加loading属性了,问题完美解决