天天看點

項目小結:日立OA系統(Asp.net)

前言                                     

   進入公司6個月後被安排到該項目中,據說該項目規模很大,而拆分到公司的就隻是二十來個頁面,而我就負責其中的3個頁面和其他頁面的腳本代碼,後來負責項目的性能優化工作。至于業務邏輯方面确實沒什麼可說的,就是CRUD。由一個很好溝通的前輩和我們6個新人一起來搞。這裡有兩項内容很值得總結,它們都讓我技術上增進不少。

目錄                                     

1.頁面控件數龐大,頁面加載極慢,一步一步優化吧!

2.一鍵關閉web系統所有頁面,不斷嘗試,卻被奇怪的方法Kill了。

3.總結

1.頁面控件數龐大,頁面加載極慢,一步一步優化吧!               

  狀況:頁面在IE(6,7,8)中加載時間為2分鐘左右。沒錯,你沒看錯,這個時間忘不了,我手按秒表、寫javascript代碼和使用HttpWatcher分别測試了N次了。

  分析原因:

  1.頁面體積大,足足7M+,小電影啊!!

  2.頁面控件數龐大。該頁面有一張表格用來顯示記錄,這張表有19或24列,每列有1至2個控件,客戶要求以每頁200條記錄進行分頁(打死他也不肯少),悲催了,頁面隻算該表含有的控件數就超過8000個。

  插曲:分析工作也弄了一個多星期,主要工作是了解請求/響應的整個過程和統計過程中各部分所花費的時間,找出時間消耗大戶進行重點打擊報複!!我了解到請求/響應過程如下:

(下面隻是首次請求的過程)

1.用戶端浏覽器發送域名到DNS,DNS根據域名找到IP再發送回用戶端浏覽器;

2.浏覽器根據IP向Web伺服器送出請求(是Get方式,是以隻有請求頭),開始踏上不歸路;

3.伺服器接收到請求進行一連串處理(詳細請參考:asp.net管道模型(管線模型)之一發不可收拾和Asp.net頁面生命周期)

4.伺服器傳回響應(響應頭、響應體)給浏覽器;

5.浏覽器邊接收響應邊将HTML代碼解釋建構成Dom樹,遇到css、js等解釋性語言就進行解釋,如果是樣式檔案、腳本檔案或圖檔連結就向伺服器發送請求。其中請求和解析js時會阻塞Dom樹的建構(後來知道設定屬性defer="defer",就不會阻塞了),後面的頁面内容無法顯示,而css就不會。

最終發現問題出現在浏覽器加載、解釋、渲染、呈現上。不能不說句IE真的很慢……

優化前的做法:表格是手工建立的,裡面全是各種web伺服器控件。最後一列是含修改等按鈕的操作列,每點一次就請求一次伺服器然後該記錄所在行變為可編輯狀态。

  下面說說我的優化方法吧!這裡學到一個原則:讓使用者盡快看到頁面的變化而不是一片空白!

  1.首先将css檔案引用放在head标簽中,js檔案引用放在頁面代碼的末尾;

  2.分别合并css檔案引用和js檔案引用的請求(具體方法請參考:網頁優化系列一:合并檔案請求(asp.net版));

  3.壓縮css檔案和js檔案,主要就是去空白行、縮寫變量名;(注意:這裡要分釋出版和開發版,因為壓縮後的css和js檔案真的是無法維護的)

  現在優化效果不大,沒辦法控件多、頁面體積大嘛!繼續優化吧!

  4.關閉表格中伺服器控件的ViewState(大部分控件用于顯示,每次回傳都重新生成一次,啟用ViewState太多餘了),關閉後大大減小了頁面體積;

  5.壓縮該頁面的ViewState并後置。壓縮ViewState進一步減小頁面體積;因為ViewState預設是放在靠近<form>标簽的地方,而ViewState對于浏覽器來說是一堆放在隐藏控件的無用字元串,但浏覽器同樣要花力氣去加載和解釋它,将ViewState後置就可以盡快讓浏覽器加載解釋可視化元素,但後置的前提是ViewState不大,否則頁面貌似呈現完成,而因ViewState過大而實際仍然加載解釋,此時使用者點選某個伺服器控件就悲催了。(具體方法請參考:網頁優化系列三:使用壓縮後置viewstate)

  要知道ViewState是往返于B/S間的,能小則小啊!  

  到這一步頁面體積已經減小了很多,頁面加載時間降低到1分多鐘了^_^!!但優化的步伐是不能就此停止的!!

  6.模仿微網誌弄滑動分頁。客戶鐵定每頁200條記錄,那我默默地變吧!!表格的顯示區域最多能顯示40條記錄,于是以50條記錄為一組進行滑動分頁(為什麼是以40條為一組呢?起碼要弄條滾動條出來蒙一下小日本嘛^_^!!)。用Ajax異步請求服務端,服務端生成<tr>……</tr>這樣的html标簽加資料傳遞過來,然後加入到表格中。注意:Table标簽除了TD的innerHTML屬性可寫可讀外,其他标簽的innerHTML屬性為隻讀,是以我在前端用了一個全局變量儲存已加載的記錄,然後跟新的記錄合并後重新生成表格,顯示時感覺會有點突兀。現在想起來其實可以把隻傳遞判斷使用什麼html标簽的辨別符和具體的内容資料,然後用js生成表格的結構,而因為這個操作的js檔案比較大就可以在前一個頁面進行預加載,當進入該頁面時就可以直接讀cache了。(具體方法請參考:實作滑動分頁(微網誌分頁方式))

  7.異步修改、删除記錄。點選每行的修改按鈕時彈出一個div,異步取資料,修改完後發送異步請求儲存資料并用js修改該行的新值;點選删除按鈕時,異步發送請求給伺服器删除記錄,然後用js修改目前行的所有td為空白并在行内首個td中标明“該行已删除(もう削除しました!)”,操作列中的控件清空。

  好了,現在頁面剩下3000多個html标簽,體積為1M左右,加載時間為5秒左右。客戶基本滿意,那這部分就算是交差了,(*^__^*) 嘻嘻……

2.一鍵關閉web系統所有頁面,不斷嘗試,卻被奇怪的方法Kill了          

  需求:在OA系統首頁有一個“關閉系統”按鈕可以關閉該系統的所有頁面。

1.首次嘗試:

  這時我想到了樹結構。

      思路:每個頁面作為一個節點,并儲存其子節點,點選首頁的“關閉系統”按鈕時就層層周遊,首先是最底層的頁面被關閉最後到首頁被關閉。

  問題:但操作過程中關閉了中間某個頁面,點選首頁的“關閉系統”按鈕時由被關閉的頁面打開的頁面就無法被關閉。

  2.二次嘗試:

  思路:将所有子、孫頁面均儲存到首頁上。

      實作:

    首頁部分:定義一個數組對象用于儲存子、孫頁面的window對象;使用var win = window.open()打開子頁面,将win對象加入到子、孫數組中。

        子頁面部分:定義一個var parent = window.opener全局對象,然後将由該頁面打開的子頁面的window對象加入到parent.子、孫數組中。

            孫頁面部分:定義一個var parent = window.opener.parent對象,同上;

            就是這樣每個子、孫頁面都有一個引用首頁window的變量,進而操作首頁的子、孫數組。

      問題:到孫頁面那一層就出現大概是運作時不知名錯誤的問題,找了很久都不知道什麼原因,過陣子有空再研究一下吧!

      插曲:系統中有個頁面是模态視窗——var smd = window.showModalDialog(),這個smd不是指向模态視窗的對象而是它的傳回值,是以無法通過引用對象.close()來關閉(他殺),這時想到用setTimeout來定時檢查模态視窗的父頁面是否還在,如果不在模态視窗就自殺去吧,問題解決咯!!

  3.奇怪的方法:

  這方法是日方客戶從網上搜尋出來并規定我們使用的,為什麼說它奇怪,看下去就知道了!

      思路:打開的子頁面均有名字,關閉時先以這些名字打開視窗并擷取打開視窗的引用對象(var win=window.open("name","_blank","url")),因同名視窗隻能存在一個,是以之前打開的同名子視窗将被覆寫。然後使用打開視窗的引用對象.close(),删除所有視窗。

  優點:真的實作了該功能;

  缺點:1.能打開的子視窗數有限。名字要規定好,該項目就規定了5個,也就是說最多隻能打開5個子視窗。

     2.關閉系統時會先出現空白頁面然後它又自動關閉。如果要關閉的視窗多那也挺突兀的。

3.總結                                     

  第一節中主要是用戶端方面的優化,服務端其實還有可優化的地方,因為測試了一下發現服務端也用了2秒多,其中查資料就用了1秒多一點。第二節的第二次嘗試失敗後因客戶要求使用他們提供的方法就沒再深入研究了,這點要多多改進才行,反正學到的是自己的,多學總有好處。

  繼續努力從IT小小鳥向IT小鳥邁進!!