當前端也擁有 Server 的能力

outcount1 8年前發布 | 22K 次閱讀 服務器 瀏覽器 JavaScript開發

來自: http://www.barretlee.com/blog/2016/02/16/when-fe-has-the-power-of-server/

今天看了不少文章,比較感興趣的是 Cache API。它是瀏覽器 Request/Response 的緩存管理工具,其使用風格和運用場景讓我瞬間聯想到了 ServiceWorker 和 Fetch API,相信很多同學也多次看到過這兩個東西,本文會對它們做一個簡潔的介紹,并談一談我對這些新玩具的看法。

Fetch API

傳統的 XMLHttpRequest,出了兩個版本,在 XHR2.0 中引入了跨源請求、上傳進度事件和對二進制數據的支持等,這些 API 的增強讓 AJAX 可以很方便地與 HTML5 API 相結合,例如 File System API、Web Audio API、WebGL 等,讓前端對音視頻的處理和富客戶端元素的處理更加有親和力。

作為一個與后端交互的通道,XHR2.0 的接口封裝依然過于底層。看看 jQuery 對 AJAX 的封裝,再回頭看看我們今天要介紹的 Fetch API,不得不驚嘆,瀏覽器已經在應用層面思考著功能的拓展,依托著 Promise 產出了十分友好的新一套接口。

以前我們使用 XHR 去請求一個資源,會這么做:

// Just getting XHR is a mess!
if (window.XMLHttpRequest) {
  request = new XMLHttpRequest();
} else if (window.ActiveXObject) {
  try {
    request = new ActiveXObject('Msxml2.XMLHTTP');
  } 
  catch (e) {
    try {
      request = new ActiveXObject('Microsoft.XMLHTTP');
    } 
    catch (e) {}
  }
}
request.onreadstatechange = function(){
  // handle data;
};
request.open('GET', 'http://barretlee.com/test.json', true);
request.send(null);

</div>

而使用 Fetch API,我們只需要:

fetch('http://barretlee.com/test.json').then(function(response) { 
  // Convert to JSON
  return response.json();
}).then(function(val) {
  console.log(val); 
});

</div>

對于 Text/HTML 和 Blob 等格式的請求和轉化也是異常方便:

// Text/HTML 請求
fetch('/next/page').then(function(response) {
    return response.text();
}).then(function(text) {
  console.log(text); 
});

// Blob 流
fetch('flowers.jpg').then(function(response) {
  return response.blob();
}).then(function(blob) {
  document.querySelector('img').src = URL.createObjectURL(blob);
});

</div>

Fetch API 讓我們更加關注請求和響應之間的交互,而不是聚焦在如何請求和如何處理響應兩個問題上。

當然,它也存在幾個相比 XHR 不足的地方,首先它不能 abort 請求,同時也不能獲取請求過程中的 progress 狀態,當然也沒有 timeout 超時處理。Fetch API 是基于 Promise 的,而 Promise 的狀態只有 pending、resolve、reject,不會出現諸如 pending(80%) 的狀態提示;我們也無法對一個 Promise chains 做 abort 處理,這些都是能夠理解并且接受的。

我也相信,Fetch API 有能力提供這些狀態信息和附加的 API,只是在這個不成熟的環境下,它目前不需要邁這么大的步子。

ServiceWorker

ServiceWorker,簡單而言就是一個放在前端的 HTTP 攔截器,比如我們要請求一個不存在的 URI 如: /test/a.html ,直接請求就會響應 404,而如果我們預先在 ServiceWorker 中注冊了這個地址,并且指定響應內容,當再次請求時,你會看到結果是存在的,舉個例子:

<!—demo.html—>
<script>
navigator.serviceWorker.register("worker.js", {
 scope: ”/test/a.html"
}).then(function(){
 fetch(‘/test/a.html’).then(function(response) {
 return response.text();
 }).then(function(text) {
 console.log(text); 
 });
});
</script>

</div>

在 demo.html 文件中,我們看到,將 /test/a.html 的請求交給 worker.js 來處理,處理方式為:

// workker.js
addEventListener("fetch", function(evt) {
  evt.respondWith(new Response(“Hi, Barret Lee”));
});

</div>

在 demo.html 的回調中使用 Fetch 獲取 /test/a.html 這個并不存在的內容,被 ServiceWorker 捕獲,交給 worker.js 處理并響應 Hi, Barret Lee 的文本,整個設計思路十分清晰,很輕松地攔截了來自客戶端的請求,并作出了響應。

由于 ServiceWorker 是對 Promise 友好的,響應時也可以模擬服務器休眠狀態:

addEventListener("fetch", function(evt) {
  evt.respondWith(new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve(new Response(“Hi, Barret Lee”));
    }, 1000);
  }));
});

</div>

由于 Fetch API 提供了對 Header 頭的修改,我們幾乎可以利用 ServiceWorker 實現真實 HTTP Server 的基本功能。

ServiceWorker 一定程度上改變了 Web 協作的交互模式,傳統情況下,我們需要開啟一個 Web Server,或者讓其他人提供 HTTP Server,前后端之間交互,溝通成本比較高。而 ServiceWorker 把 HTTP Server 搬到了客戶端,我們可以在瀏覽器上輕松 Hold 住兩端的操作。這也算是 Web 技術棧融合的表現吧。

當我們的目光放在 HTTP 的交互上,ServiceWorker 會有無限的想象空間,比如對 History API 的延伸思考,跨頁面共享問題,前端請求合并和分拆問題,mock 數據問題,前后端的聯調問題,類 graphQL 問題,數據的緩存更新和復用問題等等。

Cache API

Cache API,簡而言之就是一個 Request/Response 的緩存對象組,它的生命周期跟 ServiceWorker 是緊密相連的,它沒有失效時間,不刪除就會一直保持原樣。

caches.open('test-cache').then(function(cache) {
  cache.add('/index.html');
});

</div>

一個簡單的操作,就將 /index.html 這個頁面緩存了下來,如果你使用的是最新版的 Chrome,可以打開 DevTools > Resources > Cache Storage,多了一個 test-cache 的緩存表,表中多出一項,Request 為 http://barretlee.com/index.html , Response 為 OK 。如下方式可以查看緩存內容:

caches.open('test-cache').then(function(cache) { 
  cache.keys().then(function(cachedRequests) { 
    console.log(cachedRequests);
  });
});

</div>

當瀏覽器處于 idle(空閑) 狀態的時候,會將 Cache 資源預加載到本地。這也讓我想起了 link 標簽中有一個 prefetch 功能,也會有同學想到 Manifest,不過這兩個東西都是不能友好控制的,而 Cache 給我們帶來了這樣的便利。

小結

我一直相當看好 Fetch API 系列相關的新接口,它的特點也很清晰,首先是基于 Promise 的實現,這個實現解決了回調和狀態控制的問題,然后是提供了應用級別的接口訪問,現在可以把一個 HTTP 請求作為可控的對象隨意操作,無論是 Request 還是 Response 都在我們的掌握之中,同時也一定程度解決了跨頁面資源共享的問題(至于跨頁面通訊,我們有 postMessage 和 MessageChannel 等工具)。

目前瀏覽器對 Fetch API 和 ServiceWorker 的支持都是比較可觀的,雖然 W3C 上的文檔狀態還是 Draft 模式,相信隨著我們對業務需求的更加明確,對前端認知的的不斷深入,這些東西將很快被定為 RFC。

本文沒有對 API 的使用做深入的說明,一方面是因為這些東西能在 Google 上找到,其次,我覺得有些 API 的設計上還不夠成熟,今后會有增刪,感興趣的同學可以去 W3C 提供的文檔中深入學習下。

拓展閱讀

</div>

 本文由用戶 outcount1 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!