又名給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解析效果

解析後為:
<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