我的部落格這週的圖片超多,下載完首頁竟然要 5MB 這麼大,頻寬夠的話首頁下載完畢大概 1 分鐘左右,也因為這個關係我採用了 Lazy Load Plugin for jQuery 讓我部落格上的圖片可以等使用者看到時再下載,透過這種延遲效果就能讓使用者不會等待所有圖片都下載才能順利看到網頁,由於 Lazy Load Plugin for jQuery 必須在 jQuery(document).ready() 裡啟動,但我昨天測試卻怎麼測試都會等圖片全部下載完才會執行,那不就沒用了嗎?也因為此問題讓我研究出許多問題背後的問題與問題之外的問題,收穫頗為豐富,請繼續看下去。
基本上在 JavaScript 的 DOM 物件層級中,document 物件是隸屬於 windows 物件 ( document == window.document ),首先先介紹 jQuery(document).ready() 與 jQuery(window).load() 的差異,而這兩者的差別說明如下:
- jQuery(document).ready(function() { … }) 《 或簡寫為 $(function() { … }); 》
- 當 document 物件下所有 DOM 物件都可以正確取得時,就會觸發 jQuery.ready() 註冊的 function,這時雖然後 <img src="…" /> 定義的圖片正在下載,但由於 <img> 這個 DOM 物件已經都 ready 了,所以 jQuery 並不會等圖片全部下載完畢才執行 ready 事件。
- jQuery(window).load(function() { … })
- 而使用 window 的 load 事件,卻是完全不同的行為,jQuery 裡的 window 的 load 事件與 JavaScript 裡的 window.onload 事件一模一樣,註冊在這裡面的事件都會等到整個視窗裡所有資源都已經全部下載後才會執行,例如該頁面有 100 張圖片就會等 100 圖片都下載完才會執行,其中也包括所有 iframe 子頁面的內容必須完整載入。
但我遇到的問題就是我已經用了 jQuery(document).ready(function() { … }) 來載入 Lazy Load Plugin for jQuery 了,為什麼每次都會等待圖片全部下載完畢才會執行 ready() 事件呢?
我載入 lazyload 的語法如下:
jQuery(document).ready(function($)
{
$("#posts .story img").lazyload({
placeholder : "/pics/grey.gif",
effect : "fadeIn"
});
}
首先,我被 Fiddler 的 煙霧彈(即誤導的資訊) 打的灰頭土臉,由於 Lazy Load Plugin for jQuery 的原理是將動態將頁面所有 jQuery 選取到的圖片移除,等使用者捲動畫面看到該圖片時才會自動載入。不過在移除之前所有 DOM 都已經 ready 了,所以 Browser 已經對所有圖片發出 HTTP Request 的要求,只是等到 LazyLoad 在執行的時候才會將圖片移除,這時 HTTP Request 就會被中斷,我們用 HttpWatch 來查看即可發現這個狀況 ( 注意: 已經有許多 HTTP Request 已經發出(Sent),但之後被中斷下載(Aborted) ),從原始碼也可以看出實做的細節:
因為 Fiddler 是以類似 代理伺服器(Proxy Server) 的架構在運作,當瀏覽器已經將 HTTP 要求送達 Fiddler 且瀏覽器突然中斷連線時,這時 Fiddler 並不會停止要求網頁,而還是繼續慢慢的將圖片全部下載完畢,所以從 Fiddler 來看圖片是否被下載是不準的,因為 Fiddler 下在的圖片不見得會被瀏覽器下載。
另一方面來看,我的瀏覽器的確是等候圖片下載完畢才執行 jQuery 的 ready() 事件,研究許久之後才發現 .ready() - jQuery API 文件上有提到一段 .ready() 與 window.onload 不相容的注意事項:
而就這麼剛好我的 BlogEngine 內建的 JavaScript 有用到 window.onload 事件,在 jQuery API 文件上只有說「不相容」,但卻沒有說「哪裡不相容與何時會發生問題」,只有說「不要一起使用而以」。
而又這麼剛好我將許多 JavaScript Library 的載入與套用 jQuery(document).ready(function() { … }) 全部都擺在頁面最下方執行,這是為了效能考量,想不到這是導致效能低落的原兇,請參考加速前端網頁效能的14條規則一文。也因為這樣讓我花了四個小時才發現只要在註冊 window.onload 事件後才使用 $(document).ready() 就會導致 $(document).ready() 變的與 $(window).load() 的行為一樣,要等到網頁所有資源都下載完畢才會執行 $(document).ready() 中註冊的事件!
結論就是:只要 $(document).ready() 在 window.onload 事件註冊之前就先定義好就沒有這個問題了!
心得分享
感覺又一次老天給我的考驗(火焰的銬杯),因為我前前後後累計花費大約 8 個小時才研究出這點小小的心得,雖然覺得有點小小的不甘心(因為時間的投資報酬率太差),不過因為努力不懈得到的心得那種成就感不言可喻啊。
最近有點感慨就是同時要做事又要同時做研究的人真的很辛苦,做我們軟體開發這個行業來不及學的東西很多,舊的技術沒碰過、新的技術就學不快,或是基礎觀念不夠紮實也會影響學習的效率,但整天都在學習就不用工作啦;另一方面來說,都在趕案子的人要怎麼抽出時間做研究呢?回家之後體力不好、興趣不夠、熱情不足的人大概也很難再進修了,這件事很兩難,但願意努力的人久而久之就會慢慢感受出個人競爭力與其他人的差異了。
又要做事、又要學習,做中學 ( Learning by doing ) 變成是我們這個行業的常態,我也非常支持這種學習方式,因為比較務實,學到的知識也比較能吸收,不過我也經常看到「做而不學」的情況,許多人每天就是與程式奮戰,遇到問題就上 Google 查資料,找到 Sample Code 就用 Copy & Paste 解決,在知其然不知其所以然的情況下,久而久之程式品質就會惡化。
我覺得比較好「做中學」方式是將要做的東西或做過的東西都花些時間整理觀念(建議透過寫文章來整理),如果有相關書籍就買回來看,但是「書」不見得都是拿來「參考」的,而是要找時間「閱讀」,不過要特別注意:要用「閱讀」來讀書,而不是用「瀏覽」來看書,很多人已經習慣瀏覽而不會閱讀了!
讀這種技術或觀念的書籍或文章重點在於「思考」,讀書的時候要思考書籍內容的合理性與邏輯性,因為書籍文章都有可能會有筆誤或邏輯錯誤的情況,絕對不是像是看小說一樣看心情的。
我建議透過「寫作」來整理觀念,目的就在於「強迫思考」,要能寫出有料的東西一定是經過深思熟慮的,自己思考、辯證過的東西一定比你光看書還能深刻記憶,過程即便辛苦,但只要內心感受踏實,我想還是有可能成為一位快樂且專業的軟體開發人員。
相關連結