效能最佳化之頁面緩衝(以Javascript方式快取頁面面組件)

來源:互聯網
上載者:User

本篇文章為大家講解一個關於用戶端快取頁面面的技巧——以Javascript的方式來快取頁面面的靜態“組件”。

如果整個頁面能夠被緩衝到瀏覽器上,一個滿載HTML的巨大頁面也能運行地很棒。你可以使用Http響應緩衝頭來解決這個問題,要麼將它們手工注入你的代碼,要麼在aspx頁面上使用@OutputCache標籤來申明:

<%@ OutputCache Location=”Client” Duration=”86400” VaryByParam=”*” VaryByHeader=”*” %>
但是,瀏覽器上的這些緩衝記錄一般只能維持一天的時間。如果你有一個既有靜態內容又有動態內用的頁面,你則不能再頁面層級使用這種輸出緩衝的形式。一般來講,網站的頁面、logo、左邊的導覽功能表和頁尾都是靜態部分,參考:

中,其實只有地區二是絕對動態,地區一和地區三以及頁尾和菜單都是可以部分甚至全部緩衝!

有時候在頁面的主體地區也有很多部分不是經常變化。將所有這些合并起來的時候,會佔用相當長的下載時間。這樣,當一些有效部分從不發生變化的時候,使用者也不得不將其頁面記錄不斷地下載到本地。如果你能夠將這些靜態部分緩衝到瀏覽器上,每次頁面載入的時候你會少下載很多位元組。如果整個頁面的大小為50KB,則至少有20KB的內容是靜態,而其他30KB也許是動態。如果你能使用頁面片段的用戶端緩衝(並不是asp.net的服務端頁面的輸出緩衝),你可以很容易地節約40%的下載時間。此外,也不需要為這些靜態內容發送請求到服務端,因為它們已經緩衝到了瀏覽器上。因此,每次載入的時候,服務端不必處理這麼巨大的頁面。

Asp.net 通過使用@Outputcache的方式提供了頁面片段快取,雖然這樣確實很有好處,但是問題是該緩衝是基於服務端的。它負責返回使用者控制項的輸出並從服務端緩衝中返回。但是你不能消除下載這些位元組所付出的代價。因為它僅僅節省了伺服器上一些CPU處理能力,這對使用者來講沒有多大的益處。

對頁面部分進行緩衝僅有的方式是允許瀏覽器單獨地下載這些部分並使得這些部分就像映像、CSS或Javascript檔案一樣是緩衝的。因此,我們需要單獨地下載頁面片段並將它們緩衝到瀏覽器緩衝中。Iframe架構很容易實現這種方式,但是它會使頁面很笨拙並且也不支援適應父架構的頁面CSS樣式。在Iframe架構內部,你需要再次下載你可能要使用的其他Javascript代碼和Ajax架構。由於這些檔案是從緩衝中擷取,雖然下載速度似乎很快,但是再次下載整個架構以及大量的Javascript代碼時會增加瀏覽器的壓力。

有一種更好的辦法:使用Javascript代碼來呈現頁面內容;這樣Javascript將從瀏覽器的緩衝中獲得內容。想法如下:

1、  將整個頁面分割成很多部分。

2、  使用Javascript代碼產生頁面內容。每塊可快取的部分都由Javascript代碼控制,然後再呈現給Html

3、  瀏覽器僅緩衝可快取的部分,因此它們不會再次下載(直到使用者進行重新整理或清除緩衝為止)。頁面上那些不可快取的部分和雖然頻繁發生變化但瀏覽器沒有緩衝的部分被考慮作為頁面配置,如所示:

為典型的首頁布局,其中主體段是動態,而頁首頁尾、左菜單和logo是靜態

因為僅僅主體部分是動態,頁面的其餘部分是完全可快取的。因此,Default.aspx呈現出的整個頁面看起來類似如下代碼:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %><br /><%@ OutputCache NoStore="true" Location="None" %></p><p><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></p><p><html xmlns="http://www.w3.org/1999/xhtml"><br /><head runat="server"><br /> <title></title><br /></head><br /><body><br /> <form id="form1" runat="server"><br /> <table width="100%" border="1" style="height:450px"><br /> <tr><br /> <td>Some logo here</td><br /> <td><script id="script1" src="Header.aspx" type="text/javascript"></script></td><br /> </tr><br /> <tr><br /> <td><script id="script2" src="LeftMenu.aspx" type="text/javascript"></script></td><br /> <td bgcolor="lightgrey"><br /> <div>This is hte dynamic part which gets changed on every load.<br /> Check out the time when it was generated:<%=DateTime.Now %></div><br /> </td><br /> </tr><br /> <tr><br /> <td colspan="2"><br /> <script id="script3" src="Footer.aspx" type="text/javascript"></script><br /> </td><br /> </tr><br /> </table><br /> </form><br /></body><br /></html><br />

在瀏覽器上快取頁面面的各個部分可以消除下載靜態塊的發生。例如,頁首、左菜單和頁尾都是不會發生變化的部分,因此它們被緩衝起來以便使用者的重複訪問,但是主體部分每次使用者訪問的時候都需要從伺服器上擷取新內容。

被緩衝的部分會持續30min以上,因為瀏覽器根本不會從伺服器上下載它們,並且這也節省了大資料的傳輸耗費。僅僅主體部分才從伺服器上下載。

首次訪問的時候,頁面部分會一個接著一個地進行下載,正如你從中看到的一樣:

但是在第二次訪問的時候,僅下載Default.aspx頁面並且其他部分會立即從緩衝中進行載入。下面一份圖展示了載入頁面不同緩衝部分的執行個體。

第二次訪問時,緩衝部分的內容立即從瀏覽器緩衝中獲得。因此,整個下載位元組僅為Default.aspx頁面進行,而不是頁面上的各個小部分地區。因此,下載時間大大地減少了並且再次訪問時也非常地快。

與第一次訪問頁面上的每塊要用1S的下載時間相比,第二次訪問頁面部分地區的下載時間是5-7ms之間。這說明了再次訪問頁面緩衝部分地區時是多麼的快。

讓我們看看一個名為Header.aspx的檔案,其內容是從緩衝中獲得的。

緩衝Header.aspx頁面;注意到與一個標準的aspx頁面相比變化的僅是ContentType屬性

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Header.aspx.cs" Inherits="Header" ContentType="text/html/javascript" %><br /><%@ OutputCache Location="Client" Duration="86400" VaryByParam="*" %></p><p><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></p><p><html xmlns="http://www.w3.org/1999/xhtml"><br /><head runat="server"><br /> <title></title><br /></head><br /><body><br /> <form id="form1" runat="server"><br /> <div><br /> <h1>This is the big fat header.Lots of HTML</h1><br /> Generater on server at:<%=DateTime.Now %><br /> </div><br /> </form><br /></body><br /></html><br />

Content type被設定為text/html/javascript值,這使得你需要對頁面進行一些手工實現。

當你在某個指令碼標籤裡放置一個aspx頁面的時候,會出現錯誤。因為<script id=”script1” src=”Header.aspx” type=”type/javascript”>的預期輸出為Javascript而不是HTML內容。如果提供了HTML輸出,瀏覽器會完全地忽略它。因此,首先要滿足<script>標籤能正常工作,Header.aspx頁面必須返回Javascript代碼而不是HTML內容。其次,藉助document.writeln方法使得javascript呈現Header.aspx頁面的HTML輸出。

一個Http Module會截獲所有對.aspx頁面的調用請求。當某個頁面準備發送到瀏覽器端的時候,會檢查content type是否為text/html/javascript。如果是,然後將頁面輸出轉換為一個類似於javascript表現。

我們先建立一個名為Html2JSPageFilter.cs的響應過濾器檔案對響應流的Write方法進行重寫,並將頁面的HTML轉換為Javascript表示。因此,asp.net為你產生Html,並且你將它轉換為Javascript表示用來在瀏覽器上呈現源Html。

HttpResponse的Filter屬性

這裡我們將使用到HttpResponse的Filter屬性,這是一個非常有用但很少被使用的屬性。MSDN對其的解釋是——擷取或設定一個封裝的篩選器對象以在傳輸到用戶端之前修改Http實體內容。也就是說,你可以給你的每個頁面輸出設定你自己的篩選器。HttpResponse將發送所有的內容到篩選器

使用Http Module

你也許在想,是否可以使用一個Http handler來處理這個問題。例如,你需要使用asp.net的預設頁面handler來截獲發送所有以*.aspx為副檔名檔案的調用請求。但是你不能將相同的副檔名的檔案註冊為另一個handler。本方案中,你需要使用HttpModule,它能截獲任何一個來自asp.net管道的請求。為了實現這個功能,你需要:

1、  以Html的方式返回整個頁面輸出

2、  過濾包含在<form>標籤內的內容。Asp.net總是會產生一個<form>標籤,並且頁面上的內容僅在該標籤內部時才是可用的

下面的代碼從aspx頁面返回產生的HTML內容,並從<form>標籤中解析出內容

public override void Write(byte[] buffer, int offset, int count)<br /> {<br /> string strBuffer = System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count);<br /> //--------------------------------<br /> //Wait for the colsing </html> tag<br /> //--------------------------------<br /> Regex eof = new Regex("</html>", RegexOptions.IgnoreCase);</p><p> if (!eof.IsMatch(strBuffer))<br /> {<br /> responseHtml.Append(strBuffer);<br /> }<br /> else<br /> {<br /> responseHtml.Append(strBuffer);<br /> string finalHtml = responseHtml.ToString();</p><p> int formTagStart = finalHtml.IndexOf("<form");<br /> int formTagStartEnd = finalHtml.IndexOf('>', formTagStart);<br /> int formTagEnd = finalHtml.LastIndexOf("</form>");</p><p> string pageContentInsideFormTag = finalHtml.Substring(formTagStartEnd + 1, formTagEnd - formTagStartEnd - 1);

3、  移出隱藏欄位的ViewState,否則它會與Default.aspx頁面上的Viewstate發生衝突(預設頁面有它自己的Viewstate)。因此,包含Viewstate的<input>標籤不能再次發送到瀏覽器端。這意味著你不能使用帶Viewstate的控制項,雖然它是實現這種方法的捷徑。通常來說,緩衝部分存放的都是靜態內容,因此不管怎樣,需要Viewstate在這裡是不應該的。

下面的代碼移出<input>欄位的Viewstate,以避免它和Default.aspx頁面的Viewstate發生衝突:

Regex re = new Regex("(<input.*?__VIEWSTATE.*?/>)", RegexOptions.IgnoreCase);</p><p>pageContentInsideFormTag = re.Replace(pageContentInsideFormTag, string.Empty);

4、  將整個HTML輸出轉換為Javascript字串格式。該字串包含一個未格式化的HTML,可以作為innerHTML使用或者在document.write(‘’)語句的方法裡是使用

下面的代碼將HTML輸出轉換為一個Javascript字串表示並且消除新的行、空格、省略符號,等。可將這一的字串設定為一個元素的innerHTML值或將它傳遞給document方法:

string javascript2html = pageContentInsideFormTag.Replace("\r", "")<br /> .Replace("\n", "")<br /> .Replace(" ", " ")<br /> .Replace(" ", " ")<br /> .Replace(" ", " ")<br /> .Replace("\\", "\\\\")<br /> .Replace("'", "\\'");

5、  使用document.write方法,它能夠將javascript字串輸出到瀏覽器上。HTML被直接添加到頁面內容

產生一個document.write的語句,用來將Html輸出到瀏覽器上:

string pageOutput = "document.write('"+javascript2html+"');";<br />byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(pageOutput);</p><p>responseStream.Write(data, 0, data.Length);

這是一個非常完美的騙局。先使用一個響應過濾器來返回.aspx檔案的輸出,然後將它轉換為Javascript表示。使用document.write方法將HTML在瀏覽器的DOM中呈現並獲得該Javascript代碼的緩衝。為了方便,這裡使用一個HttpModule對象來掛鈎asp.net管道,並等待.aspx檔案發送text/html/javascript形式的內容。然後再掛鈎該響應過濾器到asp.net請求管道。

 

註冊HttpModule

HttpModule對象非常簡單。它會掛鈎內容相關的ReleaseRequestState事件,當頁面輸出準備發送到瀏覽器的時候會啟用該事件。在該事件的控制代碼中,調用響應過濾器來將HTML轉換為Javascript表示的格式

下面是實現代碼:

public void Init(HttpApplication context)<br /> {<br /> context.ReleaseRequestState += new EventHandler(InstallResponseFilter);<br /> }</p><p> private void InstallResponseFilter(object sender, EventArgs e)<br /> {<br /> HttpResponse response = HttpContext.Current.Response;</p><p> if (response.ContentType=="text/html/javascript")<br /> {<br /> response.ContentType = "text/javascript";<br /> response.Filter = new Html2JSPageFilter(response.Filter);<br /> }<br /> }

最後,通過在web.config檔案中添加一個入口節點<httpModule>來進行註冊

<httpModules><br /><add name="Html2JSModule" type="Html2JSModule"/><br /></httpModules>

你可以在你的.aspx檔案中使用這種方法在用戶端充分節省使用者的下載時間。雖然首次訪問時會稍微增加下載時間——每塊指令碼標籤在網路上的平均往返時間大約是200ms,但是再次訪問時會更加輕鬆。你自己留意一下這些不同的效能問題:登陸www.pageflakes.com網站並讓整個網站完成載入。然後,關閉瀏覽器,再開啟它,並再次輸入URL地址。可以看到再次訪問速度是多麼地快。沒錯,它們使用的就是這種技巧!你會看到與第一次訪問發送的大約400KB的資料相比,第二次訪問時僅僅發送了10-12KB的資料。這是因為所有頁面片段都被緩衝到瀏覽器中了,只要緩衝沒有到期,後續的使用者訪問網站時並不需要多少下載時間.

原始碼下載

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.