原創(chuàng)文章,未經(jīng)允許,嚴(yán)禁轉(zhuǎn)載
還記得 圖片延遲加載方案 那篇博文嗎?當(dāng)初分析了定寬高值
和定寬高比
這兩種常見的圖片延遲加載場景,也介紹了他們的應(yīng)對方案,還做了一點(diǎn)技術(shù)選型的工作。
經(jīng)過一段時(shí)間的項(xiàng)目實(shí)踐,在先前方案的基礎(chǔ)上又做了很多深入的優(yōu)化工作。最終將好奇心日報(bào)的網(wǎng)頁打開速度將降低到了1s內(nèi),Web端和Mobile端加載3屏數(shù)據(jù)消耗的流量也大幅降低。
該篇文章結(jié)合具體的項(xiàng)目實(shí)踐,將圍繞如何更快的訪問網(wǎng)頁展開,細(xì)化到具體的技術(shù)方案,以及實(shí)踐中可能遇到的坑,希望對大家有一定的啟發(fā)和幫助。
為什么要優(yōu)化網(wǎng)頁加載速度?
好奇心日報(bào)無論是設(shè)計(jì)還是內(nèi)容都追求高品質(zhì),于是豐富的圖文混合成了標(biāo)配:首頁的banner圖,文章詳情頁的配圖,研究所有趣的gif圖等等。
特別嚴(yán)重的時(shí)候,一篇文章有十多個(gè)gif圖,加載花費(fèi)的時(shí)間10-20秒之長,加載消耗的流量幾十M之多,嚴(yán)重影響了用戶體驗(yàn)!尤其是Mobile端,一寸流量一寸金;3-5s打不開頁面,用戶都會(huì)直接逃離。所以網(wǎng)頁加載速度優(yōu)化勢在必行!
我們都知道一個(gè)網(wǎng)頁的加載流程大致如下:
1、解析HTML結(jié)構(gòu)。
2、加載外部腳本和樣式表文件。
3、解析并執(zhí)行腳本代碼。// 部分腳本會(huì)阻塞頁面的加載
4、DOM樹構(gòu)建完成。//DOMContentLoaded 事件
5、加載圖片等外部文件。
6、頁面加載完畢。//load 事件
一句話就是:請求HTML,然后順帶將HTML依賴的JS/CSS/iconfont等其他資源一并請求過來。
那么優(yōu)化網(wǎng)頁的加載速度,最本質(zhì)的方式就是:減少請求數(shù)量 與 減小請求大小。
減少請求數(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按需加載
是兩個(gè)不同的概念:
JS/CSS按需打包
是預(yù)編譯發(fā)生的事情,保證只打包當(dāng)前頁面相關(guān)的邏輯。
JS/CSS按需加載
是運(yùn)行時(shí)發(fā)生的事情,保證只加載當(dāng)前頁面第一時(shí)間使用到的邏輯。
接下來我們將結(jié)合兩個(gè)本質(zhì)的優(yōu)化方式介紹具體的實(shí)踐方法。
如何減少請求數(shù)量?
1、合并圖標(biāo),減少網(wǎng)絡(luò)請求
合并圖標(biāo)是減少網(wǎng)絡(luò)請求的常見的優(yōu)化手段,網(wǎng)頁中的小圖標(biāo)特征是體積小、數(shù)量多,而瀏覽器同時(shí)發(fā)起的并行請求數(shù)量又是有限制的,所以這些小圖標(biāo)會(huì)嚴(yán)重的影響網(wǎng)頁的加載速度,阻礙關(guān)鍵內(nèi)容的請求和呈現(xiàn)
sprite圖
合并sprite圖是慢工細(xì)活兒,并沒有特別大的技術(shù)含量,但卻是每個(gè)前端開發(fā)都必須掌握的技術(shù)。
剛?cè)腴T的前端直接手動(dòng)切圖拼圖即可。
經(jīng)驗(yàn)豐富的前端可以嘗試?yán)脴?gòu)建工具實(shí)現(xiàn)自動(dòng)化,推薦使用。gulp.spritesmith插件。
// 構(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)式場景,所以越來越作為一個(gè)備用方案。
iconfont字體文件
iconfont字體文件是用字體編碼的形式來實(shí)現(xiàn)圖標(biāo)效果,既然是文字,那就可以隨意設(shè)置顏色設(shè)置大小,相對來說比sprite方案更好。但是它只適用于純色圖標(biāo)。推薦使用 阿里巴巴矢量圖標(biāo)庫
2、用base64減少不必要的網(wǎng)絡(luò)請求
上文提到的sprite圖和iconfont字體文件,對于有些場景并不適合,比如:
1、小背景圖,無法放到精靈圖中,通常循環(huán)平鋪小塊來設(shè)置大背景。
2、小gif圖,無法放到精靈圖中,發(fā)請求又太浪費(fèi)。
注意:cssnano壓縮css的時(shí)候,對于部分規(guī)則的base64 uri不能識(shí)別,會(huì)出現(xiàn)誤傷,如下圖,cssnano壓縮的時(shí)候會(huì)將//
壓縮為/
:
原因是:cssnano會(huì)跳過data:image
/data:application
后面的字符串,但是不會(huì)跳過data:img
,所以如果你使用的工具生成的是data:img
,建議換工具或者直接將其修改為data:image
。
3、圖片延遲加載
圖片是網(wǎng)頁中流量占比最多的部分,也是需要重點(diǎn)優(yōu)化的部分。
圖片延遲加載的原理就是先不設(shè)置img的src屬性,等合適的時(shí)機(jī)(比如滾動(dòng)、滑動(dòng)、出現(xiàn)在視窗內(nèi)等)再把圖片真實(shí)url放到img的src屬性上。更多內(nèi)容請移步上一篇博文: 圖片延遲加載方案
固定寬高值的圖片
固定寬高值的圖片延遲加載比較簡單,因?yàn)閷捀咧刀伎梢栽O(shè)置在css中,只需考慮src的替換問題,推薦使用lazysizes。
// 引入js文件
<script src="lazysizes.min.js" async=""></script>
// 非響應(yīng)式 例子
<img src="" data-src="image.jpg" class="lazyload" />
// 響應(yīng)式 例子,自動(dòng)計(jì)算合適的圖片
<img
data-sizes="auto"
data-src="image2.jpg"
data-srcset="image1.jpg 300w,
image2.jpg 600w,
image3.jpg 900w" class="lazyload" />
// iframe 例子
<iframe frameborder="0"
class="lazyload"
allowfullscreen=""
data-src="http://www.youtube.com/embed/ZfV-aYdU4uE">
</iframe>
注意:瀏覽器解析img標(biāo)簽的時(shí)候,如果src屬性為空,瀏覽器會(huì)認(rèn)為這個(gè)圖片是壞掉的圖,會(huì)顯示出圖片的邊框,影響市容。
lazysizes延遲加載過程中會(huì)改變圖片的class:默認(rèn)lazyload,加載中l(wèi)azyloading,加載結(jié)束:lazyloaded。結(jié)合這個(gè)特性我們有兩種解決上述問題辦法:
1、設(shè)置opacity:0,然后在顯示的時(shí)候設(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的透明圖或者灰圖。
<img class="lazyload"
src="data:image/gif;base64,R0lGODlhAQABAAA
AACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
data-src="真實(shí)url"
alt="<%= article.title %>">
此外,為了讓效果更佳,尤其是文章詳情頁中的大圖,我們可以加上loading效果。
.article-detail-bd {
.lazyload {
opacity: 0;
}
.lazyloading {
opacity: 1;
background: #f7f7f7 url(/images/loading.gif) no-repeat center;
}
}
固定寬高比的圖片
固定寬高比的圖片延遲加載相對來說復(fù)雜很多,比如文章詳情頁的圖片,由于設(shè)備的寬度值不確定,所以高度值也不確定,這時(shí)候工作的重心反倒放到了如何確定圖片的高度上。
為什么要確定圖片的高度呢?因?yàn)閱蝹€(gè)圖片的加載是從上往下,所以會(huì)導(dǎo)致頁面抖動(dòng),不僅用戶體驗(yàn)很差,而且對于性能消耗很大,因?yàn)槊看味秳?dòng)都會(huì)觸發(fā)reflow(重繪)事件,之前的博文 網(wǎng)站性能優(yōu)化 之 渲染性能 也分析過重繪對于性能的消耗問題。
固定寬高比的圖片抖動(dòng)問題,有下列兩種主流的方式可以解決:
1、第一種方案使用padding-top或者padding-bottom來實(shí)現(xiàn)固定寬高比。優(yōu)點(diǎn)是純CSS方案,缺點(diǎn)是HTML冗余,并且對輸出到第三方不友好。
<div style="padding-top:75%">
<img data-src="" alt="" class="lazyload">
<div>
2、第二種方案在頁面初始化階段利用ratio設(shè)置實(shí)際寬高值,優(yōu)點(diǎn)是html干凈,對輸出到第三方友好,缺點(diǎn)是依賴js,理論上會(huì)至少抖動(dòng)一次。
<img data-src="" alt="" class="lazyload" data-ratio="0.75">
那么,這個(gè)padding-top: 75%;
和data-ratio="0.75"
的數(shù)據(jù)從哪兒來呢?在你上傳圖片的時(shí)候,需要后臺(tái)給你返回原始寬高值,計(jì)算得到寬高比,然后保存到data-ratio上。
好奇心日報(bào)采用的第二種方案,主要在于第一種方案對第三方輸出不友好:需要對img設(shè)置額外的樣式,但第三方平臺(tái)通常不允許引入外部樣式。
確定第二種方案之后,我們定義了一個(gè)設(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中,就為了一個(gè)目的,第一時(shí)間計(jì)算圖片的高度值,降低用戶感知到頁面抖動(dòng)的可能性,保證最佳效果。
// 原生代碼
<img alt=""
data-ratio="0.562500"
data-format="jpeg"
class="lazyload"
data-src="http://img.qdaily.com/uploads/20160807124000WFJNyGam85slTC4H.jpg"
src="">
// 解析之后的代碼
<img alt=""
data-ratio="0.562500"
data-format="jpeg"
class="lazyloaded"
data-src="http://img.qdaily.com/uploads/20160807124000WFJNyGam85slTC4H.jpg"
src="http://img.qdaily.com/uploads/20160807124000WFJNyGam85slTC4H.jpg"
style="height: 323.438px;">
我們不僅保存了寬高比,還保存了圖片格式,是為了后期可以對gif做進(jìn)一步的優(yōu)化。
注意事項(xiàng)
1、避免圖片過早加載,把臨界值調(diào)低一點(diǎn)。在實(shí)際項(xiàng)目中,并不需要過早就把圖片請求過來,尤其是Mobile項(xiàng)目,過早請求不僅浪費(fèi)流量,也會(huì)因?yàn)檎埱筇啵瑢?dǎo)致頁面加載速度變慢。
2、為了最好的防抖效果,設(shè)置圖片高度的JS代碼內(nèi)嵌到HTML中以便第一時(shí)間執(zhí)行。
3、根據(jù)圖片寬度設(shè)置高度時(shí),使用clientWidth而不是width。這是因?yàn)镾afari中,第一時(shí)間執(zhí)行的JS代碼獲取圖片的width失敗,所以使用clientWidth解決這個(gè)問題。
4、JS/CSS按需打包
按需打包是webpack獨(dú)特的優(yōu)勢,如果有需要通過此種方式來管理模塊之間的依賴關(guān)系,強(qiáng)烈推薦引入!webpack門檻較高,可以看看我之前的博客:
webpack 入門
webpack 模塊化機(jī)制
好奇心日報(bào)是典型的多頁應(yīng)用,為了緩存通用代碼,我們使用webpack按需打包的同時(shí),還利用webpack的CommonsChunkPlugin 插件
抽離出公用的JS/CSS代碼,便于緩存,在請求數(shù)量和公用代碼的緩存之間做了一個(gè)很好的平衡。
5、延遲加載ga統(tǒng)計(jì)
async & defer屬性
html5中給script標(biāo)簽引入了async和defer屬性。
帶有async屬性的script標(biāo)簽,會(huì)在瀏覽器解析時(shí)立即下載腳本同時(shí)不阻塞后續(xù)的document渲染和script加載等事件,從而實(shí)現(xiàn)腳本的異步加載。
帶有defer屬性的script標(biāo)簽,和async擁有類似的功能。并且他們有可以附帶一個(gè)onload事件<script src="" defer onload="init()">
。
async和defer的區(qū)別在于:async屬性會(huì)在腳本下載完成后無序立即執(zhí)行,defer屬性會(huì)在腳本下載完成后按照document結(jié)構(gòu)順序執(zhí)行。
由于defer和async的兼容性問題,我們通常使用動(dòng)態(tài)創(chuàng)建script標(biāo)簽
的方式來實(shí)現(xiàn)異步加載腳本,即document.write('<script src="" async></script>');
,該方式也可以避免阻塞。
ga統(tǒng)計(jì)代碼
ga統(tǒng)計(jì)代碼采用就是動(dòng)態(tài)創(chuàng)建script標(biāo)簽
方案。
該方法不阻塞頁面渲染,不阻塞后續(xù)請求,但會(huì)阻塞window.onload事件,頁面的表現(xiàn)方式是進(jìn)度條一直加載或loading菊花一直轉(zhuǎn)。
所以我們延遲執(zhí)行g(shù)a初始化代碼,將其放到window.onload函數(shù)中去執(zhí)行,可以防止ga腳本阻塞window.onload事件。從而讓用戶感受到更快的加載速度。
如何減小請求大小?
1、JS/CSS/HTML壓縮
這也是常規(guī)手段,就不介紹太多,主要的方式有:
1、通過構(gòu)建工具實(shí)現(xiàn),比如webpack/gulp/fis/grunt等。
2、后臺(tái)預(yù)編譯。
3、利用第三方online平臺(tái),手動(dòng)上傳壓縮。
無論是第二種還是第三種方式,都有其局限性,第一種方法是目前的主流方式,憑借良好的插件生態(tài),可以實(shí)現(xiàn)豐富的構(gòu)建任務(wù)。
在好奇心日報(bào)的項(xiàng)目中,我們使用webpack & gulp作為構(gòu)建系統(tǒng)的基礎(chǔ)。
簡單介紹一下JS/CSS/HTML壓縮方式和一些注意事項(xiàng)
JS壓縮
JS壓縮
:使用webpack的UglifyJsPlugin
插件,同時(shí)做一些代碼檢測。
new webpack.optimize.UglifyJsPlugin({
mangle: {
except: ['$super', '$', 'exports', 'require']
}
})
CSS壓縮
CSS壓縮
:使用cssnano壓縮,同時(shí)使用postcss做一些自動(dòng)化操作,比如自動(dòng)加前綴、屬性fallback支持、語法檢測等。
var postcss = [
cssnano({
autoprefixer: false,
reduceIdents: false,
zindex: false,
discardUnused: false,
mergeIdents: false
}),
autoprefixer({ browers: ['last 2 versions', 'ie >= 9', '> 5% in CN'] }),
will_change,
color_rgba_fallback,
opacity,
pseudoelements,
sorting
];
HTML壓縮
HTML壓縮
:使用htmlmin壓縮HTML,同時(shí)對不規(guī)范的HTML寫法糾正。
// 構(gòu)建視圖文件-build版本
gulp.task('build:views', ['clean:views'], function() {
return streamqueue({ objectMode: true },
gulp.src(config.commonSrc, { base: 'src' }),
gulp.src(config.layoutsSrc, { base: 'src' }),
gulp.src(config.pagesSrc, { base: 'src/pages' }),
gulp.src(config.componentsSrc, { base: 'src' })
)
.pipe(plumber(handleErrors))
.pipe(logger({ showChange: true }))
.pipe(preprocess({ context: { PROJECT: project } }))
.pipe(gulpif(function(file) {
if (file.path.indexOf('.html') != -1) {
return true;
} else {
return false;
}
}, htmlmin({
removeComments: true,
collapseWhitespace: true,
minifyJS: true,
minifyCSS: true,
ignoreCustomFragments: [/<%[\s\S]*?%>/,
/<\?[\s\S]*?\?>/,
/<meta[\s\S]*?name="viewport"[\s\S]*?>/]
})))
.pipe(gulp.dest(config.dest));
});
某個(gè)第三方平臺(tái)要求<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0, user-scalable=no">
必須寫成小數(shù)點(diǎn)格式,而htmlmin默認(rèn)會(huì)將小數(shù)格式化為整數(shù),所以額外添加了排除項(xiàng):/<meta[\s\S]*?name="viewport"[\s\S]*?>/
。到現(xiàn)在都沒懂這個(gè)第三方平臺(tái)咋想的!
條件編譯
由于好奇心日報(bào)項(xiàng)目較多,我們費(fèi)了很大的心思抽離出前端項(xiàng)目,實(shí)現(xiàn)了前后分離。但有些場景下,我們?yōu)榱藢⑾嚓P(guān)代碼維護(hù)在一個(gè)文件中,同時(shí)又針對不同項(xiàng)目執(zhí)行不同的邏輯,這時(shí)候,強(qiáng)烈推薦使用 gulp-preprocess插件 來實(shí)現(xiàn)條件編譯。
2、gzip壓縮
gzip壓縮也是比較常規(guī)的優(yōu)化手段。前端并不需要做什么實(shí)際的工作,后臺(tái)配置下服務(wù)器就行,效果非常明顯。如果你發(fā)現(xiàn)你的網(wǎng)站還沒有配置gzip,那么趕緊行動(dòng)起來吧。
gzip壓縮原理
如果瀏覽器支持gzip壓縮,在發(fā)送請求的時(shí)候,請求頭中會(huì)帶有Accept-Encoding:gzip
。然后服務(wù)器會(huì)將原始的response
進(jìn)行g(shù)zip壓縮,并將gzip壓縮后的response
傳輸?shù)綖g覽器,緊接著瀏覽器進(jìn)行g(shù)zip解壓縮,并最終反饋到網(wǎng)頁上。
gzip壓縮效果
那么gzip壓縮的效果有多明顯呢?保守估計(jì),在已經(jīng)完成JS/CSS/HTML壓縮的基礎(chǔ)上,還能降低60-80%左右的大小。
但需要注意,gzip壓縮會(huì)消耗服務(wù)器的性能,不能過度壓縮。
所以推薦只對JS/CSS/HTML等資源做gzip壓縮。圖片的話,托管到第三方的圖片建議開啟gzip壓縮,托管到自己應(yīng)用服務(wù)器的圖片不建議開啟gzip壓縮。
3、JS/CSS按需加載
和前面提到的按需打包不同。
JS/CSS按需打包
是預(yù)編譯發(fā)生的事情,保證只打包當(dāng)前頁面相關(guān)的邏輯。
JS/CSS按需加載
是運(yùn)行時(shí)發(fā)生的事情,保證只加載當(dāng)前頁面第一時(shí)間使用到的邏輯。
那么怎么實(shí)現(xiàn)按需加載呢?好奇心日報(bào)使用webpack提供的require
及require.ensure
方法來實(shí)現(xiàn)按需加載,值得一提的是,除了指定的按需加載文件列表,webpack還會(huì)自動(dòng)解析回調(diào)函數(shù)的依賴及指定列表的深層次依賴,并最終打包成一個(gè)文件。
上訴代碼的實(shí)現(xiàn)效果是:只有當(dāng)點(diǎn)擊登錄按鈕的時(shí)候,才會(huì)去加載登錄相關(guān)的JS/CSS資源。資源在加載成功后自動(dòng)執(zhí)行。
4、圖片壓縮,jpg優(yōu)化
托管到應(yīng)用服務(wù)器的圖片壓縮
可以手動(dòng)處理,也可以通過gulp子任務(wù)來處理。
手動(dòng)處理的話,推薦一個(gè)網(wǎng)站 tinypng ,雖然是有損壓縮,但壓縮效果極好。
gulp子任務(wù)處理的話,推薦使用gulp-imagemin插件,自動(dòng)化處理,效果也還不錯(cuò)。
// 圖片壓縮
gulp.task('images', function() {
return gulp.src(config.src)
.pipe(plumber(handleErrors))
.pipe(newer(config.dest))
.pipe(logger({ showChange: true }))
.pipe(imagemin()) // 壓縮
.pipe(gulp.dest(config.dest));
});
托管到第三方平臺(tái)的圖片壓縮
比如七牛云平臺(tái),他們會(huì)有一套專門的方案來對圖片壓縮,格式轉(zhuǎn)換,裁剪等。只需要在url后面加上對應(yīng)的參數(shù)即可,雖然偶爾會(huì)有一些小bug,但整體來說,托管方案比用自家應(yīng)用服務(wù)器方案更優(yōu)。
jpg優(yōu)化
除了對圖片進(jìn)行壓縮之外,對透明圖床沒有要求的場景,強(qiáng)烈建議將png轉(zhuǎn)換為jpg,效果很明顯!
如下圖,將png格式化為jpg格式,圖片相差差不多8倍!
再次強(qiáng)調(diào),可以轉(zhuǎn)換成jpg的圖片,強(qiáng)烈建議轉(zhuǎn)換成jpg!
5、webp優(yōu)化 & srcset優(yōu)化
webp優(yōu)化
粗略看一眼,臥槽,兼容性這么差,也就安卓瀏覽器及chrome瀏覽器對它的支持還算給力。
另一方面,webp優(yōu)化能在jpg的基礎(chǔ)上再降低近50%的大小。其優(yōu)化效果明顯。此外,如果瀏覽器支持webpanimation,還能對gif做壓縮!
兼容性差,但效果好!最終好奇心決定嘗試一下。
1、判斷瀏覽器對webp及webpanimation的兼容性。
2、如果瀏覽器支持webp及webpanimation,將其替換成webp格式的圖片。
鑒于瀏覽器對webp的支持比較局限,我們采用漸進(jìn)升級的方式來優(yōu)化:對于不支持webp的瀏覽器,不做處理;對于支持webp的瀏覽器,將圖片src替換成webp格式。
那么如何判斷webp兼容性呢?
// 檢測瀏覽器是否支持webp
// 之所以沒寫成回調(diào),是因?yàn)榧词筰sSupportWebp=false也無大礙,但卻可以讓代碼更容易維護(hù)
(function() {
function webpTest(src, name) {
var img = new Image(),
isSupport = false,
className, cls;
img.onload = function() {
isSupport = !!(img.height > 0 && img.width > 0);
cls = isSupport ? (' ' + name) : (' no-' + name);
className = document.querySelector('html').className
className += cls;
document.querySelector('html').className = className.trim();
};
img.onerror = function() {
cls = (' no-' + name);
className = document.querySelector('html').className
className += cls;
document.querySelector('html').className = className.trim();
};
img.src = src;
}
var webpSrc = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoB\
AAEAAwA0JaQAA3AA/vuUAAA=',
webpanimationSrc = 'data:image/webp;base64,UklGRlIAAABXRUJQVlA4WAoAAAA\
SAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAA\
AAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA';
webpTest(webpSrc, 'webp');
webpTest(webpanimationSrc, 'webpanimation');
})();
借鑒modernizr,實(shí)現(xiàn)了檢測webp/webpanimation兼容性的函數(shù),從代碼中可以看出,檢測原理就是模擬下載對應(yīng)格式的圖片,在異步函數(shù)中可以得到兼容性結(jié)果。
接下來就是替換url為webp格式
// 獲取webp格式的src
function _getWebpSrc(src) {
var dpr = Math.round(window.devicePixelRatio || 1),
ratio = [1, 1, 1.5, 2, 2, 2],
elHtml = document.querySelector('html'),
isSupportWebp = (/(^|\s)webp(\s|$)/i).test(elHtml.className),
isSupportWebpAnimation = (/(^|\s)webpanimation(\s|$)/i).test(elHtml.className),
deviceWidth = elHtml.clientWidth,
isQiniuSrc = /img\.qdaily\.com\//.test(src),
format = _getFormat(src),
isGifWebp, isNotGifWebp, regDetailImg;
if (!src || !isQiniuSrc || !format || format == 'webp') {
return src;
}
isNotGifWebp = (format != 'gif' && isSupportWebp);
isGifWebp = (format == 'gif' && isSupportWebpAnimation);
// 根據(jù)屏幕分辨率計(jì)算大小
src = src.replace(/\/(thumbnail|crop)\/.*?(\d+)x(\d+)[^\/]*\//ig, function(match, p0, p1, p2) {
if(dpr > 1){
p1 = Math.round(p1 * ratio[dpr]);
p2 = Math.round(p2 * ratio[dpr]);
match = match.replace(/\d+x\d+/, p1 + 'x' + p2)
}
return match;
});
if(isNotGifWebp || isGifWebp) {
// 替換webp格式,首頁/列表頁
src = src.replace(/\/format\/([^\/]*)/ig, function(match, p1) {
return '/format/webp';
});
}
}
注意事項(xiàng)
1、window的屏幕像素密度不一定是整數(shù),mac瀏覽器縮放之后,屏幕像素密度也不是整數(shù)。所以獲取dpr一定要取整:dpr = Math.round(window.devicePixelRatio || 1);
。
2、ratio = [1, 1, 1.5, 2, 2, 2]
表示:1倍屏使用1倍圖,2倍屏使用1.5倍圖,3倍屏以上都用2倍圖。這兒的規(guī)則可以按實(shí)際情況來設(shè)置。
3、webp優(yōu)化更適合托管到第三方的圖片,簡單修改參數(shù)就可以獲取不同的圖片。
srcset兼容性
如上所述,在對webp優(yōu)化的時(shí)候,我們順道模擬實(shí)現(xiàn)了srcset:根據(jù)屏幕像素密度來設(shè)置最適合的圖片寬高。
lazysizes原本提供了srcset選項(xiàng),也可以借用lazysizes的方案來實(shí)現(xiàn)srcset,有興趣的可以去看看源碼。
又到總結(jié)的時(shí)候了?
本博客圍繞好奇心日報(bào)的具體實(shí)踐,在優(yōu)化頁面加載速度方面的做了一系列思考。整體來說,涉及的知識(shí)面比較廣:包括webpack & gulp的構(gòu)建系統(tǒng)、圖片的webp優(yōu)化、服務(wù)器的gzip配置、瀏覽器的加載順序、圖片延遲加載方案等等。
文中提到的gulp子任務(wù),后續(xù)也會(huì)有一系列好奇心日報(bào)項(xiàng)目的相關(guān)實(shí)踐,會(huì)覆蓋gulp子任務(wù)的設(shè)計(jì)思路,構(gòu)建系統(tǒng)的架構(gòu),以及具體子任務(wù)的剖析和講解,敬請關(guān)注。
如果該博文對你有一些幫助,請點(diǎn)擊喜歡支持一下,也歡迎在評論區(qū)留下你的建議和討論。
原創(chuàng)文章,未經(jīng)允許,嚴(yán)禁轉(zhuǎn)載