不管是第一次使用 FineUI 控件庫的網友,還是有着 3 年以上使用經驗的網友,都對 FineUI 的簡單印象深刻。當然,“一切為了簡單”也是 FineUI 一句響亮的口號,不僅如此,一個開源項目要想立足並長久發展下去,光憑着簡單是不行,還要有自己的特色,這個特色就是創新。
從 FineUI 的官網(http://fineui.com/)我們明顯看到 FineUI 的三個大特性“一切為了簡單”、“用心實現80%的功能”、“創新所以獨一無二”:
而今天要講的就是“創新”的范疇。
FineUI 的 XState 機制
我們都知道 ViewState 是 ASP.NET WebForms 的一個重要的基礎,用來在頁面回發過程中維護控件的服務器端狀態,這樣我們就能方便的在回發事件處理函數中隨意使用控件屬性了,比如下面代碼:
1: Label1.Text = TextBox1.Text;
但是在 AJAX 的應用環境中,ViewState 會代碼下載數據的冗余,所以 FineUI 很早就在其內部實現了適合 AJAX 的 XState 機制,從而減少下載數據的冗余,加快頁面的呈現速度。
但是請注意,FineUI 使用 XState 並非啟用 ViewState,傳統的 ASP.NET 控件仍然使用 ViewState,大家也仍然可以在 ViewState 中保存少量的數據,比如如下代碼依然有效:
1: if (ViewState["BindGrid1"] != null && Convert.ToBoolean(ViewState["BindGrid1"]))
2: {
3: BindGrid();
4: ViewState["BindGrid1"] = false;
5: }
之前有一篇詳細的文章來對比 ViewState 和 XState 對使用 FineUI 控件庫的印象:http://www.cnblogs.com/sanshi/archive/2013/01/08/2850459.html
這里我們簡單把結論列一下:
對於如下這個頁面,我們分別比較頁面第一次加載和點擊按鈕回發兩個過程中下載的數據量:
使用 ViewState 的版本: 第一次頁面加載下載數據(5068 bytes) 點擊按鈕回發下載數據(1251 bytes)
使用 XState 的版本: 第一次頁面加載下載數據(4922 bytes) 點擊按鈕回發下載數據(709 bytes)
可見,XState 帶來了一個很大的優勢,那就是減少數據的下載量。
現在,開始關心數據上傳量
之前一直很關心數據下載量,而忽視了數據上傳量。其實我們所處的網絡環境是一個上行下行不對稱的網絡,一般上行速度是下行速度的 1/2,甚至更少。來引用一篇文章中的一段:
Why Upload and Download Speeds Differ
Upload speed is usually slower than download speed because Internet providers have designed their systems to optimize download speeds. This is because most Internet users spend more time downloading than uploading. In other words, Internet providers give priority to downloading since it's more frequently done than uploading.
換句話說,因為人們對下載的需求更旺盛,所以網絡提供商一般會分配給下載更高的傳輸速度。
我一直沒有向這個方向考慮,直到有論壇網友提出了這個問題:http://fineui.com/bbs/forum.php?mod=viewthread&tid=3166
看到這篇帖子的時候,我就意識到了,FineUI 是做出改變的時候了!
因為我們需要在服務器和客戶端之前持久化數據,對於這個網友的例子而言,就是表格中那些已經渲染后的HTML代碼。通用的辦法就是對客戶端提交的數據進行壓縮,但是在客戶端使用 JavaScript 實現類似 Gzip 的壓縮簡直就是天方夜譚(瀏覽器性能受不了)!
怎么辦?
均衡下載量和上傳量
既然無法在客戶端進行壓縮,就只好在服務器端進行壓縮了。所以思路如下:
1. 在頁面加載時在服務器端壓縮需要持久化的狀態,並寫入頁面中;
2. 下載 AJAX 提交時,提交壓縮后的持久化數據;
3. 在 AJAX 提交過程中,重新生成新的壓縮后的持久化數據,並寫入頁面中;
4. 如此下去….
基於如下幾點考慮,這部分新增的壓縮數據不會對下載量造成很大的影響:
1. 下行帶寬一般都比較充足;
2. 經過壓縮后的持久化數據一般比較小;
3. 下載數據一般都會經過 GZIP 壓縮。
具體到 FineUI 中的實現,也很簡單,首先定義 GZIP 壓縮和解壓函數:
1: public static string Gzipped(string source)
2: {
3: using (var outStream = new MemoryStream())
4: {
5: using (var gzipStream = new GZipStream(outStream, CompressionMode.Compress))
6: {
7: using (var mStream = new MemoryStream(Encoding.UTF8.GetBytes(source)))
8: {
9: mStream.WriteTo(gzipStream);
10: }
11: }
12:
13: return StringUtil.EncodeTo64(outStream.ToArray());
14: }
15: }
16:
17: public static string Ungzipped(string source)
18: {
19: byte[] bytes = Convert.FromBase64String(source);
20:
21: using (GZipStream stream = new GZipStream(new MemoryStream(bytes), CompressionMode.Decompress))
22: {
23: const int size = 512;
24: byte[] buffer = new byte[size];
25: using (MemoryStream memory = new MemoryStream())
26: {
27: int count = 0;
28: do
29: {
30: count = stream.Read(buffer, 0, size);
31: if (count > 0)
32: {
33: memory.Write(buffer, 0, count);
34: }
35: } while (count > 0);
36:
37: return System.Text.Encoding.UTF8.GetString(memory.ToArray());
38: }
39: }
40: }
然后為控件基類添加一個需要對哪些屬性進行 GZIP 壓縮的屬性:
1: private List<string> _gzippedAjaxProperties = new List<string>();
2:
3: internal List<string> GzippedAjaxProperties
4: {
5: get { return _gzippedAjaxProperties; }
6: set { _gzippedAjaxProperties = value; }
7: }
最后,在頁面第一次加載和 AJAX 過程中壓縮這個屬性:
1: bool propertyGzippped = _gzippedAjaxProperties.Contains(property);
2: string propertyGzippedValue = String.Empty;
3:
4: object propertyValue = GetPropertyJSONValue(property);
5:
6: JToken tokenValue = propertyValue as JToken;
7: jo.Add(property, tokenValue);
8:
9: if (propertyGzippped)
10: {
11: propertyGzippedValue = tokenValue.ToString(Newtonsoft.Json.Formatting.None);
12: }
13:
14: if (propertyGzippped && !String.IsNullOrEmpty(propertyGzippedValue))
15: {
16: jo.Add(property + "_GZ", StringUtil.Gzipped( propertyGzippedValue));
17: }
測試優化結果
為了進行測試,我們創建了一個表格頁面,頁面效果如下:
這個頁面總共有 22 行數據,我們來比較頁面第一次加載和點擊“選中了哪些行”兩個操作在優化前后的結果。
優化前,頁面第一次加載(UP:0 DOWN:30684):
優化前,頁面回發(UP:33202 DOWN:186):
優化后,頁面第一次加載(UP:0 DOWN:34366):
優化后,頁面回發(UP:6016 DOWN:186):
經過優化后,頁面第一次加載時下載數據由 30684 bytes 增加為 34366 bytes,增加了 12%,而這個增加量不會對下載造成多大的影響。
而上傳量卻有大幅減少,AJAX 回發時上傳數據由原來的 33202 bytes 減少為 6016,減少了 82%,對於上行帶寬不足的現實,這個改變帶來的影響確實巨大的!
結論
優化后的 FineUI 控件庫會稍微增加下載數據量,但是考慮到下行帶寬一般比較充裕,並且下行數據一般會經過 GZIP 壓縮,所以這個改變影響不大。現實的好處是上傳數據大幅減少了 80%,這個改變帶來的影響確實巨大的!
注:這個特性會加 FineUI 下個版本中去(FineUI v3.3.1)。
喜歡這篇文章,就不要忘記點擊頁面右下角的【推薦】按鈕哦。