圖片太多或者太大將導致頁面加載完成緩慢:圖片太多導致向服務器請求的次數太多,圖片太大導致每次請求的時間過長。
以下是幾種優化方案:
1、將圖片服務與應用服務分離:
對於服務器來說,圖片始終是最消耗系統資源的,如果將圖片服務和應用服務放在同一服務器的話,應用服務器很容易會因爲圖片的高I/O負載而崩潰,所以當網站存在大量的圖片讀寫操作時,建議使用圖片服務器.
(注:圖片服務器是專門爲圖片讀寫操作優化的獨立服務器,運行網站的服務器稱爲應用服務器)
另外瀏覽器在同一時間對同一域名下的資源的併發請求數目是有限制的,一般在2-6之間,超過限制數目的請求就會被阻塞.一些主流瀏覽器對 HTTP1.1 和 HTTP 1.0 的最大併發連接數目,可以參考如下圖:
把圖片服務器與應用服務器分開,圖片服務器採用獨立域名 ,css、js和圖片就可以併發請求了。
2、圖片壓縮
藉助第三方軟件來進行壓縮,比如 https://tinypng.com/,壓縮後分辨率不變,肉眼看不失真。
3、圖標字體
一些小圖標使用圖標字體,通過引入font文件後添加 class 的方式引入,參考: https://icomoon.io/app/#/select
4、圖片懶加載
圖片懶加載,簡單來說就是在頁面渲染過程中,圖片不會一次性全部加載,會在需要的時候加載,比如當滾動條滾動到某一個位置時觸發事件加載圖片,如下代碼:
// html
<img src="default.jpg" data-src="http://ww4.sinaimg.cn/large/006y8mN6gw1fa5obmqrmvj305k05k3yh.jpg" />
// js
function lazyload(els, n) { //監聽頁面滾動事件
var n = n || 0; //存儲圖片加載到的位置,避免每次都從第一張圖片開始遍歷
var num = els.length;
var seeHeight = document.documentElement.clientHeight; //可見區域高度
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滾動條距離頂部高度
for (var i = n; i < num; i++) {
if (els[i].offsetTop < seeHeight + scrollTop) {
if (els[i].getAttribute("src") == "default.jpg") {
els[i].src = els[i].getAttribute("data-src");
}
n = i + 1;
}
}
}
// 簡單的節流函數
//fun 要執行的函數
//delay 延遲
//time 在time時間內必須執行一次
function throttle(fun, delay, time) {
var timeout,
startTime = new Date();
return function() {
var context = this,
args = arguments,
curTime = new Date();
clearTimeout(timeout);
// 如果達到了規定的觸發時間間隔,觸發 handler
if (curTime - startTime >= time) {
fun.apply(context, args);
startTime = curTime;
// 沒達到觸發間隔,重新設定定時器
} else {
timeout = setTimeout(fun, delay);
}
};
};
// 採用節流函數
var els = document.getElementsByTagName("img");
window.addEventListener('scroll',throttle(lazyload(els,0),500,1000));
通過js將img標籤的data-src屬性賦值給src屬性
5、精靈圖 css Sprites
當網站或者APP有大量小icon,如果上傳到圖片服務器比如CDN,要加載所有這些小icon將增加大量請求,而CDN是按流量計費的,這無疑將增加很多成本。
CSS Sprites 將小icon合併成一張圖片,只需要加載一次,每次通過background-position來控制顯示icon,這樣就可以節約大量請求。如圖:
缺點:在長期開發多人合作的項目中,會不好維護這些sprites,每次對icon做修改,都得相應的改動css裏background-position的值,比較繁瑣.
6、將圖片壓縮成base64格式來節約請求
將圖片轉換成base64,實際上是變大了,並且瀏覽器在decode base64編碼的圖片時需要耗費很多時間的,所以如果我們選擇此種方案的話,最好選擇一些小圖片,不然得不償失,在webpack中可以設置最大多少byte的圖片壓縮成base64,配置示例如下:
module: {
// 編譯器配置
loaders: [
// .css文件用style-loader和css-loader處理
{
test: /\.css$/,
loader: 'style-loader!css-loader'
},
// .styl文件用style-loader、css-loader和stylus-loader編譯
{
test: /\.styl(us)?$/,
loader: 'style!css!stylus?sourceMap'
},
// .js文件用jsx-loader編譯
{
test: /\.js$/,
loader: 'js-loader?harmony'
},
// 圖片用用url-loader處理,小於2k的直接轉爲base64
{
test: /\.(png|jpg|jpeg)$/,
loader: 'url-loader?limit=2048'
}
]
}
針對decode base64編碼的圖片比較慢的問題,我們可以選擇使用canvas來加速.當向canvas發出繪畫命令時,瀏覽器直接將指令發到GPU實時繪畫和渲染圖形.因此,我們可以使用canvas來渲染base64編碼後的圖片,示例代碼如下:(TODO:圖片更新時優化)
// 緩存圖片,並將圖片渲染
var renderCvs = function(parent,max){
//將img標籤的class設置爲lazyload,其父節點是a標籤
var lazyloadImage = $('.lazyload',parent);
if(lazyloadImage.length<1){
return;
}
var max = max||lazyloadImage.length;
for(var i=0;i<max;i++){
var imgId = lazyloadImage[i].id;
//再次打開網頁,讀取緩存,如果有,就從緩存中展示
var imageCache = localStorage.getItem(imgId);
if(imageCache){
lazyloadImage[i].src = imageCache;
continue;
}
var img = new Image();
img.index = i;
img.id = imgId;
img.crossorigin="anonymous";
img.onload = function(){
var _this = this;
var zCvs = $('#'+this.id);
var domCvs = zCvs[0];
domCvs.src = this.src;
zCvs.removeClass('lazyload');
try{
var cvs = document.createElement('canvas');
cvs.style.display = 'none';
document.body.appendChild(cvs);
var rcvs = cvs.getContext('2d');
cvs.width = 140;
cvs.height = 108;
rcvs.drawImage(this,0,0,140,108);
//這裏進行緩存 ,toDataURL()方法將url轉成base64字符串
setTimeout(function(){
var data = cvs.toDataURL();
//將字符串保存到localStorage中
localStorage.setItem(_this.id,data);
document.body.removeChild(cvs);
},200);
}catch(ex){
}
}
img.src = lazyloadImage[i].getAttribute('data-src');
}
}
7、採用更高壓縮比的圖片格式
webp、bpg、sharpP等新圖片格式具有更好的壓縮比,可以使用這類新型的圖片來代替原始圖片
原理:對圖片格式轉換,在畫質可以接受的情況下達到更好的壓縮比效果
優勢:減少圖片加載流量,效果比較明顯
劣勢:服務器和瀏覽器壓力增大,而且服務器需要額外的服務支持,格式轉換要考慮瀏覽器的兼容性
8、漸進式加載
先加載低分辨率的圖片,再加載高分辨率圖片。