摘要:如果你的文件涉及操作,可以直接在里面添加回調(diào)函數(shù),或者說基本上我們的文件都可以寫在里面進行調(diào)用其實,這和我們將文件放在底部,在上面加以及異步加載文件的效果是一樣一樣的。
如果大家想繼續(xù)看下面的內(nèi)容的話,有一個要求,就是回答我一個問題:
你這樣寫過代碼嗎?
window.onload = function(){ $(".gravatar").on("click",function(){ //... }); //以及其他操作DOM的節(jié)點 }
如果答案是 yes. 那么,bingo, 這里我們將深入講解,這樣寫代碼到底有沒有IQ。
如果答案是 No. 那么,2333333, 你也可以看一下。 萬一哪天用上了呢?
可能會有童鞋反問,那么,我改怎么寫呢?
沒錯,這里就是說的就是這個。
使用過jquery的童鞋,應(yīng)該知道有一個叫做ready的方法.
即:
$(document).ready(function(){ //操作DOM相關(guān) //... })
那這個和上面的寫法有什么區(qū)別呢? 誰比較好一點呢(指性能)?
wait wait wait ~
這問題有點多誒。 不急。 想想看, jquery老大哥 就是幫你 提高性能的,肯定是下面那種好呢。
Why?
原因我們接著說.
頁面加載就是從你輸入網(wǎng)址+enter開始,發(fā)生的一些列過程,最終到頁面顯示。 從微觀上分的話,頁面加載有兩部分
一個是以DOMContentLoaded觸發(fā)為標志的DOM樹的渲染完成
一個是以輔助資源img,font加載完成為觸發(fā)標志的onload事件
他們兩個的具體區(qū)別就是"資源的加載"這個關(guān)鍵點.
在獲得數(shù)據(jù)響應(yīng)后,頁面開始解析,發(fā)生的過程為:
(1) 解析HTML結(jié)構(gòu)。
(2) 加載外部腳本和樣式表文件。
(3) 解析并執(zhí)行腳本代碼。
(4) 構(gòu)造HTML DOM模型。//ready執(zhí)行
(5) 加載圖片等外部文件。
(6) 頁面加載完畢。//load執(zhí)行
其實,說到這里,這篇文章就已經(jīng)結(jié)束了。
想得美。
這只是,頁面加載很淺的一塊,前端能在頁面加載上做的工作其實超級多。 要知道, 從你輸入網(wǎng)站 + enter鍵后,發(fā)生的過程為:
重定向=>檢查DNS緩存=> DNS解析 => TCP連接 => 發(fā)請求 => 得到響應(yīng)=> 瀏覽器處理 => 最后onload
你可以數(shù)一數(shù),前文的頁面加載和這里的頁面加載的范圍到底是怎樣的一個區(qū)別. 也就是說上文的頁面加載其實 只算是
瀏覽器處理=> 最后onload這一過程。 懂吧。 很小很小。
所以,這里我們先從宏觀上來講解一下,頁面加載的整個流程.
這樣,干講頁面加載真的很沒趣誒, 又沒有吃的,又沒有程序員鼓勵師,又沒有l(wèi)eader的加薪,憑借的是本寶寶的 滿腔熱情 和 對技術(shù)的執(zhí)著。 感動吧~
開玩笑的, 意淫了之后。我們說正事。
如果我們想深入了解宏觀頁面加載,需要掌握ECMA5新給出的一個API。 performance . 是不是 感覺很熟悉呢?
以前,我們來檢查瀏覽器的時候,大部分情況下是使用
console.time(specialNum); console.timeEnd(specialNum);
或者
new Date().getTime(); //或者 Date.now();
上面說的兩種方法, 獲取的精度都是毫秒級(10^-6),對于一些非常精確的測試,他們的作用來還是蠻有限的,而且獲取數(shù)據(jù)的方式,也比較complicated.
ES5提出的performance可以獲取到,微秒級別(10^-9). 而且,能夠得到后臺事件的更多時間數(shù)據(jù)。
他的兼容性是IE9+ 。 覺得已經(jīng)足夠了。
通常,我們可以從performance.timing對象上,獲得我們想要的一切時間值.具體有哪些,我就不贅述了。直接看一張圖:
(from Sam Dutton)
比如,我們獲得重定向時間用:
var time = performance.timing; var redirect = time.redirectEnd - time.redirectStart; //單位為微秒
這就已經(jīng)夠我們用的啦。
里面需要進行一點解釋
即DOMContentLoaded事件 是在domContentLoaded那段觸發(fā)的。圖中所指的domContentLoaded其實分為兩塊, 一個是domContentLoadedEventStart和domContentLoadedEventEnd. 詳見下述說明:(from 賴小賴小賴)
// 獲取 performance 數(shù)據(jù) var performance = { // memory 是非標準屬性,只在 Chrome 有 // 財富問題:我有多少內(nèi)存 memory: { usedJSHeapSize: 16100000, // JS 對象(包括V8引擎內(nèi)部對象)占用的內(nèi)存,一定小于 totalJSHeapSize totalJSHeapSize: 35100000, // 可使用的內(nèi)存 jsHeapSizeLimit: 793000000 // 內(nèi)存大小限制 }, // 哲學(xué)問題:我從哪里來? navigation: { redirectCount: 0, // 如果有重定向的話,頁面通過幾次重定向跳轉(zhuǎn)而來 type: 0 // 0 即 TYPE_NAVIGATENEXT 正常進入的頁面(非刷新、非重定向等) // 1 即 TYPE_RELOAD 通過 window.location.reload() 刷新的頁面 // 2 即 TYPE_BACK_FORWARD 通過瀏覽器的前進后退按鈕進入的頁面(歷史記錄) // 255 即 TYPE_UNDEFINED 非以上方式進入的頁面 }, timing: { // 在同一個瀏覽器上下文中,前一個網(wǎng)頁(與當前頁面不一定同域)unload 的時間戳,如果無前一個網(wǎng)頁 unload ,則與 fetchStart 值相等 navigationStart: 1441112691935, // 前一個網(wǎng)頁(與當前頁面同域)unload 的時間戳,如果無前一個網(wǎng)頁 unload 或者前一個網(wǎng)頁與當前頁面不同域,則值為 0 unloadEventStart: 0, // 和 unloadEventStart 相對應(yīng),返回前一個網(wǎng)頁 unload 事件綁定的回調(diào)函數(shù)執(zhí)行完畢的時間戳 unloadEventEnd: 0, // 第一個 HTTP 重定向發(fā)生時的時間。有跳轉(zhuǎn)且是同域名內(nèi)的重定向才算,否則值為 0 redirectStart: 0, // 最后一個 HTTP 重定向完成時的時間。有跳轉(zhuǎn)且是同域名內(nèi)部的重定向才算,否則值為 0 redirectEnd: 0, // 瀏覽器準備好使用 HTTP 請求抓取文檔的時間,這發(fā)生在檢查本地緩存之前 fetchStart: 1441112692155, // DNS 域名查詢開始的時間,如果使用了本地緩存(即無 DNS 查詢)或持久連接,則與 fetchStart 值相等 domainLookupStart: 1441112692155, // DNS 域名查詢完成的時間,如果使用了本地緩存(即無 DNS 查詢)或持久連接,則與 fetchStart 值相等 domainLookupEnd: 1441112692155, // HTTP(TCP) 開始建立連接的時間,如果是持久連接,則與 fetchStart 值相等 // 注意如果在傳輸層發(fā)生了錯誤且重新建立連接,則這里顯示的是新建立的連接開始的時間 connectStart: 1441112692155, // HTTP(TCP) 完成建立連接的時間(完成握手),如果是持久連接,則與 fetchStart 值相等 // 注意如果在傳輸層發(fā)生了錯誤且重新建立連接,則這里顯示的是新建立的連接完成的時間 // 注意這里握手結(jié)束,包括安全連接建立完成、SOCKS 授權(quán)通過 connectEnd: 1441112692155, // HTTPS 連接開始的時間,如果不是安全連接,則值為 0 secureConnectionStart: 0, // HTTP 請求讀取真實文檔開始的時間(完成建立連接),包括從本地讀取緩存 // 連接錯誤重連時,這里顯示的也是新建立連接的時間 requestStart: 1441112692158, // HTTP 開始接收響應(yīng)的時間(獲取到第一個字節(jié)),包括從本地讀取緩存 responseStart: 1441112692686, // HTTP 響應(yīng)全部接收完成的時間(獲取到最后一個字節(jié)),包括從本地讀取緩存 responseEnd: 1441112692687, // 開始解析渲染 DOM 樹的時間,此時 Document.readyState 變?yōu)?loading,并將拋出 readystatechange 相關(guān)事件 domLoading: 1441112692690, // 完成解析 DOM 樹的時間,Document.readyState 變?yōu)?interactive,并將拋出 readystatechange 相關(guān)事件 // 注意只是 DOM 樹解析完成,這時候并沒有開始加載網(wǎng)頁內(nèi)的資源 domInteractive: 1441112693093, // DOM 解析完成后,網(wǎng)頁內(nèi)資源加載開始的時間 // 在 DOMContentLoaded 事件拋出前發(fā)生 domContentLoadedEventStart: 1441112693093, // DOM 解析完成后,網(wǎng)頁內(nèi)資源加載完成的時間(如 JS 腳本加載執(zhí)行完畢) domContentLoadedEventEnd: 1441112693101, // DOM 樹解析完成,且資源也準備就緒的時間,Document.readyState 變?yōu)?complete,并將拋出 readystatechange 相關(guān)事件 domComplete: 1441112693214, // load 事件發(fā)送給文檔,也即 load 回調(diào)函數(shù)開始執(zhí)行的時間 // 注意如果沒有綁定 load 事件,值為 0 loadEventStart: 1441112693214, // load 事件的回調(diào)函數(shù)執(zhí)行完畢的時間 loadEventEnd: 1441112693215 } };
不過performance還有另外一個方法 now
performance.now()通常,我們會將該方法和Date.now()進行一個對比。
performance.now(); //輸出是微秒級別 Date.now(); //輸出是毫秒級別
其中Date.now()是輸出 從1970年開始的毫秒數(shù).
performance.now()參考的是從.performance.timing.navigationStart(頁面開始加載)的時間, 到現(xiàn)在的微秒數(shù).
這里,我們可以使用performance.now()來模擬獲取DomContentLoaded的時間。
var timesnipe = performance.now(); document.addEventListener("DOMContentLoaded", function() { console.log(performance.now() - timesnipe); }, false); window.addEventListener("load", function() { console.log(performance.now() - timesnipe); }, false); //但是這樣并不等同于,只能算作約等于 performance.timing.domContentLoadedEventStart - performance.timing.domLoading; //檢測domLoadEvent觸發(fā)時間
上面不相等的原因就在于,當執(zhí)行script的時候,DOM其實已經(jīng)開始解析DOM和頁面內(nèi)容, 所以會造成時間上 比 真實時間略短。另外performance還有其他幾個API,比如makr,getEntries. 不過,這里因為和頁面顯示的關(guān)系不是很大,這里就不做過多的講解了。 有興趣,可以參考:賴小賴小賴
接下來,我們一步一步來看一下,頁面加載的整個過程.
這是頁面加載的第一步(也有可能沒有). 比如,當一個頁面已經(jīng)遷移,但是你輸入原來的網(wǎng)站地址的時候就會發(fā)生。
或者, 比如example.com -> m.example.com/home 。 這樣耗費的時間成本是雙倍的。 這里就會經(jīng)過兩次DNS解析,TCP連接,以及請求的發(fā)送。所以,在后臺設(shè)置好正確的網(wǎng)址是很重要的。
如圖:
這里,我們可以使用.performance的屬性,計算出重定向時間
redirectTime = redirectEnd - redirectStart
接著我們就到了cache,DNS,TCP,Request,以及Response的階段
cache,DNS,TCP,Request,Response如果我們的域名輸入正確的話,接著,瀏覽器會查詢本地是否有域名緩存(appCache),如果有,則不需要進行DNS解析,否則需要對域名進行解析,找到真實的IP地址,然后建立3次握手連接, 發(fā)送請求, 最后接受數(shù)據(jù)。 通常,這一部分,可以做的優(yōu)化有:
發(fā)送請求的優(yōu)化:加異地機房,加CDN.(加快解析request)
請求加載數(shù)據(jù)的優(yōu)化:頁面內(nèi)容經(jīng)過 gzip 壓縮,靜態(tài)資源 css/js 等壓縮(request到response的優(yōu)化)
ok~ 使用performance測試時間為:
// DNS 緩存時間 times.appcache = t.domainLookupStart - t.fetchStart; // TCP 建立連接完成握手的時間 times.connect = t.connectEnd - t.connectStart; //DNS 查詢時間 times.lookupDomain = t.domainLookupEnd - t.domainLookupStart; //整個解析時間 var lookup = t.responseEnd - t.fetchStart;
其實,只要對照那個圖查查over,不用太關(guān)注上面的式子。使用時需要注意,performance的相關(guān)操作,最好放在onload的回調(diào)中執(zhí)行,避免出現(xiàn)異常的bug.
process,onload這里的過程其實就和開頭的時候說的一樣
(1) 解析HTML結(jié)構(gòu)。
(2) 加載外部腳本和樣式表文件。
(3) 解析并執(zhí)行腳本代碼。
(4) 構(gòu)造HTML DOM模型。//ready執(zhí)行
(5) 加載圖片等外部文件。
(6) 頁面加載完畢。//load執(zhí)行
ok~ 這里,我們來計算一下時間:
上performance
//計算DOMContentLoaded觸發(fā)時間 var contentLoadedTime = t.domContentLoadedEventStart-t.domLoading //計算load觸發(fā)時間 var loadTime = t.domComplete - t.domLoading;
更直觀的,我們可以在Chrome的developer工具的network選項里面得到我們想要的答案.
這兩個線,分別代表的是DOMContentLoaded和onload觸發(fā)的時間。 這也更能直觀的看出,DOMContentLoaded事件比onload事件先觸發(fā)吧。現(xiàn)在回到我們開頭的那個問題。我們到底該將代碼寫在什么地方呢?
這里,這個問題就很好回答了。如果你的js文件涉及DOM操作,可以直接在DOMContentLoaded里面添加回調(diào)函數(shù),或者說基本上我們的js文件都可以寫在里面進行調(diào)用. 其實,這和我們將js文件放在body底部,在js上面加async,defer,以及hard Callback異步加載js文件的效果是一樣一樣的。
上面一部分我有篇文章已經(jīng)介紹過了,所以這里就不贅述了。
接下來我們要做的最后一件事,就是看看jquery老大哥,他的ready事件的原理到底是什么.
jquery主要做的工作就是兼容IE6,7,8實現(xiàn)DOMContentLoaded的效果.由于現(xiàn)在主流只要兼容到IE8, 剩下IE6,7我們不做過多的分析了。
目前流行的做法有兩種, 一種是使用readystatechange實現(xiàn),另外一種使用IE自帶的doScroll方法實現(xiàn).
這其實是IE6,7,8的特有屬性,用它來標識某個元素的加載狀態(tài)。 但是現(xiàn)在w3c規(guī)定,只有xhr才有這個事件。 所以,這里,我們一般只能在IE中使用readyStateChange否則,其他瀏覽器是沒有效果的。
詳見:readyState兼容性分析
這樣,我們模擬jquery的ready事件時就可以使用:
document.onreadystatechange = function () { if (document.readyState == "interactive" || document.readyState == "complete") { //添加回調(diào)... } }
理想很豐滿,現(xiàn)實很骨感。 事實上, 當readyState為interactive時, Dom的結(jié)構(gòu)并未完全穩(wěn)定,如果還有其他腳本影響DOM時, 這時候可能會造成bug。 另外為complete時, 這時候圖片等相關(guān)資源已經(jīng)加載完成。 這個時候模擬觸發(fā)DOMContentLoaded事件,其實和onload事件觸發(fā)時間并沒有太久的時間距離。 這種方式兼容低版本IE還是不太可靠的。
另外提供一個doScroll方式
這是IE低版本特有的,不過IE11已經(jīng)棄用了。 使用scrollLeft和scrollTop代替. doScroll 的主要作用是檢測DOM結(jié)構(gòu)是否問題, 通常我們會使用輪詢來檢測doScroll是否可用,當可用的時候一定是DOM結(jié)構(gòu)穩(wěn)定,圖片資源還未加載的時候。
我們來看一下jquery中實現(xiàn)doScroll的兼容:
//低版本的IE瀏覽器,這里添加監(jiān)聽作為向下兼容,如果doScroll執(zhí)行出現(xiàn)bug,也能保證ready函數(shù)的執(zhí)行 document.attachEvent( "onreadystatechange", DOMContentLoaded ); window.attachEvent( "onload", jQuery.ready ); //在ready里面會對執(zhí)行做判斷,確保只執(zhí)行一次 var top = false; // 如果是IE且不是iframe就通過不停的檢查doScroll來判斷dom結(jié)構(gòu)是否ready try { top = window.frameElement == null && document.documentElement; } catch(e) {} if ( top && top.doScroll ) { (function doScrollCheck() { if ( !jQuery.isReady ) {//ready方法沒有執(zhí)行過 try { // 檢查是否可以向左scroll滑動,當dom結(jié)構(gòu)還沒有解析完成時會拋出異常 top.doScroll("left"); } catch(e) { //遞歸調(diào)用,直到當dom結(jié)構(gòu)解析完成 return setTimeout( doScrollCheck, 50 ); } //沒有發(fā)現(xiàn)異常,表示dom結(jié)構(gòu)解析完成,刪除之前綁定的onreadystatechange事件 //執(zhí)行jQuery的ready方法 jQuery.ready(); } })(); } //看看jQuery.ready()方法: ready:function(wait) { if (wait === true ? --jQuery.readyWait : jQuery.isReady) { //判斷頁面是否已完成加載并且是否已經(jīng)執(zhí)行ready方法 //通過isReady狀態(tài)進行判斷, 保證只執(zhí)行一次 return; } if (!document.body) { return setTimeout(jQuery.ready); } jQuery.isReady = true; //指示ready方法已被執(zhí)行 //這也是上面兩次綁定事件的原因,會保證只執(zhí)行一次 if (wait !== true && --jQuery.readyWait > 0) { return; } //以下是處理ready的狀態(tài) readyList.resolveWith(document, [jQuery]); if (jQuery.fn.trigger) { //解除引用 jQuery(document).trigger("ready").off("ready"); } }
以上就是jquery 兼容ready的方法。
ending~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/78711.html
摘要:什么是重流重繪通常頁面在加載你的等文件時,引擎會對文件加以解析,最終生成兩顆樹,渲染樹和樹樹中的需要顯示節(jié)點在渲染樹中都會存在但是的則不會存在。瀏覽器會重新計算出渲染樹這一過程叫做重流重排將更新后的結(jié)構(gòu)重新渲染到頁面這一過程叫做重繪。 本文采用問答模式,目的是深入HTML內(nèi)部,去學(xué)習(xí)一些我們不經(jīng)常關(guān)注,但卻實實在在存在的problem. 文章內(nèi)容略顯裝逼,如果大家受不了,請帶好護目鏡。...
摘要:本文是面向前端小白的,大手子可以跳過,寫的不好之處多多分鐘搞定常用基礎(chǔ)知識前端掘金基礎(chǔ)智商劃重點在實際開發(fā)中,已經(jīng)非常普及了。 JavaScript字符串所有API全解密 - 掘金關(guān)于 我的博客:louis blog SF專欄:路易斯前端深度課 原文鏈接:JavaScript字符串所有API全解密 本文近 6k 字,讀完需 10 分鐘。 字符串作為基本的信息交流的橋梁,幾乎被所有的編程...
摘要:主要兼容的微信的瀏覽器,因為要在朋友圈來營銷,總體來說,會偏設(shè)計以及動畫些。 有一天,我們組內(nèi)的一個小伙伴突然問我,你知道有一個叫重構(gòu)工程師的崗位?這是干什么的?重構(gòu)工程師 這個問題引發(fā)了我對前端領(lǐng)域發(fā)展的思考,所以我來梳理下前端領(lǐng)域的發(fā)展過程,順便小小的預(yù)測下2017年的趨勢。不想看回憶的,可以直接跳到后面看展望。 神說,要有光,就有了光 自1991年蒂姆·伯納斯-李公開提及HTML...
摘要:主要兼容的微信的瀏覽器,因為要在朋友圈來營銷,總體來說,會偏設(shè)計以及動畫些。 有一天,我們組內(nèi)的一個小伙伴突然問我,你知道有一個叫重構(gòu)工程師的崗位?這是干什么的?重構(gòu)工程師 這個問題引發(fā)了我對前端領(lǐng)域發(fā)展的思考,所以我來梳理下前端領(lǐng)域的發(fā)展過程,順便小小的預(yù)測下2017年的趨勢。不想看回憶的,可以直接跳到后面看展望。 神說,要有光,就有了光 自1991年蒂姆·伯納斯-李公開提及HTML...
閱讀 1207·2021-11-17 09:33
閱讀 3612·2021-09-28 09:42
閱讀 3343·2021-09-13 10:35
閱讀 2493·2021-09-06 15:00
閱讀 2442·2021-08-27 13:12
閱讀 3612·2021-07-26 23:38
閱讀 1849·2019-08-30 15:55
閱讀 542·2019-08-30 15:53