減少請求數(shù)量
網(wǎng)站建設(shè)無論是設(shè)計(jì)還是內(nèi)容都追求高品質(zhì),于是豐富的圖文混合成了標(biāo)配:首頁的banner圖,文章詳情頁的配圖,研究所有趣的gif圖等等。南寧網(wǎng)站建設(shè)
特別嚴(yán)重的時候,一篇文章有十多個gif圖,加載花費(fèi)的時間10-20秒之長,加載消耗的流量幾十M之多,嚴(yán)重影響了用戶體驗(yàn)!尤其是Mobile端,一寸流量一寸金;3-5s打不開頁面,用戶都會直接逃離。所以網(wǎng)頁加載速度優(yōu)化勢在必行!
我們都知道一個網(wǎng)頁的加載流程大致如下:
1、解析HTML結(jié)構(gòu)。
2、加載外部腳本和樣式表文件。
3、解析并執(zhí)行腳本代碼。// 部分腳本會阻塞頁面的加載
4、DOM樹構(gòu)建完成。//DOMContentLoaded 事件
5、加載圖片等外部文件。
6、頁面加載完畢。//load 事件
一句話就是:請求HTML,然后順帶將HTML依賴的JS/CSS/iconfont等其他資源一并請求過來。
那么優(yōu)化網(wǎng)頁的加載速度,最本質(zhì)的方式就是:減少請求數(shù)量與減小請求大小。
1、將小圖標(biāo)合并成sprite圖或者iconfont字體文件
2、用base64減少不必要的網(wǎng)絡(luò)請求
3、圖片延遲加載
4、JS/CSS按需打包
5、延遲加載ga統(tǒng)計(jì)
6、等等...
減小請求大小
1、JS/CSS/HTML壓縮
2、gzip壓縮
3、JS/CSS按需加載
4、圖片壓縮,jpg優(yōu)化
5、webp優(yōu)化 & srcset優(yōu)化
6、等等...
JS/CSS按需打包
和JS/CSS按需加載
是兩個不同的概念:JS/CSS按需打包
是預(yù)編譯發(fā)生的事情,保證只打包當(dāng)前頁面相關(guān)的邏輯。JS/CSS按需加載
是運(yùn)行時發(fā)生的事情,保證只加載當(dāng)前頁面第一時間使用到的邏輯。
接下來我們將結(jié)合兩個本質(zhì)的優(yōu)化方式介紹具體的實(shí)踐方法。
如何減少請求數(shù)量?
1、合并圖標(biāo),減少網(wǎng)絡(luò)請求
合并圖標(biāo)是減少網(wǎng)絡(luò)請求的常見的優(yōu)化手段,網(wǎng)頁中的小圖標(biāo)特征是體積小、數(shù)量多,而瀏覽器同時發(fā)起的并行請求數(shù)量又是有限制的,所以這些小圖標(biāo)會嚴(yán)重的影響網(wǎng)頁的加載速度,阻礙關(guān)鍵內(nèi)容的請求和呈現(xiàn)。
sprite圖南寧網(wǎng)站開發(fā)
合并sprite圖是慢工細(xì)活兒,并沒有特別大的技術(shù)含量,但卻是每個前端開發(fā)都必須掌握的技術(shù)。
剛?cè)腴T的前端直接手動切圖拼圖即可。
經(jīng)驗(yàn)豐富的前端可以嘗試?yán)脴?gòu)建工具實(shí)現(xiàn)自動化,
// 構(gòu)建視圖文件gulp.task('sprites', function() { var spriteData = gulp.src(config.src) .pipe(plumber(handleErrors)) .pipe(newer(config.imgDest)) .pipe(logger({ showChange: true })) .pipe(spritesmith({ cssName: 'sprites.css', imgName: 'sprites.png', cssTemplate: path.resolve('./gulp/lib/template.css.handlebars') })); var imgStream = spriteData.img .pipe(buffer()) .pipe(gulp.dest(config.imgDest)); var cssStream = spriteData.css .pipe(gulp.dest(config.cssDest)); return merge([imgStream, cssStream]); });
sprite圖不適合無線端的響應(yīng)式場景,所以越來越作為一個備用方案。
iconfont字體文件
iconfont字體文件是用字體編碼的形式來實(shí)現(xiàn)圖標(biāo)效果,既然是文字,那就可以隨意設(shè)置顏色設(shè)置大小,相對來說比sprite方案更好。但是它只適用于純色圖標(biāo)。
2、用base64減少不必要的網(wǎng)絡(luò)請求
base64碼兼容性
上文提到的sprite圖和iconfont字體文件,對于有些場景并不適合,比如:
1、小背景圖,無法放到精靈圖中,通常循環(huán)平鋪小塊來設(shè)置大背景。
2、小gif圖,無法放到精靈圖中,發(fā)請求又太浪費(fèi)。
base64使用場景
注意:cssnano壓縮css的時候,對于部分規(guī)則的base64 uri不能識別,會出現(xiàn)誤傷,如下圖,cssnano壓縮的時候會將//
壓縮為/
:
cssnano壓縮base64
原因是:cssnano會跳過data:image
/data:application
后面的字符串,但是不會跳過data:img
,所以如果你使用的工具生成的是data:img
,建議換工具或者直接將其修改為data:image
。
3、圖片延遲加載
圖片是網(wǎng)頁中流量占比最多的部分,也是需要重點(diǎn)優(yōu)化的部分。
圖片延遲加載的原理就是先不設(shè)置img的src屬性,等合適的時機(jī)(比如滾動、滑動、出現(xiàn)在視窗內(nèi)等)再把圖片真實(shí)url放到img的src屬性上。
固定寬高值的圖片
固定寬高值的圖片延遲加載比較簡單,因?yàn)閷捀咧刀伎梢栽O(shè)置在css中,只需考慮src的替換問題。
注意:瀏覽器解析img標(biāo)簽的時候,如果src屬性為空,瀏覽器會認(rèn)為這個圖片是壞掉的圖,會顯示出圖片的邊框,影響市容。
第一塊是初始狀態(tài),第四塊是成功狀態(tài),第二塊第三塊是影響市容的狀態(tài)
lazysizes延遲加載過程中會改變圖片的class:默認(rèn)lazyload,加載中l(wèi)azyloading,加載結(jié)束:lazyloaded。結(jié)合這個特性我們有兩種解決上述問題辦法:
1、設(shè)置opacity:0,然后在顯示的時候設(shè)置opacity:1
// 漸現(xiàn) lazyload.lazyload, .lazyloading{ opacity: 0; } .lazyloaded{ opacity: 1; transition: opacity 500ms; //加上transition就可以實(shí)現(xiàn)漸現(xiàn)的效果}
2、用一張默認(rèn)的圖占位,比如1x1的透明圖或者灰圖
此外,為了讓效果更佳,尤其是文章詳情頁中的大圖,我們可以加上loading效果。
.article-detail-bd { .lazyload { opacity: 0; } .lazyloading { opacity: 1; background: #f7f7f7 url(/images/loading.gif) no-repeat center; } }
4、固定寬高比的圖片
固定寬高比的圖片延遲加載相對來說復(fù)雜很多,比如文章詳情頁的圖片,由于設(shè)備的寬度值不確定,所以高度值也不確定,這時候工作的重心反倒放到了如何確定圖片的高度上。南寧微信開發(fā)
為什么要確定圖片的高度呢?因?yàn)閱蝹€圖片的加載是從上往下,所以會導(dǎo)致頁面抖動,不僅用戶體驗(yàn)很差,而且對于性能消耗很大。
固定寬高比的圖片抖動問題,有下列兩種主流的方式可以解決:
1、第一種方案使用padding-top或者padding-bottom來實(shí)現(xiàn)固定寬高比。優(yōu)點(diǎn)是純CSS方案,缺點(diǎn)是HTML冗余,并且對輸出到第三方不友好。
2、第二種方案在頁面初始化階段利用ratio設(shè)置實(shí)際寬高值,優(yōu)點(diǎn)是html干凈,對輸出到第三方友好,缺點(diǎn)是依賴js,理論上會至少抖動一次。
那么,這個padding-top: 75%;
和data-ratio="0.75"
的數(shù)據(jù)從哪兒來呢?在你上傳圖片的時候,需要后臺給你返回原始寬高值,計(jì)算得到寬高比,然后保存到data-ratio上。
采用的第二種方案,主要在于第一種方案對第三方輸出不友好:需要對img設(shè)置額外的樣式,但第三方平臺通常不允許引入外部樣式。
確定第二種方案之后,我們定義了一個設(shè)置圖片高度的函數(shù):
// 重置圖片高度,僅限文章詳情頁function resetImgHeight(els, placeholder) { var ratio = 0, i, len, width; for (i = 0, len = els.length; i < len; i++) { els[i].src = placeholder; width = els[i].clientWidth; //一定要使用clientWidth if (els[i].attributes['data-ratio']) { ratio = els[i].attributes['data-ratio'].value || 0; ratio = parseFloat(ratio); } if (ratio) { els[i].style.height = (width * ratio) + 'px'; } } }
我們將以上代碼的定義和調(diào)用都直接放到了HTML中,就為了一個目的,第一時間計(jì)算圖片的高度值,降低用戶感知到頁面抖動的可能性,保證最佳效果。
// 原生代碼// 解析之后的代碼
我們不僅保存了寬高比,還保存了圖片格式,是為了后期可以對gif做進(jìn)一步的優(yōu)化。
注意事項(xiàng)
1、避免圖片過早加載
把臨界值調(diào)低一點(diǎn)。在實(shí)際項(xiàng)目中,并不需要過早就把圖片請求過來,尤其是Mobile項(xiàng)目,過早請求不僅浪費(fèi)流量,也會因?yàn)檎埱筇?,?dǎo)致頁面加載速度變慢。南寧網(wǎng)站建設(shè)
2、為了最好的防抖效果
設(shè)置圖片高度的JS代碼內(nèi)嵌到HTML中以便第一時間執(zhí)行。
3、根據(jù)圖片寬度設(shè)置高度
使用clientWidth而不是width。這是因?yàn)镾afari中,第一時間執(zhí)行的JS代碼獲取圖片的width失敗,所以使用clientWidth解決這個問題。
4、JS/CSS按需打包
好奇心日報是典型的多頁應(yīng)用,為了緩存通用代碼,我們使用webpack按需打包的同時,還利用webpack的CommonsChunkPlugin 插件
抽離出公用的JS/CSS代碼,便于緩存,在請求數(shù)量和公用代碼的緩存之間做了一個很好的平衡。
5、延遲加載ga統(tǒng)計(jì)
async & defer屬性
html5中給script標(biāo)簽引入了async和defer屬性。
帶有async屬性的script標(biāo)簽,會在瀏覽器解析時立即下載腳本同時不阻塞后續(xù)的document渲染和script加載等事件,從而實(shí)現(xiàn)腳本的異步加載。
帶有defer屬性的script標(biāo)簽,和async擁有類似的功能。并且他們有可以附帶一個onload事件