站頁面速度提升,用戶停留時間會變長,跳出率會降低。
尤其在移動端更加明顯,谷歌和百度都做過類似的分析和研究,結論很接近:
當頁面加載速度從1秒變成3秒時,跳出率提升了32%。
當頁面加載速度從1秒變成5秒時,跳出率提升了90%。
換句話說,如果你的移動端頁面5秒才能打開,90%的人都會關掉頁面離開。
國外幾個網站測試過速度下降對業務指標的負面影響:
Bing - 網頁速度打開慢2秒,(收入/用戶)比下降4.3%。
Google - 400毫秒的延遲導致(搜索量/用戶)下降0.59%。
雅虎 - 400毫秒的延遲導致全站流量下降5-9%。
提升頁面速度的好處:
Shopzilla - 網站提速5秒,轉換率提升了7-12%,SEM投放會話量增加了一倍,所需服務器減少了一半。
Mozilla - 著陸頁打開速度比原來快了2.2秒后,下載轉化率提升了15.4%,因此每年多增加6000萬次Firefox下載。
Netflix - 僅僅因為使用了Gzip壓縮,頁面速度就提升了13-25%,帶寬費用減少了50%。
谷歌博客2010年就說過:“我們已經決定把網站的打開速度作為搜索排名因素?!?/p>
如何提升頁面打開速度呢?
頁面打開速度受非常多因素影響,簡單的可以劃分為“前端”和“后端”兩部分。
“后端”包含了服務器性能、功能實現方法、頁面模塊、緩存時間、CDN加速等等,受后端技術和硬件條件的制約和影響。
這部分工作一般由研發工程師和運維來負責解決。實現成本或高獲低,存在不少不確定性。
而“前端”部分則相對清晰,按照優化規范的指導,前端工程師可以立即對網頁進行“瘦身”。
轉載Github上翻譯的前端優化指南如下。(待補充完善本文)
前端優化指南
避免 內聯式/嵌入式 代碼
你可以通過三種方式在HTML頁面中引入CSS或Javascript代碼:
1) 內聯式: 在HTML標簽的style屬性中定義樣式,在onclick這樣的屬性中定義Javascript代碼;
2) 嵌入式: 在頁面中使用<style>標簽定義樣式,使用<script>標簽定義Javascript代碼;
3) 引用外部文件: 在<link>標簽中定義href屬性引用CSS文件,在<script>標簽中定義src屬性引入Javascript文件.
盡管前兩種方式減少了HTTP請求數,可是實際上卻增加了HTML文檔的體積。不過,當你的頁面中的CSS或者Javascript代碼足夠少,反而是開啟一個HTTP請求的花費要更大時,采用這兩種方式卻是最有用的。因此,你需要測試評估這種方式是否真的提升了速度。同時也要考慮到你的頁面的目標和它的受眾:如果你期望人們只會訪問它一次,例如對一些臨時活動來說,你決不會期望有回訪客出現,那么使用內聯式/嵌入式代碼能夠幫助減少HTTP請求數。
> 盡量避免在你的HTML中手工編寫CSS/JS(首選的方法是通過工具實現這個過程的自動化)。
第三種方式不僅使你的代碼更有序,而且使得瀏覽器能夠緩存它。這種方式在大多數的情況下都是首選,特別是一些大文件和多頁面的情況。
> 小工具 / 參考
樣式在上,腳本在下
當我們把樣式放在<head>標簽中時,瀏覽器在渲染頁面時就能盡早的知道每個標簽的樣式,我們的用戶就會感覺這個頁面加載的很快。
<head> <meta charset="UTF-8"> <title>Browser Diet</title> <!-- CSS --> <link rel="stylesheet" href="style.css" media="all"></head>
但是如果我們將樣式放在頁面的結尾,瀏覽器在渲染頁面時就無法知道每個標簽的樣式,直到CSS被下載執行后。
另一方面,對于Javascript來說,因為它在執行過程中會阻塞頁面的渲染,所以我們要把它放在頁面的結尾。
<body> <p>Lorem ipsum dolor sit amet.</p> <!-- JS --> <script src="script.js"></script></body>
> 參考
嘗試async
為了解釋這個屬性對于性能優化是多么有用,我們應該先明白,當不使用它時會發生什么。
<script src="example.js"></script>
使用上面這種方式時,頁面會在這個腳本文件被完全下載、解析、執行完后才去渲染之后的HTML,在這之前會一直處于阻塞狀態。這就意味著會增加你的頁面的加載時間。有時這種行為是我們希望的,而大多數時候則不想要。
<script async src="example.js"></script>
使用上面這種方式時,腳本的加載是異步的,不會影響到這之后的頁面解析。腳本會在下載完之后立即執行。需要注意的是,如果有多個使用這種方式異步加載的腳本,他們是沒有特定的執行順序的。
> 參考
壓縮你的樣式表
為了保持代碼的可讀性,最好的方法是在代碼中添加注釋和使用縮進:
.center { width: 960px; margin: 0 auto;}/* --- Structure --- */.intro { margin: 100px; position: relative;}
但是對于瀏覽器來說,這些都是不重要的。正因為如此,通過自動化工具壓縮你的CSS是非常有用的。
.center{width:960px;margin:0 auto}.intro{margin:100px;position:relative}
這樣做能夠減小文件的大小,從而得到更快的下載、解析和執行。
對于使用預處理器例如 Sass, Less, and Stylus, 你可以通過配置縮小編譯輸出的CSS代碼。
> 小工具 / 參考
合并多個CSS文件
對于樣式的組織和維護,另一個好方法是將他們模塊化。
<link rel="stylesheet" href="structure.css" media="all"><link rel="stylesheet" href="banner.css" media="all"><link rel="stylesheet" href="layout.css" media="all"><link rel="stylesheet" href="component.css" media="all"><link rel="stylesheet" href="plugin.css" media="all">
然而,這樣每個文件就是一個HTTP請求(我們都知道,瀏覽器的并行下載數是有限的)。
<link rel="stylesheet" href="main.css" media="all">
所以,合并你的CSS文件。文件數量的減少就會帶來請求數量的減少和更快的頁面加載速度。
Want to have the best of both worlds? Automate this process through a build tool.
> 小工具 / 參考
使用 標簽而不是 @import
有兩種方式可以引入一個外部的樣式表:通過 <link> 標簽:
<link rel="stylesheet" href="style.css">
或者通過 @import 指令 (使用在一個外部樣式表中或者頁面內嵌的 <style> 標簽中):
@import url('style.css');
當你在一個外部樣式表中使用第二種方式時,瀏覽器無法通過并行下載的方式下載這個資源,這樣就會導致其他資源的下載被阻塞。
> 參考
異步加載第三方內容
嵌入一個Youtube視頻或者一個like/tweet按鈕,有人沒有加載過這樣的第三方內容嗎?
問題在于,不管是用戶端的還是服務器端的連接,都無法保證這些代碼是正常有效的工作的。這些服務有可能臨時dowan掉或者是被用戶或者其公司的防火墻阻止。
為了避免這些在頁面加載時成為問題,或者更嚴重的是,阻塞了全部頁面的加載,總是應該異步加載這些代碼 (或者使用 Friendly iFrames).
var script=document.createElement('script'), scripts=document.getElementsByTagName('script')[0];script.async=true;script.src=url;scripts.parentNode.insertBefore(script, scripts);
另外,如果你想加載多個第三方插件,你可以使用這個代碼來實現異步的加載。
> 視頻 / 參考
緩存數組長度
循環無疑是和Javascript性能非常相關的一部分。試著優化循環的邏輯,從而讓每次循環更加的高效。
要做到這一點,方法之一是存儲數組的長度,這樣的話,在每次循環時都不用重新計算。
var arr=new Array(1000), len, i;for (i=0; i < arr.length; i++) { // Bad - size needs to be recalculated 1000 times}for (i=0, len=arr.length; i < len; i++) { // Good - size is calculated only 1 time and then stored}
> Results on JSPerf
> 注解:雖然現代瀏覽器引擎會自動優化這個過程,但是不要忘記還有舊的瀏覽器
在迭代document.getElementsByTagName('a')等類似方法生成的HTML節點數組(NodeList)時,緩存數組長度尤為關鍵。這些集合通常被認為是“活的”,也就是說,當他們所對應的元素發生變化時,他們會被自動更新。
var links=document.getElementsByTagName('a'), len, i;for (i=0; i < links.length; i++) { // Bad - each iteration the list of links will be recalculated to see if there was a change}for (i=0, len=links.length; i < len; i++) { // Good - the list size is first obtained and stored, then compared each iteration}// Terrible: infinite loop examplefor (i=0; i < links.length; i++) { document.body.appendChild(document.createElement('a')); // each iteration the list of links increases, never satisfying the termination condition of the loop // this would not happen if the size of the list was stored and used as a condition}
> 參考
避免使用document.write
這個(壞)方法已經被開發者拋棄了很多年, 但是在某些情況下仍然是需要的,例如在一些Javascript文件的同步回退中。
舉例來說,如果發現Google的CDN沒有響應,HTML5 Boilerplate則會通過這個方法來調用本地的jQuery庫。
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script><script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.0.min.js"><\/script>')</script>
> 注意:如果在window.onload事件中或之后執行document.write方法,會將當前頁面替換掉。
<span>foo</span><script> window.onload=function() { document.write('<span>bar</span>'); };</script>
這段代碼執行后的結果是頁面中只會呈現出bar字符,而不是期望的出現foobar。在window.onload事件之后執行也是同樣的結果。
<span>foo</span><script> setTimeout(function() { document.write('<span>bar</span>'); }, 1000); window.onload=function() { // ... };</script>
這段代碼執行的結果和上一段代碼的結果相同。
> 參考
最小化重繪和回流
當有任何屬性或元素發生改變時,都會引起DOM元素的重繪和回流。
當一個元素的布局不變,外觀發生改變時,就會引起重繪。Nicole Sullivan描述這個就像是樣式的改變,例如改變background-color。
回流的代價是最高的,當改變一個頁面的布局時就會發生回流,例如改變一個元素的寬。
毫無疑問,應當避免過多的重繪和回流,所以,對于下面的代碼:
var div=document.getElementById("to-measure"), lis=document.getElementsByTagName('li'), i, len;for (i=0, len=lis.length; i < len; i++) { lis[i].style.width=div.offsetWidth + 'px';}
應當變為:
var div=document.getElementById("to-measure"), lis=document.getElementsByTagName('li'), widthToSet=div.offsetWidth, i, len;for (i=0, len=lis.length; i < len; i++) { lis[i].style.width=widthToSet + 'px';}
當你設置style.width時,瀏覽器需要重新計算布局。通常,瀏覽器暫時是不需要知道改變了元素的樣式的,直到它需要更新屏幕時,正因為如此,改變多個元素的樣式只會產生一次回流。然而,在第一個例子中,我們每次請求offsetWidth時,都會使瀏覽器重新計算布局。
如果需要得到頁面中的布局數據,那么請參照第二個例子,將這些操作放在任何會改變布局的設置前。
> 示例 / 參考
避免不必要的DOM操作
當你獲得DOM而又什么都不做時,這簡直就是在殺死寶貴的生命。
說真的,瀏覽器遍歷DOM元素的代價是昂貴的。雖然Javascript引擎變得越來越強大,越來越快速,但是還是應該最大化的優化查詢DOM樹的操作。
最簡單的替代方案就是,當一個元素會出現多次時,將它保存在一個變量中,這樣的話你就沒必要每次都去查詢DOM樹了。
// really bad!for (var i=0; i < 100; i++) { document.getElementById("myList").innerHTML +="<span>" + i + "</span>";}
// much better :)var myList="";for (var i=0; i < 100; i++) { myList +="<span>" + i + "</span>";}document.getElementById("myList").innerHTML=myList;
// much *much* better :)var myListHTML=document.getElementById("myList").innerHTML;for (var i=0; i < 100; i++) { myListHTML +="<span>" + i + "</span>";}
> Results on JSPerf
壓縮你的腳本
和CSS一樣,為了保持代碼的可讀性,最好的方法是在代碼中添加注釋和使用縮進:
BrowserDiet.app=function() { var foo=true; return { bar: function() { // do something } };};
但是對于瀏覽器來說,這些都是不重要的。正因為如此,請記住用自動化工具壓縮你的Javascript代碼。
BrowserDiet.app=function(){var a=!0;return{bar:function(){}}}
這樣做能夠減小文件的大小,從而得到更快的下載、解析和執行。
> 小工具 / 參考
將多個JS文件合并
對于腳本的組織和維護,另一個好方法是將他們模塊化。
<script src="navbar.js"></script><script src="component.js"></script><script src="page.js"></script><script src="framework.js"></script><script src="plugin.js"></script>
然而,這樣每個文件就是一個HTTP請求(我們都知道,瀏覽器的并行下載數是有限的)。
<script src="main.js"></script>
所以,合并你的JS文件。文件數量的減少就會帶來請求數量的減少和更快的頁面加載速度。
想要兩全其美?通過構建工具自動化這個過程吧。
> 小工具 / 參考
jQuery Selectors
在使用jQuery時,選擇器也是一個重要的問題。有許多方法可以從DOM中選取元素,但這不意味著這些方法有相同的性能,你可以用classes、IDs或者find()、children()等方法選取元素。
在這些方法中,使用ID選擇器是最快的,因為它是原生DOM操作。
$("#foo");
> Results on JSPerf
JavaScript中使用for,而不是each
原生Javascript中的函數執行幾乎總是要比jQuery快一些。正因為如此,請使用Javascript的for循環,不要使用jQuery.each方法。
但是請注意,雖然for in是原生的,可是在許多情況下,它的性能要比jQuery.each差一些。
在for循環迭代時,請記得緩存集合的長度。
for ( var i=0, len=a.length; i < len; i++ ) { e=a[i];}
在社區中,關于while和for循環的反向使用問題是一個熱門話題,而這經常被認為是最快的迭代方式。然而實際上,這通常只是為了防止循環不夠清晰。
// 逆轉 whilewhile ( i-- ) { // ...}// 逆轉 forfor ( var i=array.length; i--; ) { // ...}
> Results on JSPerf / 參考
使用CSS Sprites
這個技術就是將各種圖片整合到一個文件中去。
然后通過CSS去定位它們。
.icon-foo { background-image: url('mySprite.png'); background-position: -10px -10px;}.icon-bar { background-image: url('mySprite.png'); background-position: -5px -5px;}
這么做的結果就是,減少了HTTP請求數,避免延遲頁面上的其他資源。
在使用sprite時,應當避免在每個圖片之間的空隙過大。這個雖然不會影響到文件的大小,但是會影響到內存的消耗。
盡管每個人都知道sprites,但是這種技術并沒有被廣泛使用—或許是由于開發者沒有使用自動化工具去生成。 我們著重介紹了一些工具,或許可以幫到你。
> 小工具 / 參考
適當時可以使用Data URI顯示圖片
這種技術是CSS Sprites的替代方法。
Data-URI是指使用圖片的數據代替通常使用的圖片URI,在下面的例子中,我們就使用它減少了HTTP請求數。
使用前:
.icon-foo { background-image: url('foo.png');}
使用后:
.icon-foo { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII%3D');}
所有的現代瀏覽器和IE8及以上版本的IE都支持這個方法,圖片需要使用base64方法編碼。
這種技術和CSS Sprites技術都是可以使用構建工具得到的。使用構建工具的好處是不用手工去進行圖片的拼合替換,在開發時使用單獨的文件就可以。
然而壞處是,隨著你的HTML/CSS文件的增大增多,你必須考慮你可能會有一個非常大的圖片。如果你在HTTP請求中沒有使用gzip技術壓縮你的HTML/CSS,那么我們不推薦使用這種方法,因為減少HTTP請求數得到的大文件對于速度來說可能帶來相反的結果。
> 小工具 / 參考
不要在<img>標簽中調整圖像
總是在img標簽中設置width和height屬性。這樣可以防止渲染過程中的重繪和回流。
<img width="100" height="100" src="logo.jpg" alt="Logo">
知道這個之后,一個開發者將一個700x700px的圖像設置為50x50px來顯示。
但是這個開發者不知道的是,大量的沒有用的數據也發送到了客戶端。
所以請記?。耗憧梢栽跇撕炛卸x一個圖片的寬高,但不意味著你應該通過這么做來(等比)縮放大圖。
> 參考
優化你的圖片。
圖片文件中包含許多對于Web來說沒有用的東西。舉例來說,一個JPEG圖片中可能包含一些Exif元數據(數據,相機型號,坐標等等)。一個PNG圖片會包含有關顏色,元數據的信息,有時甚至還包含一個縮略圖。這些只會增加文件的大小,而對于瀏覽器來說卻毫無用處。
有很多工具能夠幫你從圖片中去除這些信息,并且不會降低圖片的質量。我們把這個稱做無損壓縮。
另一種優化圖片的方式是,以圖片質量為代價進行壓縮。我們稱之為有損壓縮。舉例來說,當你導出一個JPEG圖片時,你可以選擇導出的圖片質量(從0到100)??紤]到性能,總是選擇可接受范圍內的最低值。在PNG圖片中,另一個常見的有損技術是減少顏色數量,或者將PNG-24格式轉換為PNG-8格式。
為了提升用戶的體驗,你還應該將你的JPEG文件轉換為漸進式的?,F在大多數的瀏覽器都支持漸進式JPEG文件,并且這種格式的文件創建簡單,沒有明顯的性能損失問題。頁面中的這種格式的圖片能夠更快的展現(看例子).
> 小工具 / 參考
診斷工具檢查你的優化成果,查看優化建議
如果你想知道這個世界上的Web性能,那么你一定要給你的瀏覽器安裝YSlow 從現在起,它們將是你最好的朋友。
或者你可以選擇使用在線工具,訪問WebPageTest, HTTP Archive或者PageSpeed。
以上一些網站有可能需要科學上網,國內可以使用百度統計后臺的測速工具。
百度統計后臺菜單底部有一個“網站速度診斷”,添加網址就可以測速,還會給出診斷優化建議,有網通和電信兩條線路。
附錄
不少團隊在前端優化方面也做了很多實踐,非常有學習價值:
前端優化實踐總結 | http://Aotu.io「凹凸實驗室」
https://aotu.io/notes/2016/04/12/jcloud-opt/index.html
移動H5前端性能優化指南 - 前端技術 - 騰訊ISUX
https://isux.tencent.com/h5-performance.html
Web 前端優化專題 - DBA Notes
http://dbanotes.net/web-performance.html
雅虎前端優化35條規則翻譯
https://github.com/creeperyang/blog/issues/1
淺析渲染引擎與前端優化-京東
https://jdc.jd.com/archives/2806
言:對于大多數前端工程師來說,圖片就是UI設計師(或者自己)切好的圖,你要做的只是把圖片丟進項目中,然后用以鏈接的方式呈現在頁面上,而且我們也經常把精力放在項目的打包優化構建上,如何分包,如何抽取第三方庫……..有時我們會忘了,圖片才是一個網站最大頭的那塊加載資源(見下圖),雖然圖片加載可以不不阻礙頁面渲染,但優化圖片,絕對可以讓網站的體驗提升一個檔次。
如果效果真的需要圖片來表現,那么選擇圖片格式是優化的第一步。我們經常聽到的詞語包括矢量圖、標量圖、SVG、有損壓縮、無損壓縮等等,我們首先說明各種圖片格式的特點
圖片格式壓縮方式透明度動畫瀏覽器兼容適應場景JPEG有損壓縮不支持不支持所有復雜顏色及形狀、尤其是照片 漸進式吃cpuGIF無損壓縮支持支持所有簡單顏色,動畫PNG無損壓縮支持不支持所有需要透明時,但是體積太大APNG無損壓縮支持支持FirefoxSafariiOS Safari需要半透明效果的動畫WebP有損壓縮支持支持ChromeOperaAndroid ChromeAndroid Browser復雜顏色及形狀瀏覽器平臺可預知SVG無損壓縮支持支持所有(IE8以上)簡單圖形,需要良好的放縮體驗需要動態控制圖片特效
壓縮圖片可以使用統一的壓縮工具 — imagemin,它是一款可以集成多個壓縮庫的工具,支持jpg,png,webp等等格式的圖片壓縮,比如pngquant,mozjpeg等等,作為測試用途,我們可以直接安裝imagemin-pngquant來嘗試png圖片的壓縮
npm install imagemin
npm install imagemin-pngquant
``
先安裝imagemin庫,再安裝對應的png壓縮庫
```js
const imagemin=require('imagemin');
const imageminPngquant=require('imagemin-pngquant');
(async ()=> {
await imagemin(['images/*.png'], 'build/images', {
plugins: [
imageminPngquant({ quality: '65-80' })
]
});
console.log('Images optimized');
})();
quailty一項決定壓縮比率,65-80貌似是一個在壓縮率和質量之間實現平衡的數值
npm install imagemin-mozjpeg
const imagemin=require('imagemin');
const imageminMozjpeg=require('imagemin-mozjpeg');
(async ()=> {
await imagemin(['images/*.jpg'], 'build/images', {
use: [
imageminMozjpeg({ quality: 65, progressive: true })
]
});
console.log('Images optimized');
})();
注意到我們使用了progressive:true選項,這可以將圖片轉換為漸進式圖片,關于漸進式圖片,它允許在加載照片的時候,如果網速比較慢的話,先顯示一個類似模糊有點小馬賽克的質量比較差的照片,然后慢慢的變為清晰的照片:
漸進式圖片 Progressive JPEG
Progressive JPEG文件包含多次掃描,這些掃描順尋的存儲在JPEG文件中。打開文件過程中,會先顯示整個圖片的模糊輪廓,隨著掃描次數的增加,圖片變得越來越清晰。這種格式的主要優點是在網絡較慢的情況下,可以看到圖片的輪廓知道正在加載的圖片大概是什么。在一些網站打開較大圖片時,你就會注意到這種技術。
非漸進式的圖片(Baseline JPEG)
這種類型的JPEG文件存儲方式是按從上到下的掃描方式,把每一行順序的保存在JPEG文件中。打開這個文件顯示它的內容時,數據將按照存儲時的順序從上到下一行一行的被顯示出來,直到所有的數據都被讀完,就完成了整張圖片的顯示。如果文件較大或者網絡下載速度較慢,那么就會看到圖片被一行行加載的效果,這種格式的JPEG沒有什么優點,因此,一般都推薦使用Progressive JPEG。
基本JPEG和漸進JPEG該什么時候使用?
當您的JPEG圖像低于10K時,最好保存為基本JPEG(估計有75%的可能性會更小) 對于超過10K的文件,漸進式JPEG將為您提供更好的壓縮(在94%的情況下) Chrome + Firefox + IE9瀏覽器下,漸進式圖片加載更快,而且是快很多,至于其他瀏覽器,與基本式圖片的加載一致,至少不會拖后腿。
漸進式圖片也有不足,就是吃CPU吃內存。
總結一下兩者的區別:
漸進式jpeg(progressive jpeg)圖片及其相關 簡單來說,漸進式圖片一開始就決定了大小,而不像Baseline圖片一樣,不斷地從上往下加載,從而造成多次回流,但漸進式圖片需要消耗CPU去多次計算渲染,這是其主要缺點。 當然,交錯式png也可以實現相應的效果,但目前pngquant沒有實現轉換功能,但是ps中導出png時是可以設置為交錯式的。
那我們怎么查看圖片是漸進式還是基本的呢
通過對比保存的圖片格式(格式在線分析:https://exif.tuchong.com/)
也有一些網上推薦的轉化工具
https://www.imgonline.com.ua/eng/compress-image.php
http://www.imagemagick.org/script/download.php
說了這么多,是不是感覺很啰嗦,接下來我們在實際項目中如何操作
實際項目中,總不能UI丟一個圖過來你就跑一遍壓縮代碼吧?幸好imagemin有對應的webpack插件,在webpack遍地使用的今天,我們可以輕松實現批量壓縮:
先安裝imagemin-webpack-plugin
npm install imagemin-webpack-plugin
import ImageminPlugin from 'imagemin-webpack-plugin'
import imageminMozjpeg from 'imagemin-mozjpeg'
module.exports={
plugins: [
new ImageminPlugin({
plugins: [
imageminMozjpeg({
quality: 100,
progressive: true
})
]
})
]
}
接著在webpack配置文件中,引入自己需要的插件,使用方法完全相同。具體可參考github的文檔imagemin-webpack-plugin
同時我們推薦幾種比較好用的圖片壓縮工具
1. docsmall在線圖片壓縮
https://docsmall.com/
國內公司開發在線圖片壓縮工具
服務器在國內,上傳速度很快
頁面簡潔無廣告,美觀大方
壓縮率很好,基本能壓縮到原來的一半以下
壓縮出的圖片畫質很清晰,跟原圖幾乎沒有差別
對png、jpg格式的支持都很好
還有針對PDF的壓縮功能
2. tinypng
https://tinypng.com/
國外團隊開發的在線圖片壓縮網站,有口皆碑
唯一的問題就是上傳速度不夠快,畢竟是國外的
界面全英文,對英語不好的朋友來說不夠友好
3. 智圖
https://zhitu.isux.us/
騰訊的一個團隊出品
可以自定義壓縮比例,如果壓出來的體積不夠小,你還可以選擇一個更高的壓縮率
保證圖片體積夠小
圖片按需加載是個老生常談的話題,傳統做法自然是通過監聽頁面滾動位置,符合條件了再去進行資源加載,我們看看如今還有什么方法可以做到按需加載。
使用強大的IntersectionObserver IntersectionObserver提供給我們一項能力:可以用來監聽元素是否進入了設備的可視區域之內,這意味著:我們等待圖片元素進入可視區域后,再決定是否加載它,畢竟用戶沒看到圖片前,根本不關心它是否已經加載了。 這是Chrome51率先提出和支持的API,而現在,各大瀏覽器對它的支持度已經有所改善(除了IE,全線崩~) 廢話不多說,上代碼: 首先,假設我們有一個圖片列表,它們的src屬性我們暫不設置,而用data-src來替代:
<li>
<img class="list-item-img" alt="loading" data-src='a.jpg'/>
</li>
<li>
<img class="list-item-img" alt="loading" data-src='b.jpg'/>
</li>
<li>
<img class="list-item-img" alt="loading" data-src='c.jpg'/>
</li>
<li>
<img class="list-item-img" alt="loading" data-src='d.jpg'/>
</li>
這樣會導致圖片無法加載,這當然不是我們的目的,我們想做的是,當IntersectionObserver監聽到圖片元素進入可視區域時,將data-src”還給”src屬性,這樣我們就可以實現圖片加載了:
const observer=new IntersectionObserver(function(changes) {
changes.forEach(function(element, index) {
// 當這個值大于0,說明滿足我們的加載條件了,這個值可通過rootMargin手動設置
if (element.intersectionRatio > 0) {
// 放棄監聽,防止性能浪費,并加載圖片。
observer.unobserve(element.target);
element.target.src=element.target.dataset.src;
}
});
});
function initObserver() {
const listItems=document.querySelectorAll('.list-item-img');
listItems.forEach(function(item) {
// 對每個list元素進行監聽
observer.observe(item);
});
}
initObserver();
運行代碼并觀察控制臺的Network,會發現圖片隨著可視區域的移動而加載,我們的目的達到了。
IntersectionObserver
瀏覽器兼容
還是Chrome的黑科技——loading屬性
從新版本Chrome(76)開始,已經默認支持一種新的html屬性——loading,它包含三種取值:auto、lazy和eager(ps: 之前有文章說是lazyload屬性,后來chrome的工程師已經將其確定為loading屬性,原因是lazyload語義不夠明確),我們看看這三種屬性有什么不同:
這個現象跟chrome的lazy-loading功能的實現機制有關:
首先,瀏覽器會發送一個預請求,請求地址就是這張圖片的url,但是這個請求只拉取這張圖片的頭部數據,大約2kb,具體做法是在請求頭中設置range: bytes=0-2047,
而從這段數據中,瀏覽器就可以解析出圖片的寬高等基本維度,接著瀏覽器立馬為它生成一個空白的占位,以免圖片加載過程中頁面不斷跳動,這很合理,總不能為了一個懶加載,讓用戶犧牲其他方面的體驗吧?這個請求返回的狀態碼是206,表明:客戶端通過發送范圍請求頭Range抓取到了資源的部分數據,詳細的狀態碼解釋可以看看這篇文章
然后,在用戶滾動到圖片附近時,再發起一個請求,完整地拉取圖片的數據下來,這個才是我們熟悉的狀態碼200請求。
可以預測到,如果以后這個屬性被普遍使用,那一個服務器要處理的圖片請求連接數可能會變成兩倍,對服務器的壓力會有所增大,但時代在進步,我們可以依靠http2多路復用的特性來緩解這個壓力,這時候就需要技術負責人權衡利弊了
要注意,使用這項特性進行圖片懶加載時,記得先進行兼容性處理,對不支持這項屬性的瀏覽器,轉而使用JavaScript來實現,比如上面說到的IntersectionObserver:
if ("loading" in HTMLImageElement.prototype) {
// 支持loading
} else {
// .....
}
當網速慢的時候,圖片還沒加載完之前,用戶會看到一段空白的時間,在這段空白時間,就算是漸進式圖片也無法發揮它的作用,我們需要更友好的展示方式來彌補這段空白,有一種方法簡單粗暴,那就是用一張占位圖來頂替,這張占位圖被加載過一次后,即可從緩存中取出,無須重新加載,但這種圖片會顯得有些千篇一律,并不能很好地做到preview的效果。
這里介紹另一種占位圖做法——css漸變色背景,原理很簡單,當img標簽的圖片還沒加載出來,我們可以為其設置背景色,比如:
<img src="a.jpg" style="background: red;"/>
這樣會先顯示出紅色背景,再渲染出真實的圖片,重點來了,我們此時要借用工具為這張圖片"配制"出合適的漸變背景色,以達到部分preview的效果,我們可以使用 https://calendar.perfplanet.com/2018/gradient-image-placeholders/ 這篇文章中推薦的工具GIP進行轉換 ,這里附上在線轉換的地址 https://tools.w3clubs.com/gip/
經過轉換后,我們得到了下面這串代碼:
background: linear-gradient(
to bottom,
#1896f5 0%,
#2e6d14 100%
)
我們經常會遇到這種情況:一張在普通筆記本上顯示清晰的圖片,到了蘋果的Retina屏幕或是其他高清晰度的屏幕上,就變得模糊了。
這是因為,在同樣尺寸的屏幕上,高清屏可以展示的物理像素點比普通屏多,比如Retina屏,同樣的屏幕尺寸下,它的物理像素點的個數是普通屏的4倍(2 * 2),所以普通屏上顯示清晰的圖片,在高清屏上就像是被放大了,自然就變得模糊了,要從圖片資源上解決這個問題,就需要在設備像素密度為2的高清屏中,對應地展示一張兩倍大小的圖。
而通常來講,對于背景圖片,我們可以使用css的@media進行媒體查詢,以決定不同像素密度下該用哪張倍圖,例如:
.bg {
background-image: url("bg.png");
width: 100px;
height: 100px;
background-size: 100% 100%;
}
@media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2)
{
.bg {
background-image: url("bg@2x.png") // 尺寸為200 * 200的圖
}
}
這么做有兩個好處,一是保證高像素密度的設備下,圖片仍能保持應有的清晰度,二是防止在低像素密度的設備下加載大尺寸圖片造成浪費。
那么如何處理img標簽呢?
我們可以使用HTML5中img標簽的srcset來達到這個效果,看看下面這段代碼:
<img width="320" src="bg@2x.png" srcset="bg.png 1x;bg@2x.png 2x"/>
這段代碼的作用是:當設備像素密度,也就是dpr(devicePixelRatio)為1時,使用bg.png,為2時使用二倍圖bg@2x.png,依此類推,你可以根據需要設置多種精度下要加載的圖片,如果沒有命中,瀏覽器會選擇最鄰近的一個精度對應的圖片進行加載。 要注意:老舊的瀏覽器不支持srcset的特性,它會繼續正常加載src屬性引用的圖像。
要同時適配不同像素密度、不同大小的屏幕,應該怎么辦呢?
<picture>
<source media="(max-width: 500px)" srcset="cat-vertical.jpg">
<source media="(min-width: 501px)" srcset="cat-horizontal.jpg">
<img src="cat.jpg" alt="cat">
</picture>
就要用到標簽。它是一個容器標簽,內部使用和,指定不同情況下加載的圖像。
上面代碼中,標簽內部有兩個標簽和一個標簽。
標簽的media屬性給出媒體查詢表達式,srcset屬性就是標簽的srcset屬性,給出加載的圖像文件。sizes屬性其實這里也可以用,但由于有了media屬性,就沒有必要了。
瀏覽器按照標簽出現的順序,依次判斷當前設備是否滿足media屬性的媒體查詢表達式,如果滿足就加載srcset屬性指定的圖片文件,并且不再執行后面的標簽和標簽。
標簽是默認情況下加載的圖像,用來滿足上面所有都不匹配的情況。
上面例子中,設備寬度如果不超過500px,就加載豎屏的圖像,否則加載橫屏的圖像。
除了響應式圖像,標簽還可以用來選擇不同格式的圖像。比如,如果當前瀏覽器支持 Webp 格式,就加載這種格式的圖像,否則加載 PNG 圖像。
<picture>
<source type="image/svg+xml" srcset="logo.xml">
<source type="image/webp" srcset="logo.webp">
<img src="logo.png" alt="ACME Corp">
</picture>
上面代碼中,標簽的type屬性給出圖像的 MIME 類型,srcset是對應的圖像 URL。
瀏覽器按照標簽出現的順序,依次檢查是否支持type屬性指定的圖像格式,如果支持就加載圖像,并且不再檢查后面的標簽了。上面例子中,圖像加載優先順序依次為 svg 格式、webp 格式和 png 格式。
使用CDN對圖片自動進行優化,我在國外的CDN提供商處很少見到這類服務,倒是國內的兩大新秀CDN七牛和又拍在這方面都做了大量工作。其工作方式為,向CDN請求圖片的URL參數中包含了圖片處理的參數(格式、寬高等),CDN服務器根據請求生成所需的圖片,發送到用戶瀏覽器。
七牛云存儲的圖片處理接口極其豐富,覆蓋了圖片的大部分基本操作,例如:
圖片裁剪,支持多種裁剪方式(如按長邊、短邊、填充、拉伸等) 圖片格式轉換,支持JPG, GIF, PNG, WebP等,支持不同的圖片壓縮率 圖片處理,支持圖片水印、高斯模糊、重心處理等
當然其他cdn對于圖像處理也有很豐富的處理,相關文檔里也介紹很詳細,可以參考cdn文檔
阿里云
騰訊
我們通過如下URL請求,裁剪正中部分,等比縮小生成200x200縮略圖:
http://qiniuphotos.qiniudn.com/gogopher.jpg?imageView2/1/w/200/h/200
七牛cdn
首先復習一下Base64的概念,Base64就是一種基于64個可打印字符來表示二進制數據的方法,編碼過程是從二進制數據到字符串的過程,在web應用中我們經常用它來做啥呢——傳輸圖片數據。HTML中,img的src和css樣式的background-image都可以接受base64字符串,從而在頁面上渲染出對應的圖片。正是基于瀏覽器的這項能力,很多開發者提出了將多張圖片轉換為base64字符串,放進css樣式文件中的“優化方式”,這樣做的目的只有一個——減少HTTP請求數。但實際上,在如今的應用開發中,這種做法大多數情況是“負優化”效果,接下來讓我們細數base64 Url的“罪狀”:
當你把圖片轉換為base64字符串之后,字符串的體積一般會比原圖更大,一般會多出接近3成的大小,如果你一個頁面中有20張平均大小為50kb的圖片,轉它們為base64后,你的css文件將可能增大1.2mb的大小,這樣將嚴重阻礙瀏覽器的關鍵渲染路徑:
css文件本身就是渲染阻塞資源,瀏覽器首次加載時如果沒有全部下載和解析完css內容就無法進行渲染樹的構建,而base64的嵌入則是雪上加霜,這將把原先瀏覽器可以進行優化的圖片異步加載,變成首屏渲染的阻塞和延遲。
或許有人會說,webpack的url-loader可以根據圖片大小決定是否轉為base64(一般是小于10kb的圖片),但你也應該擔心如果頁面中有100張小于10kb的圖片時,會給css文件增加多少體積。
假設你的base64Url會被你的應用多次復用,本來瀏覽器可以直接從本地緩存取出的圖片,換成base64Url,將造成應用中多個頁面重復下載1.3倍大小的文本,假設一張圖片是100kb大小,被你的應用使用了10次,那么造成的流量浪費將是:(100 1.3 10) - 100=1200kb。
這是比較次要的問題,dataurl在低版本IE瀏覽器,比如IE8及以下的瀏覽器,會有兼容性問題,詳細情況可以參考這篇文章。
無論哪張圖片,看上去都是一堆沒有意義的字符串,光看代碼無法知道原圖是哪張,不利于某些情況下的比對。 說了這么多 既然這種方案缺點這么多,為啥它會從以前就被廣泛使用呢?這要從早期的http協議特性說起,在http1.1之前,http協議尚未實現keep-alive,也就是每一次請求,都必須走三次握手四次揮手去建立連接,連接完又丟棄無法復用,而即使是到了http1.1的時代,keep-alive可以保證tcp的長連接,不需要多次重新建立,但由于http1.1是基于文本分割的協議,所以消息是串行的,必須有序地逐個解析,所以在這種請求“昂貴”,且早期圖片體積并不是特別大,用戶對網頁的響應速度和體驗要求也不是很高的各種前提結合下,減少圖片資源的請求數是可以理解的。
但是,在越來越多網站支持http2.0的前提下,這些都不是問題,h2是基于二進制幀的協議,在保留http1.1長連接的前提下,實現了消息的并行處理,請求和響應可以交錯甚至可以復用,多個并行請求的開銷已經大大降低,我已經不知道還有什么理由繼續堅持base64Url的使用了。
圖片優化的手段總是隨著瀏覽器特性的升級,網絡傳輸協議的升級,以及用戶對體驗要求的提升而不停地更新迭代,幾年前適用的或顯著的優化手段,幾年后不一定仍然如此。因地制宜,多管齊下,才能將其優化做到極致!
S方法:
$("body").attr('style','overflow-y:hidden') //這個是解決豎狀滾動條
//橫向 需要把overflow-y改成overflow-x即可
CSS辦法:
原始處理方法是將要展示的圖片進行處理。比如你的DIV寬度為500px(像素),那你上傳的圖片或放入網頁的圖片寬度就要小于500px,也就是你圖片需要圖片軟件剪切、等比例縮小方法處理后再上傳、放入網頁中解決撐破撐開DIV問題。
常見很多大型圖片站點、新聞站點都是將照片圖片進行處理適應網頁寬度情況下,進行圖片編輯處理的。
如果不處理照片方法適應DIV有限寬度,那可以對DIV設置隱藏超出內容方法。只需要對DIV設置寬度后加入CSS樣式“overflow:hidden”即可解決隱藏圖片比DIV過寬部分解決撐破DIV問題
三、解決方法三
對圖片img標簽中只加入寬度即可解決。這樣可以等比例縮小圖片,不會影響圖片畫面質量。
比如你的網頁DIV寬度為500px,那你上傳圖片后對img標簽設置width等于500以下即可。
<img src="圖片路徑" width="小于你的DIV寬度" />即可解決圖片過寬導致DIV SPAN撐破,這樣好處可以等比例放大縮小圖片
四、CSS解決撐破方法四
這種方法使用CSS直接對div內的img進行寬度設置,這樣不好是如果圖片過小會影響網頁瀏覽圖片時候效果。
Div結構:<div class="dc5"><img src="圖片路徑" /></div>
對應CSS代碼:.dc5 img{寬度值+單位}
五、CSS解決圖片撐破撐開DIV方法五
使用max-width(最大寬度),比如你DIV寬度為500px,那你對應DIV樣式再加入最大寬度CSS樣式“max-width="500px"”即可解決,但是在IE6瀏覽器不兼容此屬性,謹慎使用。
六、解決圖片撐破DIV層方法總結與推薦
1)、最大寬度(max-width)+overflow:hidden。我們這樣設置可以讓IE6版本以上瀏覽器支持最大寬度樣式,也讓IE6下隱藏圖片超出寬度而撐開DIV得到解決,此方法比較方便和實用。
2)、只使用overflow:hidden屬性,如方法二
3)、圖片使用上傳時候軟件處理下,以適應DIV布局寬度,如方法一
以上為推薦解決IMG圖片撐破有限DIV寬度方法,根據實際情況大家可以任意選擇適合自己解決網頁中圖片撐破DIV層方法。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。