今天看了很多文章,比較感興趣的是 Cache API。它是瀏覽器 Request/Response 的緩存管理工具,其使用風格和運用場景讓我瞬間聯想到了 ServiceWorker 和 Fetch API,相信不少同窗也屢次看到過這兩個東西,本文會對它們作一個簡潔的介紹,並談一談我對這些新玩具的見解。html
傳統的 XMLHttpRequest,出了兩個版本,在 XHR2.0 中引入了跨源請求、上傳進度事件和對二進制數據的支持等,這些 API 的加強讓 AJAX 能夠很方便地與 HTML5 API 相結合,例如 File System API、Web Audio API、WebGL 等,讓前端對音視頻的處理和富客戶端元素的處理更加有親和力。前端
做爲一個與後端交互的通道,XHR2.0 的接口封裝依然過於底層。看看 jQuery 對 AJAX 的封裝,再回頭看看咱們今天要介紹的 Fetch API,不得不驚歎,瀏覽器已經在應用層面思考着功能的拓展,依託着 Promise 產出了十分友好的新一套接口。json
之前咱們使用 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);
而使用 Fetch API,咱們只須要:瀏覽器
fetch('http://barretlee.com/test.json').then(function(response) { // Convert to JSON return response.json(); }).then(function(val) { console.log(val); });
對於 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); });
Fetch API 讓咱們更加關注請求和響應之間的交互,而不是聚焦在如何請求和如何處理響應兩個問題上。服務器
固然,它也存在幾個相比 XHR 不足的地方,首先它不能 abort 請求,同時也不能獲取請求過程當中的 progress 狀態,固然也沒有 timeout 超時處理。Fetch API 是基於 Promise 的,而 Promise 的狀態只有 pending、resolve、reject,不會出現諸如 pending(80%) 的狀態提示;咱們也沒法對一個 Promise chains 作 abort 處理,這些都是可以理解而且接受的。工具
我也相信,Fetch API 有能力提供這些狀態信息和附加的 API,只是在這個不成熟的環境下,它目前不須要邁這麼大的步子。post
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>
在 demo.html 文件中,咱們看到,將 /test/a.html
的請求交給 worker.js
來處理,處理方式爲:
// workker.js addEventListener("fetch", function(evt) { evt.respondWith(new Response(「Hi, Barret Lee」)); });
在 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); })); });
因爲 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,簡而言之就是一個 Request/Response 的緩存對象組,它的生命週期跟 ServiceWorker 是緊密相連的,它沒有失效時間,不刪除就會一直保持原樣。
caches.open('test-cache').then(function(cache) { cache.add('/index.html'); });
一個簡單的操做,就將 /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); }); });
當瀏覽器處於 idle(空閒) 狀態的時候,會將 Cache 資源預加載到本地。這也讓我想起了 link 標籤中有一個 prefetch 功能,也會有同窗想到 Manifest,不過這兩個東西都是不能友好控制的,而 Cache 給咱們帶來了這樣的便利。
我一直至關看好 Fetch API 系列相關的新接口,它的特色也很清晰,首先是基於 Promise 的實現,這個實現解決了回調和狀態控制的問題,而後是提供了應用級別的接口訪問,如今能夠把一個 HTTP 請求做爲可控的對象隨意操做,不管是 Request 仍是 Response 都在咱們的掌握之中,同時也必定程度解決了跨頁面資源共享的問題(至於跨頁面通信,咱們有 postMessage 和 MessageChannel 等工具)。
目前瀏覽器對 Fetch API 和 ServiceWorker 的支持都是比較可觀的,雖然 W3C 上的文檔狀態仍是 Draft 模式,相信隨着咱們對業務需求的更加明確,對前端認知的的不斷深刻,這些東西將很快被定爲 RFC。
本文沒有對 API 的使用作深刻的說明,一方面是由於這些東西能在 Google 上找到,其次,我以爲有些 API 的設計上還不夠成熟,從此會有增刪,感興趣的同窗能夠去 W3C 提供的文檔中深刻學習下。