向服務器請求數據的五種技術


   Ajax,在它最基本的層面,是一種與服務器通訊而不重載當前頁面的方法,數據可從服務器獲得或發送給服務器。有多種不同的方法構造這種通訊通道,每種方法都有自己的優勢和限制。
      有五種常用技術用於向服務器請求數據:
      (1)XMLHttpRequest (XHR)
      (2)動態腳本標簽插入
      (3)框架
      (4)Comet
      (5)多部分的XHR
      在現代高性能JavaScript中使用的三種技術是XHR,動態腳本標簽插入和多部分的XHR。使用Comet和iframe(作為數據傳輸技術)往往是極限情況,不在這里討論。

      一、XMLHttpRequest
      目前最常用的方法中,XMLHttpRequest(XHR)用來異步收發數據。所有現代瀏覽器都能夠很好地支持它,而且能夠精細地控制發送請求和數據接收。你可以向請求報文中添加任意的頭信息和參數(包括GET和POST),並讀取從服務器返回的頭信息,以及響應文本自身。以下是使用示例:
      var url = '/data.php';
      var params = [
            'id=934875',
            'limit=20'
      ];
      var req = new XMLHttpRequest();
      req.onreadystatechange = function() {
            if (req.readyState=== 4) {
                  var responseHeaders = req.getAllResponseHeaders();
                  var data = req.responseText;
            }
      }
      req.open('GET', url + '?' + params.join('&'), true);
      req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
      req.send(null);
      此例顯示了如何從URL請求數據,使用參數,以及如何讀取響應報文和頭信息。readyState等於4表示整個響應報文已經收並完可用於操作。
      readyState等於3則表示此時正在與服務器交互,響應報文還在傳輸之中。這就是所謂的“流”,它是提高數據請求性能的強大工具:
      req.onreadystatechange = function() {
            if (req.readyState=== 3) {
                  var dataSoFar = req.responseText;
                  …
            }
            else if (req.readyState=== 4) {
                  var data = req.responseText;
                  …
            }
      }
      由於XHR提供了高級別的控制,瀏覽器在上面增加了一些限制。你不能使用XHR從當前運行的代碼域之外請求數據,而且老版本的IE 也不提供readyState3,它不支持流。從請求返回的數據像一個字符串或者一個XML對象那樣對待,這意味着處理大量數據將相當緩慢。
盡管有這些缺點,XHR仍舊是最常用的請求數據技術,也是最強大的,它應當成為你的首選。
      當使用XHR請求數據時,你可以選擇POST 或GET。如果請求不改變服務器狀態只是取回數據(又稱作冪等動作)則使用GET。GET請求被緩沖起來,如果你多次提取相同的數據可提高性能。
      只有當URL和參數的長度超過了2'048個字符時才使用POST提取數據。因為Internet Explorer限制URL的長度,過長將導致請求(參數)被截斷。
      二、動態腳本標簽插入
      該技術克服了XHR的最大限制:它可以從不同域的服務器上獲取數據。這是一種黑客技術,而不是實例化一個專用對象,你用JavaScript創建了一個新腳本標簽,並將它的源屬性設置為一個指向不同域的URL。
      var scriptElement = document.createElement('script');
      scriptElement.src = 'http://any-domain.com/javascript/lib.js';
      document.getElementsByTagName_r('head')[0].appendChild(scriptElement);
      但是動態腳本標簽插入與XHR相比只提供更少的控制。你不能通過請求發送信息頭。參數只能通過GET方法傳遞,不能用POST。你不能設置請求的超時或重試,實際上,你不需要知道它是否失敗了。你必須等待所有數據返回之后才可以訪問它們。你不能訪問響應信息頭或者像訪問字符串那樣訪問整個響應報文。
      最后一點非常重要。因為響應報文被用作腳本標簽的源碼,它必須是可執行的JavaScript。你不能使用裸XML,或者裸JSON,任何數據,無論什么格式,必須在一個回調函數之中被組裝起來。
      var scriptElement = document.createElement('script');
      scriptElement.src = 'http://any-domain.com/javascript/lib.js';
      document.getElementsByTagName_r('head')[0].appendChild(scriptElement);
      function jsonCallback(jsonString) {
            var data = ('(' + jsonString + ')');
      }
      在這個例子中,lib.js 文件將調用jsonCallback 函數組裝數據:
      jsonCallback({ "status": 1, "colors": [ "#fff", "#000", "#ff0000" ] });
      盡管有這些限制,此技術仍然非常迅速。其響應結果是運行JavaScript,而不是作為字符串必須被進一步處理。正因為如此,它可能是客戶端上獲取並解析數據最快的方法。我們比較了動態腳本標簽插入和XHR的性能,在本章后面JSON 一節中。
      請小心使用這種技術從你不能直接控制的服務器上請求數據。JavaScript沒有權限或訪問控制的概念,所以你的頁面上任何使用動態腳本標簽插入的代碼都可以完全控制整個頁面。包括修改任何內容、將用戶重定向到另一個站點,或跟蹤他們在頁面上的操作並將數據發送給第三方。使用外部來源的代碼時務必非常小心。
      三、多部分XHR
      多部分XHR(MXHR)允許你只用一個HTTP 請求就可以從服務器端獲取多個資源。它通過將資源(可以是CSS 文件,HTML 片段,JavaScript代碼,或base64 編碼的圖片)打包成一個由特定分隔符界定的大字符串,從服務器端發送到客戶端。JavaScript代碼處理此長字符串,根據它的媒體類型和其他“信息頭”解析出每個資源。
      讓我們從頭到尾跟隨這個過程。首先,發送一個請求向服務器索取幾個圖像資源:
      var req = new XMLHttpRequest();
      req.open('GET', 'rollup_images.php', true);
      req.onreadystatechange = function() {
            if (req.readyState== 4) {
                  splitImages(req.responseText);
            }
      };
      req.send(null);
      這是一個非常簡單的請求。你向rollup_images.php 要求數據,一旦你收到返回結果,就將它交給函數splitImages處理。
      下一步,服務器讀取圖片並將它們轉換為字符串:
      $images = array('kitten.jpg', 'sunset.jpg', 'baby.jpg');
      foreach ($images as $image) {
            $image_fh = fopen($image, 'r');
            $image_data = fread($image_fh, filesize($image));
            fclose($image_fh);
            $payloads[] = base64_encode($image_data);
      }
      $newline = chr(1);
      echo implode($newline, $payloads);
      這段PHP代碼讀取三個圖片,並將它們轉換成base64字符串。它們之間用一個簡單的字符,UNICODE的1,連接起來,然后返回給客戶端。
      然后回到客戶端,此數據由splitImage 函數處理:
      function splitImages(imageString) {
            var imageData = imageString.split("\u0001");
            var imageElement;
            for (var i = 0, len = imageData.length; i < len; i++) {
                  imageElement = document.createElement('img');
                  imageElement.src = 'data:image/jpeg;base64,' + imageData[i];
                  document.getElementById('container').appendChild(imageElement);
            }
      }
      此函數將拼接而成的字符串分解為三段。每段用於創建一個圖像元素,然后將圖像元素插入頁面中。圖像不是從base64 轉換成二進制,而是使用data:URL 並指定image/jpeg 媒體類型。
      最終結果是:在一次HTTP 請求中向瀏覽器傳入了三張圖片。也可以傳入20 張或100 張,響應報文會更大,但也只是一次HTTP 請求。它也可以擴展至其他類型的資源。JavaScript文件,CSS 文件,HTML片段,許多類型的圖片都可以合並成一次響應。任何數據類型都可作為一個JavaScript處理的字符串被發送。下面的函數用於將JavaScript代碼、CSS 樣式表和圖片轉換為瀏覽器可用的資源:
      function handleImageData(data, mimeType) {
            var img = document.createElement('img');
            img.src = 'data:' + mimeType + ';base64,' + data;
            return img;
      }
      function handleCss(data) {
            var style = document.createElement('style');
            style.type = 'text/css';
            var node = document.createTextNode(data);
            style.appendChild(node);
            document.getElementsByTagName_r('head')[0].appendChild(style);
      }
      function handleJavaScript(data) {
            (data);
      }
      由於MXHR響應報文越來越大,有必要在每個資源收到時立刻處理,而不是等待整個響應報文接收完成。這可以通過監聽readyState3 實現:
      var req = new XMLHttpRequest();
      var getLatestPacketInterval, lastLength = 0;
      req.open('GET', 'rollup_images.php', true);
      req.onreadystatechange = readyStateHandler;
      req.send(null);
      function readyStateHandler{
            if (req.readyState=== 3 && getLatestPacketInterval === null) {
                  getLatestPacketInterval = window.setInterval(function() {
                        getLatestPacket();
                  }, 15);
            }
            if (req.readyState=== 4) {
                  clearInterval(getLatestPacketInterval);
                  getLatestPacket();
            }
      }
      function getLatestPacket() {
            var length = req.responseText.length;
            var packet = req.responseText.substring(lastLength, length);
            processPacket(packet);
            lastLength = length;
      }
      當readyState3第一次發出時,啟動了一個定時器。每隔15毫秒檢查一次響應報文中的新數據。數據片段被收集起來直到發現一個分隔符,然后一切都作為一個完整的資源處理。以健壯的方式使用MXHR的代碼很復雜但值得進一步研究。
      使用此技術有一些缺點,其中最大的缺點是以此方法獲得的資源不能被瀏覽器緩存。如果你使用MXHR獲取一個特定的CSS 文件然后在下一個頁面中正常加載它,它不在緩存中。因為整批資源是作為一個長字符串傳輸的,然后由JavaScript代碼分割。由於沒有辦法用程序將文件放入瀏覽器緩存中,所以用這種方法獲取的資源也無法存放在那里。
      另一個缺點是:老版本的Internet Explorer不支持readyState3或data: URL。Internet Explorer 8兩個都支持,但在Internet Explorer 6和7中必須設法變通。
      盡管有這些缺點,但某些情況下MXHR仍然顯著提高了整體頁面的性能:網頁包含許多其他地方不會用到的資源(所以不需要緩存),尤其是圖片。
      網站為每個頁面使用了獨一無二的打包的JavaScript或CSS文件以減少HTTP請求,因為它們對每個頁面來說是獨一的,所以不需要從緩存中讀取,除非重新載入特定頁面。
      由於HTTP請求是Ajax中最極端的瓶頸之一,減少其需求數量對整個頁面性能有很大影響。尤其是當你將100個圖片請求轉化為一個MXHR請求時。Ad hoc 在現代瀏覽器上測試了大量圖片,其結果顯示出此技術比逐個請求快了4到10倍。
      有時你不關心接收數據,而只要將數據發送給服務器。你可以發送用戶的非私有信息以備日后分析,或者捕獲所有腳本錯誤然后將有關細節發送給服務器進行記錄和提示。當數據只需發送給服務器時,有兩種廣泛應用的技術:XHR和燈標。
      (1) XMLHttpRequest
      雖然XHR主要用於從服務器獲取數據,它也可以用來將數據發回。數據可以用GET或POST 方式發回,以及任意數量的HTTP 信息頭。這給你很大靈活性。當你向服務器發回的數據量超過瀏覽器的最大URL長度時XHR特別有用。這種情況下,你可以用POST 方式發回數據:
      var url = '/data.php';
      var params = [
            'id=934875',
            'limit=20'
      ];
      var req = new XMLHttpRequest();
      req.onerror = function() {
            // Error.
      };
      req.onreadystatechange = function() {
            if (req.readyState== 4) {
                  // Success.
            }
      };
      req.open('POST', url, true);
      req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      req.setRequestHeader('Content-Length', params.length);
      req.send(params.join('&'));
      正如你在這個例子中看到的,如果失敗了我們什么也不做。當我們用XHR捕獲登陸用戶統計信息時這么做通常沒什么問題,但是,如果發送到服務器的是至關重要的數據,你可以添加代碼在失敗時重試:
      function xhrPost(url, params, callback) {
            var req = new XMLHttpRequest();
            req.onerror = function() {
                  setTimeout(function() {
                        xhrPost(url, params, callback);
                  }, 1000);
            };
            req.onreadystatechange = function() {
                  if (req.readyState== 4) {
                        if (callback && typeof callback === 'function') {
                             callback();
                        }
                  }
            };
            req.open('POST', url, true);
            req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            req.setRequestHeader('Content-Length', params.length);
            req.send(params.join('&'));
      }
      當使用XHR將數據發回服務器時,它比使用GET要快。這是因為對少量數據而言,向服務器發送一個GET請求要占用一個單獨的數據包。另一方面,一個POST至少發送兩個數據包,一個用於信息頭。另一個用於POST體。POST更適合於向服務器發送大量數據,即因為它不關心額外數據包的數量,又因為Internet Explorer 的URL長度限制,它不可能使用過長的GET請求。
      (2) 燈標
      此技術與動態腳本標簽插入非常類似。JavaScript用於創建一個新的Image 對象,將src 設置為服務器上一個腳本文件的URL。此URL 包含我們打算通過GET格式傳回的鍵值對數據。注意並沒有創建img 元素或者將它們插入到DOM 中。
      var url = '/status_tracker.php';
      var params = [
            'step=2',
            'time=1248027314'
      ];
      (new Image()).src = url + '?' + params.join('&');
      服務器取得此數據並保存下來,而不必向客戶端返回什么,因此沒有實際的圖像顯示。這是將信息發回服務器的最有效方法。其開銷很小,而且任何服務器端錯誤都不會影響客戶端。
      簡單的圖像燈標意味着你所能做的受到限制。你不能發送POST 數據,所以你被URL 長度限制在一個相當小的字符數量上。你可以用非常有限的方法接收返回數據。可以監聽Image 對象的load 事件,它可以告訴你服務器端是否成功接收了數據。你還可以檢查服務器返回圖片的寬度和高度(如果返回了一張圖片)並用這些數字通知你服務器的狀態。例如,寬度為1 表示“成功”,2 表示“重試”。
      如果你不需要為此響應返回數據,那么你應當發送一個204 No Content 響應代碼,無消息正文。它將阻止客戶端繼續等待永遠不會到來的消息體:
      var url = '/status_tracker.php';
      var params = [
            'step=2',
            'time=1248027314'
      ];
      var beacon = new Image();
      beacon.src = url + '?' + params.join('&');
      beacon.onload = function() {
            if (this.width == 1) {
                  // Success.
            }
            else if (this.width == 2) {
                  // Failure; create another beacon and try again.
            }
      };
      beacon.onerror = function() {
            // Error; wait a bit, then create another beacon and try again.
      };
      燈標是向服務器回送數據最快和最有效的方法。服務器根本不需要發回任何響應正文,所以你不必擔心客戶端下載數據。唯一的缺點是接收到的響應類型是受限的。如果你需要向客戶端返回大量數據,那么使用XHR。如果你只關心將數據發送到服務器端(可能需要極少的回復),那么使用圖像燈標。

原創文章,轉載請注明:  轉載自 http://www.yiiyaa.net/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM