前端性能優化(2)

一.上文回顧

上回我們主要從圖片的合并、壓縮等方面介紹前端性能優化問題(詳見Java Web 前端高性能
優化(一)

本次我們主要從圖像BASE64 編碼、GZIP壓縮、懶加載與預加載以及?OneAPM?Browser

Insight 的定位分析功能四個方面介紹前端優化方法

二.圖像的 BASE64 編碼

不管如何,圖片的下載始終都要向服務器發出請求,要是圖片的下載不用向服務器發出請求,

而可以隨著 HTML 的下載同時下載到本地那就太好了。而目前,瀏覽器已經支持了該特性,

我們可以將圖片數據編碼成 BASE64 的字符串,使用該字符串代替圖像地址。假設用 S代表

這個 BASE64 字符串,那么就可以使用<img src="data:image/png;base64,S">?來顯示

這個圖像??梢钥闯觯瑘D像的數據包含在了 HTML 代碼里,無需再次訪問服務器。那么圖像

要如何編碼成 BASE64 字符串呢?可以使用 在線的工具---“Base64 Online”,這個工具可以上

傳圖片將圖片轉換為 BASE64 字符串。當然,如果讀者有興趣,完全可以自己實現一個

BASE64 編碼工具,比如使用 Java 開發,它的代碼就如清單 1 所示。

清單 1. BASE64 的 Java 代碼

 public static String getPicBASE64(String picPath) {           String content = null;           try {               FileInputStream fis = new FileInputStream(picPath);               byte[] bytes = new byte[fis.available()];               fis.read(bytes);               content = new sun.misc.BASE64Encoder().encode(bytes); // 具體的編碼方法               fis.close();             } catch (Exception e) {               e.printStackTrace();           }           return content;    }

本文編碼了一個圖像,并且將編碼獲得的 BASE64 字符串,寫到了 HTML 之中,如下清單 2

所示。

清單 2. 嵌入 BASE64 的測試 HTML 代碼

 <html>  <body>  <img src="data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAeQAAAB8BAMAAABKwt5QAAAAA3NCSVQICAjb4U/gAAAAGFBMVEX/  ……(省略了大部分編碼)… BJRU5ErkJggg==">  </body>  </html>

由于圖片數據包含在了 BASE64 字符串中,因此無需向服務器請求圖像數據,結果顯示如下

圖所示。

圖 1. BASE64 顯示圖像

然而這種策略并不能濫用,它適用的情況是瀏覽器連接服務器的時間 > 圖片下載時間,也就

是發起連接的代價要大于圖片下載,那么這個時候將圖片編碼為 BASE64 字符串,就可以避

免連接的建立,提高效率。如果圖片較大的話,使用 BASE64 編碼雖然可以避免連接建立,

但是相對于圖像下載,請求的建立只占很小的比例,如果用 BASE64,對于動態網頁來說圖像

緩存就會失效(靜態網頁可以緩存),而且 BASE64 字符串的總大小要大于純圖片的大小,

這樣一算就非常不合適了。因此,如果你的頁面已經靜態化,圖像又不是非常大,可以嘗試

BASE64 編碼,客戶端會將網頁內容和圖片的 BASE64 編碼一起緩存;而如果你的頁面是動

態頁面,圖像還較大,每次都要下載 BASE64 字符串,那么就不能用 BASE64 編碼圖像,而

正常引用圖像,從而使用到瀏覽器的圖像緩存,提高下載速度。從現實我們接觸的角度看,如

一些在線 HTML 編輯器,里面的小圖標,如笑臉等,都使用到了 BASE64 編碼,因為它們非

常小,數量多,BASE64 可以幫助網頁減少圖標的請求數,提高效率。

三.Browser Insight 定位分析

作為一個網站的前端運維人員或者優化人員,大多數情況下并不一定要注重每一位用戶的訪問

情況,只要大部分用戶訪問網站的時候處于一個滿意的程度就可以了?,F在大多數前端性能優

化工具往往注重的是某個時間段內的頁面平均響應時間,這就造成可能因為某個用戶偶然性的

網絡卡頓而延長整個時間段內的頁面加載時間。

前一段時間發現OneAPM的Browser Insight 推出了定位分析功能,可以從響應時間分布來查

看用戶的整體響應分布,并可以針對不同時間分布內的用戶確定影響其響應時間的因素。

圖 2.Browser Insight 定位分析

粘貼的圖像1247x619 97.3 KB

四.GZIP 壓縮

為了減少傳輸的數據,壓縮是一個不錯的選擇,而 HTTP 協議支持 GZIP 的壓縮格式,服務

器響應的報頭包含 Content-Encoding: gzip,它告訴瀏覽器,這個響應的返回數據,已經壓縮

成 GZIP 格式,瀏覽器獲得數據后要進行解壓縮操作。這在一定程度可以減少服務器傳輸的數

據,提高系統性能。那么如何給服務器響應添加 Content-Encoding: gzip 報頭,同時壓縮響

應數據呢?如果你用的是 Tomcat 服務器,打開 $tomcat_home$/conf/server.xml 文件,對

Connector 進行配置,配置如清單 3 所示。

清單 3. TOMCAT 配置清單

 <Connector  port ="80"  maxHttpHeaderSize ="8192"  maxThreads ="150"  minSpareThreads ="25"  maxSpareThreads ="75"  enableLookups ="false"  redirectPort ="8443"  acceptCount ="100"  connectionTimeout ="20000"  disableUploadTimeout ="true"  URIEncoding ="utf-8"    compression="on"  compressionMinSize="2048"  noCompressionUserAgents="gozilla, traviata"  compressableMimeType="text/html,text/xml" />

我們為 Connector 添加了如下幾個屬性,他們意義分別是:

compression="on" 打開壓縮功能

compressionMinSize="2048" 啟用壓縮的輸出內容大小,這里面默認為 2KB

noCompressionUserAgents="gozilla, traviata" 對于以下的瀏覽器,不啟用壓縮

compressableMimeType="text/html,text/xml, image/png" 壓縮類型

有時候,我們無法配置 server.xml,比如如果我們只是租用了別人的空間,但是它并沒有啟用

GZIP,那么我們就要使用程序啟用 GZIP 功能。我們將需要壓縮的文件,放到指定的文件

夾,使用一個過濾器,過濾對這個文件夾里文件的請求。

清單 4. 自定義 Filter 壓縮 GZIP

 // 監視對 gzipCategory 文件夾的請求 @WebFilter(urlPatterns = { "/gzipCategory/*" })  public class GZIPFilter implements Filter {  @Override  public void doFilter(ServletRequest request, ServletResponse response,  FilterChain chain) throws IOException, ServletException {  String parameter = request.getParameter("gzip");  // 判斷是否包含了 Accept-Encoding 請求頭部 HttpServletRequest s = (HttpServletRequest)request;  String header = s.getHeader("Accept-Encoding");  //"1".equals(parameter) 只是為了控制,如果傳入 gzip=1,才執行壓縮,目的是測試用 if ("1".equals(parameter) && header != null && header.toLowerCase().contains("gzip")) {  HttpServletResponse resp = (HttpServletResponse) response;  final ByteArrayOutputStream buffer = new ByteArrayOutputStream();  HttpServletResponseWrapper hsrw = new HttpServletResponseWrapper(  resp) {  @Override  public PrintWriter getWriter() throws IOException {  return new PrintWriter(new OutputStreamWriter(buffer,  getCharacterEncoding()));  }  @Override  public ServletOutputStream getOutputStream() throws IOException {  return new ServletOutputStream() {  @Override  public void write(int b) throws IOException {  buffer.write(b);  }  };  }  };  chain.doFilter(request, hsrw);  byte[] gzipData = gzip(buffer.toByteArray());  resp.addHeader("Content-Encoding", "gzip");  resp.setContentLength(gzipData.length);  ServletOutputStream output = response.getOutputStream();  output.write(gzipData);  output.flush();  } else {  chain.doFilter(request, response);  }  }  // 用 GZIP 壓縮字節數組 private byte[] gzip(byte[] data) {  ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(10240);  GZIPOutputStream output = null;  try {  output = new GZIPOutputStream(byteOutput);  output.write(data);  } catch (IOException e) {  } finally {  try {  output.close();  } catch (IOException e) {  }  }  return byteOutput.toByteArray();  } …… }

該程序的主體思想,是在響應流寫回之前,對響應的字節數據進行 GZIP 壓縮,因為并不是所

有的瀏覽器都支持 GZIP 解壓縮,如果瀏覽器支持 GZIP 解壓縮,會在請求報頭的

Accept-Encoding 里包含 gzip。這是告訴服務器瀏覽器支持 GZIP 解壓縮,因此如果用程序控

制壓縮,為了保險起見,還需要判斷瀏覽器是否發送 accept-encoding: gzip 報頭,如果包含

了該報頭,才執行壓縮。為了驗證壓縮前后的情況,使用 Firebug 監控請求和響應報頭。

清單 5. 壓縮前請求

 GET /testProject/gzipCategory/test.html HTTP/1.1  Accept: */*  Accept-Language: zh-cn  Accept-Encoding: gzip, deflate  User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)  Host: localhost:9090  Connection: Keep-Alive

清單 6. 不壓縮的響應

 HTTP/1.1 200 OK  Server: Apache-Coyote/1.1  ETag: W/"5060-1242444154000" Last-Modified: Sat, 16 May 2009 03:22:34 GMT  Content-Type: text/html  Content-Length: 5060 Date: Mon, 18 May 2009 12:29:49 GMT

清單 7. 壓縮后的響應

 HTTP/1.1 200 OK  Server: Apache-Coyote/1.1  ETag: W/"5060-1242444154000" Last-Modified: Sat, 16 May 2009 03:22:34 GMT  Content-Encoding: gzip Content-Type: text/html  Content-Length: 837 Date: Mon, 18 May 2009 12:27:33 GMT

可以看到,壓縮后的數據比壓縮前數據小了很多。壓縮后的響應報頭包含 Content-Encoding:

gzip。同時 Content-Length 包含了返回數據的大小。GZIP 壓縮是一個重要的功能,前面

提到的是對單一服務器的壓縮優化,在高并發的情況,多個 Tomcat 服務器之前,需要采用

反向代理的技術,提高并發度,而目前比較火的反向代理是 Nginx(這在后續的文章會進行

詳細的介紹)。對 Nginx 的 HTTP 配置部分里增加如下配置。

清單 8. Nginx 的 GZIP 配置

 gzip  on;  gzip_min_length  1000;  gzip_buffers     4 8k;  gzip_types       text/plain application/x-javascript text/css text/html application/xml;

由于 Nginx 具有更高的性能,利用該配置可以更好的提高性能。在高性能服務器上該配置將

非常有用。

五.懶加載與預加載

預加載和懶加載,是一種改善用戶體驗的策略,它實際上并不能提高程序性能,但是卻可以明

顯改善用戶體驗或減輕服務器壓力。

預加載原理是在用戶查看一張圖片時,就將下一張圖片先下載到本地,而當用戶真正訪問下一

張圖片時,由于本地緩存的原因,無需從服務器端下載,從而達到提高用戶體驗的目的。為了

實現預加載,我們可以實現如下的一個函數。

清單 9. 預加載函數

 function  preload(callback) {   var imageObj = new Image();  images = new Array();  images[0]="pre_image1.jpg";  images[1]=" pre_image2.jpg";  images[2]=" pre_image3.jpg";  for(var i=0; i<=2; i++) {    imageObj.src=images[i];   if (imageObj.complete) { // 如果圖片已經存在于瀏覽器緩存,直接調用回調函數      callback.call(imageObj);    } else {  imageObj.onload = function () {// 圖片下載完畢時異步調用 callback 函數        callback.call(imageObj);// 將回調函數的 this 替換為 Image 對象    };  }  }  }  function callback()  {   alert(this.src + “已經加載完畢 , 可以在這里繼續預加載下一組圖片”);  }

上面的代碼,首先定義了 Image 對象,并且聲明了需要預加載的圖像數組,然后逐一的開始

加載(.src=images[i])。如果已經在緩存里,則不做其他處理;如果不在緩存,監聽 onload

事件,它會在圖片加載完畢時調用。

而懶加載則是在用戶需要的時候再加載。當一個網頁中可能同時有上百張圖片,而大部分情況

下,用戶只看其中的一部分,如果同時顯示上百張,則浪費了大量帶寬資源,因此可以當用戶

往下拉動滾動條時,才去請求下載被查看的圖像,這個原理與 word 的顯示策略非常類似。

在 JavaScript 中,它的基本原理是首先要有一個容器對象,容器里面是 img 元素集合。用隱

藏或替換等方法,停止 img 的加載,也就是停止它去下載圖像。然后歷遍 img 元素,當元素

在加載范圍內,再進行加載(也就是顯示或插入 img 標簽)。加載范圍一般是容器的視框范

圍,即瀏覽者的視覺范圍內。當容器滾動或大小改變時,再重新歷遍元素判斷。如此重復,直

到所有元素都加載后就完成。當然對于開發來講,選擇已有的成熟組件,并不失為一個上策

,Lazy Load Plugin for jQuery 是基于 JQuery 的懶加載組件,它有自己的官方網站4。這是一

個不錯的免費插件??梢詭椭绦騿T快速的開發懶加載應用。

小結

Java Web 前端高性能優化(一)、(二)總結了前端性能問題定位以及圖片優化的幾種方式,

將它們歸結起來,在讀者需要的時候,可以查看本文的內容,相信按照本文的方法,可以輔助

讀者進行前端的高性能優化。

:本文轉載自 IBM 社區,由 OneAPM 產品運營編輯整理,原文鏈接為:

http://www.ibm.com/developerworks/cn/java/j-lo-javawebhiperf1/#icomments

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一.上文回顧 上回我們主要從圖片的合并、壓縮等方面介紹前端性能優化問題(詳見Java Web 前端高性能優化(一)...
    OneAPM閱讀 828評論 0 4
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,947評論 18 139
  • Yahoo!的Exceptional Performance團隊為改善Web性能帶來最佳實踐。他們為此進行了一系列...
    拉風的老衲閱讀 1,874評論 0 1
  • 今天一個學生被叫到了辦公室,他只穿了一件夾克。年僅6旬的班主任老師帶著憤怒地嚷到:“怎么把數學老師惹生氣了?去進班...
    昌悟道閱讀 302評論 0 0
  • 成都的那一男一女兩個司機,迅速成了全國最著名的兩個司機。許多馬路上也就只有兩個司機,一個是頑劣成性、隨心所欲的司機...
    小好閱讀 422評論 0 2