4
头图
又名给vuepress写一个插件😊

最终的效果如下

思路

最近在看pwa时,在MDN上看见一段有趣的关于实现渐进式图片加载的snippet,大意如下:

<img src="thumbnail.png" data-src="origin.png"></img>
var imgToLoad=document.qureySelectorAll('img[data-src]')
imgToLoad.forEach(img=>{
img.setAttribute('src',imgAttribute('data-src'));
img.onload=()=>{
   img.removeAttribute('data-src')
}
})
 img[data-src] {
            filter: blur(0.2em);
        }
        
 img {
            filter: blur(0em);
            transition: filter 0.3s;
    }

总得来说便是在加载渲染页面时,先加载和渲染页面的缩略图,作为一个placeholder,然后再去加载真正的原图,当原图加载完毕后,再渲染原图。这缩短了页面加载渲染的时间,让用户打开页面能够快速获得反馈。

我们可能在medium和gatsby.js网站上见到过这种图片加载方式,但是vuepress本身是不支持的,于是便想实现这种图片加载方式,顺便写一个vuepress插件。

实现的步骤如下:

1. 生成一张缩略图,然后插入进去

根据需求,我们想要这样一个markdown解析效果

![image](./origin.jpg)

解析后为:

<img src="./thumbnial.jpg" data-src="./orign.jpg"></img>

首先需要说明一下一个vuepress页面是如何生成的

         markdown-loader       vue-loader
markdown--------------->vue SFCs?------>vue spa

上述并不准确,因为没有去深究中间产物到底是什么。
回到主线上,官网上给出,vuepress使用markdonw-it作为markdown的解析器,即上述的markdown-loader其实就是一个包装后的markdown-it,同时vuepress暴露出了extendMarkdown这一api用来改写markdown解析规则。
所以,我们可以通过这一api,插入我们自定义的关于图片的解析规则就能够实现我们所需要的结果。于是我们可以写一个mark-it的插件来实现这一功能。
在这,我遇到了两个坑,分别是

  • markdown-it插件仅支持同步函数
  • vue-loader默认不解析data-src这一attribute.因为vuepres使用了url-loader,所以会将图片之类的静态文件给存放在assets/images文件夹下,而data-src中的图片地址却不会发生变化。

第二个非常好办,我们可以通过chainWebpack这一api该变vuepress的webpack config就可以实现,即在vueloader的transformAssetUrls上添加img:["data-src"]就好了。

对于第一个生成原图片的缩略图,我使用了imageThumbnail库,该库仅支持异步方法。所以我们需要将该异步方法包装成真正的同步方法。这里使用到了一个deasync的库,它暴露了node底层的一个sleep()方法,来使main line 强制挂起,提前进行事件循环,直到事件循环结束,再执行主线程序来实现同步。

2. 加载原图片

这一步可以使用clientRootMixin这一api,给globalLayout.vue混入一个全局的方法就可以了~

具体代码可以看一下俺的github仓库,由于接触vuepress不深入,有些部分的实现非常的naive,而且整个代码也不是很优雅,很多方法都非常僵硬,欢迎大家pr或者自己编写代码更好地实现👏。

另,我将该插件上传到了npm上,大家可以npm install玩一玩~

$ npm install vuepress-plugin-progressive-image-loader

huaiyuG
63 声望1 粉丝

Front-end beginner.