首先,咱得明白什么是“首屏加載”時(shí)間。
答:用戶(hù)能夠看到第一屏區(qū)域內(nèi)所有元素加載完的時(shí)間就是“首屏加載”時(shí)間。一個(gè)頁(yè)面的“總加載時(shí)間”(onload)一定大于等于“首屏加載”時(shí)長(zhǎng)。
通常需要考慮首屏?xí)r間的頁(yè)面,都是因?yàn)樵谑灼廖恢脙?nèi)放入了較多的圖片資源。
而圖片資源處理是異步的,會(huì)先將圖片長(zhǎng)寬應(yīng)用于頁(yè)面排版,然后隨著收到圖片數(shù)據(jù)由上至下繪制顯示的。并且瀏覽器對(duì)每個(gè)頁(yè)面的TCP連接數(shù)限制,使得并不是所有圖片都能立刻開(kāi)始下載和顯示。
所以我們需要獲取首屏內(nèi)最后一張圖片加載完的時(shí)間(綁定首屏內(nèi)所有圖片的 load 事件),然后減去 navigationStart 時(shí)間,則為“首屏加載”時(shí)間。
首屏位置調(diào)用 API 開(kāi)始統(tǒng)計(jì) -> 綁定首屏內(nèi)所有圖片的 load 事件 -> 頁(yè)面加載完后判斷圖片是否在首屏內(nèi),找出加載最慢的一張 -> 首屏?xí)r間
復(fù)制代碼
白屏?xí)r間=開(kāi)始渲染時(shí)間(首字節(jié)時(shí)間+HTML下載完成時(shí)間)+頭部資源加載時(shí)間。
// PerformanceTiming
performance.timing.responseStart - performance.timing.navigationStart
// or 在 chrome 高版本下
(chrome.loadTimes().firstPaintTime - chrome.loadTimes().startLoadTime)*1000
performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart
performance.timing.loadEventEnd - performance.timing.navigationStart
然我在其它地方也寫(xiě)過(guò)這篇文章,這里也寫(xiě)一下吧。
在前端或小程序開(kāi)發(fā)中,有時(shí)候有一個(gè)需求,就是獲得選擇照片的拍攝時(shí)間。不過(guò),選擇照片之后能夠獲得的時(shí)間是最新的修改時(shí)間,有時(shí)候還不太正確。但是,我們還是有辦法去解析照片的拍攝時(shí)間的,這就要說(shuō)到EXIF了。
一般而言,使用移動(dòng)設(shè)備的相機(jī)應(yīng)用所拍攝的照片都是帶EXIF信息的JPG格式。EXIF信息就會(huì)記錄拍照片的設(shè)備型號(hào)、時(shí)間等信息,不過(guò)JPG格式并沒(méi)有要求必須攜帶EXIF信息,所以有些照片是沒(méi)有EXIF信息的。EXIF信息也是可以被修改和刪除的。雖然有解析庫(kù)可以使用,但我們就直接寫(xiě)一個(gè)代碼,不用庫(kù),代碼如下:
parseImage(data){
// data為ArrayBuffer類(lèi)型的JPG文件的二進(jìn)制數(shù)據(jù)
let arr=new Uint8Array(data);
let base=0; // TIFF數(shù)據(jù)頭開(kāi)始地址
let timeTagIndex=0; // EXIF時(shí)間信息標(biāo)記開(kāi)始地址
for(let i=0;i<arr.length;i++){
// 獲取TIFF數(shù)據(jù)頭地址
if(arr[i]==69 && arr[i+1]==120 && arr[i+2]==105 && arr[i+3]==102 && arr[i+4]==0 && arr[i+5]==0){
base=i+6;
}
// 獲取時(shí)間標(biāo)簽地址
if(arr[i]==0x90 && arr[i+1]==0x03){
timeTagIndex=i;
break; // 因?yàn)檫@個(gè)if的條件比較容易重復(fù),但是我們要的是第一個(gè),所以這里就可以直接退出了
}
}
let bias=0; // 偏移地址
for(let i=0;i<=3;i++){
bias=bias<<8;
bias+=arr[timeTagIndex+8+i];
}
let datetime_addr_index=base+bias; // 實(shí)際地址
let datetimestr=""; // 日期字符串
for(let i=datetime_addr_index;i<=datetime_addr_index+19;i++){
datetimestr+=String.fromCharCode(arr[i]);
}
return datetimestr; // 接著,我們可以將日期字符串進(jìn)行一定的字符串和日期時(shí)間處理,以滿足需要
}
這個(gè)代碼,在瀏覽器端和小程序中都是可以使用的。選擇照片文件之后,一般可以讀取到它的ArrayBuffer,這個(gè)ArrayBuffer包含了照片中的每一個(gè)字節(jié)。將ArrayBuffer傳入這個(gè)函數(shù),它會(huì)解析然后返回“年:月:日 時(shí):分:秒”格式的、代表日期含義的字符串。
關(guān)于第3行代碼,因?yàn)锳rrayBuffer是不能直接操作的,因此要把它轉(zhuǎn)換為T(mén)ypedArray,TypedArray有很多種,那么選那種比較好呢?由于在格式解析中,我們喜歡用0x00~0xff中的一個(gè)值來(lái)表示一個(gè)字節(jié),而Uint8Array中每個(gè)元素的取值正好是0x00~0xff(即0~255),因此選擇Uint8Array這種TypedArray是比較合適的。
關(guān)于第7~10行代碼,EXIF有很多種格式,最常用的是TIFF。EXIF數(shù)據(jù)的前6個(gè)字節(jié)稱(chēng)為EXIF數(shù)據(jù)頭,之后則是TIFF數(shù)據(jù)頭和TIFF數(shù)據(jù)。
在EXIF數(shù)據(jù)頭中,前四個(gè)字節(jié)是Exif的ASCII碼(注意大小寫(xiě)),后面是2個(gè)字節(jié)的0x00。
關(guān)于第11~15行代碼,在這里,我們獲取到了時(shí)間信息存儲(chǔ)的位置。在TIFF中,存儲(chǔ)的比如型號(hào)、時(shí)間等各種類(lèi)型的信息,被稱(chēng)為一個(gè)標(biāo)簽。每一個(gè)標(biāo)簽占用12個(gè)字節(jié):2個(gè)字節(jié)(表示這個(gè)標(biāo)簽代表什么含義,意思就是說(shuō)后面的10個(gè)字節(jié)是什么型號(hào)還是時(shí)間還是什么,如果代表時(shí)間,則這兩個(gè)字節(jié)一定是0x9003)+2個(gè)字節(jié)(代表用什么格式存儲(chǔ),時(shí)間標(biāo)簽的情況下這里的值一般是0x0002,代表值使用ASCII碼的格式進(jìn)行存儲(chǔ))+4個(gè)字節(jié)(代表值會(huì)占用多少個(gè)字節(jié),時(shí)間標(biāo)簽規(guī)定值一定是20個(gè)字節(jié))+4個(gè)字節(jié)(該標(biāo)簽的值或者值所在地址,因?yàn)檫@里只有4個(gè)字節(jié),而時(shí)間的值一定有20個(gè)字節(jié),因此在此時(shí)這里存儲(chǔ)的是時(shí)間標(biāo)簽的值所在的地址,而不是具體的值,在TIFF中,所有地址都是從TIFF數(shù)據(jù)頭的第一個(gè)字節(jié)所在地址開(kāi)始計(jì)算的,因此我們需要知道TIFF數(shù)據(jù)頭的地址,然后時(shí)間值的地址就是TIFF數(shù)據(jù)頭的地址加上這個(gè)值)。
關(guān)于第25~28行代碼,我們根據(jù)上面獲取到的時(shí)間標(biāo)簽所在位置,讀取20個(gè)字節(jié),這20個(gè)字節(jié)就是EXIF中的時(shí)間信息了。
加載圖片是提高用戶(hù)體驗(yàn)的一個(gè)很好方法。圖片預(yù)先加載到瀏覽器中,訪問(wèn)者便可順利地在你的網(wǎng)站上沖浪,并享受到極快的加載速度。
這對(duì)圖片畫(huà)廊及圖片占據(jù)很大比例的網(wǎng)站來(lái)說(shuō)十分有利,它保證了圖片快速、無(wú)縫地發(fā)布,也可幫助用戶(hù)在瀏覽你網(wǎng)站內(nèi)容時(shí)獲得更好的用戶(hù)體驗(yàn)。本文將分享三個(gè)不同的預(yù)加載技術(shù),來(lái)增強(qiáng)網(wǎng)站的性能與可用性。
方法一:用css和JavaScript實(shí)現(xiàn)預(yù)加載
實(shí)現(xiàn)預(yù)加載圖片有很多方法,包括使用css、JavaScript及兩者的各種組合。這些技術(shù)可根據(jù)不同設(shè)計(jì)場(chǎng)景設(shè)計(jì)出相應(yīng)的解決方案,十分高效。
單純使用CSS,可容易、高效地預(yù)加載圖片,代碼如下:
#preload-01 { background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px; }
#preload-02 { background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px; }
#preload-03 { background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px; }
將這三個(gè)ID選擇器應(yīng)用到(X)html元素中,我們便可通過(guò)CSS的background屬性將圖片預(yù)加載到屏幕外的背景上。
只要這些圖片的路徑保持不變,當(dāng)它們?cè)赪eb頁(yè)面的其他地方被調(diào)用時(shí),瀏覽器就會(huì)在渲染過(guò)程中使用預(yù)加載(緩存)的圖片。簡(jiǎn)單、高效,不需要任何JavaScript。
該方法雖然高效,但仍有改進(jìn)余地。使用該法加載的圖片會(huì)同頁(yè)面的其他內(nèi)容一起加載,增加了頁(yè)面的整體加載時(shí)間。
為了解決這個(gè)問(wèn)題,我們?cè)黾恿艘恍㎎avaScript代碼,來(lái)推遲預(yù)加載的時(shí)間,直到頁(yè)面加載完畢。代碼如下:
function preloader() {
if (document.getElementById) {
document.getElementById("preload-01").style.background="url(http://domain.tld/image-01.png) no-repeat -9999px -9999px";
document.getElementById("preload-02").style.background="url(http://domain.tld/image-02.png) no-repeat -9999px -9999px";
document.getElementById("preload-03").style.background="url(http://domain.tld/image-03.png) no-repeat -9999px -9999px";
}
}
function addLoadEvent(func) {
var oldonload=window.onload;
if (typeof window.onload !='function') {
window.onload=func;
} else {
window.onload=function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
在該腳本的第一部分,我們獲取使用類(lèi)選擇器的元素,并為其設(shè)置了background屬性,以預(yù)加載不同的圖片。
該腳本的第二部分,我們使用addLoadEvent()函數(shù)來(lái)延遲preloader()函數(shù)的加載時(shí)間,直到頁(yè)面加載完畢。
如果JavaScript無(wú)法在用戶(hù)的瀏覽器中正常運(yùn)行,會(huì)發(fā)生什么?很簡(jiǎn)單,圖片不會(huì)被預(yù)加載,當(dāng)頁(yè)面調(diào)用圖片時(shí),正常顯示即可。
上述方法有時(shí)確實(shí)很高效,但我們逐漸發(fā)現(xiàn)它在實(shí)際實(shí)現(xiàn)過(guò)程中會(huì)耗費(fèi)太多時(shí)間。相反,我更喜歡使用純JavaScript來(lái)實(shí)現(xiàn)圖片的預(yù)加載。
下面將提供兩種這樣的預(yù)加載方法,它們可以很漂亮地工作于所有現(xiàn)代瀏覽器之上。
JavaScript代碼段1
只需簡(jiǎn)單編輯、加載所需要圖片的路徑與名稱(chēng)即可,很容易實(shí)現(xiàn):
<div class="hidden">
<script type="text/javascript">
var images=new Array()
function preload() {
for (i=0; i < preload.arguments.length; i++) {
images[i]=new Image()
images[i].src=preload.arguments[i]
}
}
preload(
"http://domain.tld/gallery/image-001.jpg",
"http://domain.tld/gallery/image-002.jpg",
"http://domain.tld/gallery/image-003.jpg"
)
</script>
</div>
該方法尤其適用預(yù)加載大量的圖片。我的畫(huà)廊網(wǎng)站使用該技術(shù),預(yù)加載圖片數(shù)量達(dá)50多張。將該腳本應(yīng)用到登錄頁(yè)面,只要用戶(hù)輸入登錄帳號(hào),大部分畫(huà)廊圖片將被預(yù)加載。
JavaScript代碼段2
該方法與上面的方法類(lèi)似,也可以預(yù)加載任意數(shù)量的圖片。將下面的腳本添加入任何Web頁(yè)中,根據(jù)程序指令進(jìn)行編輯即可。
<div class="hidden">
<script type="text/javascript">
if (document.images) {
img1=new Image();
img2=new Image();
img3=new Image();
img1.src="http://domain.tld/path/to/image-001.gif";
img2.src="http://domain.tld/path/to/image-002.gif";
img3.src="http://domain.tld/path/to/image-003.gif";
}
</script>
</div>
正如所看見(jiàn),每加載一個(gè)圖片都需要?jiǎng)?chuàng)建一個(gè)變量,如“img1=new Image();”,及圖片源地址聲明,如“img3.src=“../path/to/image-003.gif”;”。參考該模式,你可根據(jù)需要加載任意多的圖片。
我們又對(duì)該方法進(jìn)行了改進(jìn)。將該腳本封裝入一個(gè)函數(shù)中,并使用 addLoadEvent(),延遲預(yù)加載時(shí)間,直到頁(yè)面加載完畢。
function preloader() {
if (document.images) {
var img1=new Image();
var img2=new Image();
var img3=new Image();
img1.src="http://domain.tld/path/to/image-001.gif";
img2.src="http://domain.tld/path/to/image-002.gif";
img3.src="http://domain.tld/path/to/image-003.gif";
}
}
function addLoadEvent(func) {
var oldonload=window.onload;
if (typeof window.onload !='function') {
window.onload=func;
} else {
window.onload=function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
上面所給出的方法似乎不夠酷,那現(xiàn)在來(lái)看一個(gè)使用Ajax實(shí)現(xiàn)圖片預(yù)加載的方法。該方法利用DOM,不僅僅預(yù)加載圖片,還會(huì)預(yù)加載CSS、JavaScript等相關(guān)的東西。使用Ajax,比直接使用JavaScript,優(yōu)越之處在于JavaScript和CSS的加載不會(huì)影響到當(dāng)前頁(yè)面。該方法簡(jiǎn)潔、高效。
window.onload=function() {
setTimeout(function() {
// XHR to request a js and a CSS
var xhr=new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.js');
xhr.send('');
xhr=new XMLHttpRequest();
xhr.open('GET', 'http://domain.tld/preload.css');
xhr.send('');
// preload image
new Image().src="http://domain.tld/preload.png";
}, 1000);
};
上面代碼預(yù)加載了“preload.js”、“preload.css”和“preload.png”。1000毫秒的超時(shí)是為了防止腳本掛起,而導(dǎo)致正常頁(yè)面出現(xiàn)功能問(wèn)題。
下面,我們看看如何用JavaScript來(lái)實(shí)現(xiàn)該加載過(guò)程:
window.onload=function() {
setTimeout(function() {
// reference to <head>
var head=document.getElementsByTagName('head')[0];
// a new CSS
var css=document.createElement('link');
css.type="text/css";
css.rel="stylesheet";
css.href="http://domain.tld/preload.css";
// a new JS
var js=document.createElement("script");
js.type="text/javascript";
js.src="http://domain.tld/preload.js";
// preload JS and CSS
head.appendChild(css);
head.appendChild(js);
// preload image
new Image().src="http://domain.tld/preload.png";
}, 1000);
};
這里,我們通過(guò)DOM創(chuàng)建三個(gè)元素來(lái)實(shí)現(xiàn)三個(gè)文件的預(yù)加載。正如上面提到的那樣,使用Ajax,加載文件不會(huì)應(yīng)用到加載頁(yè)面上。從這點(diǎn)上看,Ajax方法優(yōu)越于JavaScript。
- End -
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。