PWA 学习笔记之 fetch API
cn-programming·@semlinker·
0.000 HBDPWA 学习笔记之 fetch API
Progressive Web App (PWA)是渐进增强 Web App,它能让我们在不可靠的网络上也能快速加载、能够接收桌面通知、具有桌面图标,并且可采用顶层全屏体验的方式加载。  (背景素材来源:[Google - progressive-web-apps](https://developers.google.com/web/progressive-web-apps/)) Progressive Web App 具有的以下主要特点: - **渐进式** - 适用于选用任何浏览器的所有用户,因为它是以渐进式增强作为核心宗旨来开发的。 - **自适应** - 适合任何机型:桌面设备、移动设备、平板电脑或任何未来设备。 - **持续更新** - 在服务工作线程更新进程的作用下时刻保持最新状态。 - **安全** - 通过 HTTPS 提供,以防止窥探和确保内容不被篡改。 - **可安装** - 用户可免去使用应用商店的麻烦,直接将对其最有用的应用“保留”在主屏幕上。 - **可链接** - 可通过网址轻松分享,无需复杂的安装。 PWA 基于很多新的 API 和新的技术,如 fetch API、CacheStorage API、Background Sync、Service Worker 和 IndexedDB 等。然而要想真正了解并掌握 PWA,就必须了解它背后基于的技术。因此后续的文章,我们将逐一介绍 PWA 的相关技术。有兴趣的小伙伴们赶紧上车,我们将从 fetch API 开始,开启 PWA 的学习旅程。 fetch 中文的意思为获取,即通过它我们可以用来获取资源。在前端的日常工作中,我们通常需要从 API 获取数据,然后对数据进行处理或展示。在与服务器交互过程中使用的数据格式一般是 JSON,接下来我们先来体验一下,利用 fetch API 获取 [angular](https://github.com/angular/angular) 项目的团队的前五位成员,实现代码如下: ```javascript if("fetch" in this) { fetch("https://api.github.com/orgs/angular/members?page=1&per_page=5") .then(res => res.json()) .then(console.dir) } ``` 要实现同样的功能,我们当然也可以使用 XMLHttpRequest 对象,实现代码如下: ```javascript var xhr = new XMLHttpRequest(); xhr.open('GET', "https://api.github.com/orgs/angular/members?page=1&per_page=5"); xhr.responseType = 'json'; xhr.onload = function() { console.dir(xhr.response); }; xhr.send(); ``` 是不是感觉使用 fetch 简单很多,然而我们并不能随心所欲的使用它,因为它有兼容性问题,具体如下下图所示 (详细信息可浏览 [can i use - fetch](https://caniuse.com/#search=fetch)):  (图片来源:[caniuse - fetch](https://caniuse.com/#search=fetch)) 对于大多数小伙伴来说,应该更熟悉 jQuery.ajax()。fetch 规范与 jQuery.ajax() 的主要区别如下: - 当接收到一个代表错误的 HTTP 状态码时,从 `fetch()` 返回的 Promise **不会被标记为 reject,** 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ), **仅当网络故障时或请求被阻止时,才会标记为 reject**。 - 默认情况下, `fetch` **不会从服务端发送或接收任何 cookies**, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 [credentials](https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalFetch/fetch#%E5%8F%82%E6%95%B0) 选项)。 除了使用 fetch API 来获取 JSON 数据,我们也可以使用它来获取其它资源,比如普通文本、图片资源等。下面我们来看一下如何使用 fetch API 来获取图片资源,并在页面中显示。 ```javascript if ("fetch" in this) { let myImage = document.querySelector('img'); fetch('https://mdn.github.io/dom-examples/streams/grayscale-png/tortoise.png') .then(function(response) { return response.blob(); }) .then(function(myBlob) { let objectURL = URL.createObjectURL(myBlob); myImage.src = objectURL; }); } ``` 前面的两个示例中,我们通过 fetch 方法分别实现 JSON 数据的读取和图片的资源的获取功能。fetch 方法是 Fetch API 的核心方法,同时定义在 window 和 WorkerGlobalScope 环境中,因此我们可以在 Service Worker 环境中使用它。 在 [Fetch 标准](https://fetch.spec.whatwg.org/#fetch-method) 中该方法的声明如下: ``` partial interface WindowOrWorkerGlobalScope { [NewObject] Promise<Response> fetch(RequestInfo input, optional RequestInit init); }; ``` 通过观察上面的方法签名,我们可以知道 fetch 方法支持两个参数,调用后返回一个 Promise 对象。第二个参数是一个参数对象,用来初始化 Request。该参数对象有几个重要的属性: - `method`:请求方法,可取 `"GET"`, `"POST"` 等,默认为 `"GET"`。 - `mode`:请求模式,可取 `"no-cors"`, `"cors"`, `"same-origin"`。 - `credentials`:是否携带 Cookie,可取:`"omit"`, `"same-origin"`, `"include"`。 - `cache`: 缓存模式,可取: `default`, `no-store`, `reload`, `no-cache`, `force-cache`, `only-if-cached`。 了解详细的信息,请阅读 [https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalFetch/fetch](https://developer.mozilla.org/zh-CN/docs/Web/API/GlobalFetch/fetch)。 在 PWA 应用中,fetch API 的用武之地在于资源(比如图片、脚本文件或样式文件等)的获取。为了能够保证用户的离线体验,我们可以在获取资源时,对资源进行缓存。 具体示例如下: ```javascript self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { // 若缓存不存在,则使用fetch API从网上获取,然后利用Cache API缓存资源。 return response || fetch(event.request).then((response) => { return caches.open('v1').then((cache) => { cache.put(event.request, response.clone()); return response; }); }); }) ); }); ``` 对于 Cache API,我们下一篇会介绍。这里需要注意的是,在使用 cache.put() 保存资源时,我们调用 response 对象的 clone() 方法,而不是直接保存 response 对象。为什么需要克隆响应对象呢?**这是因为 Request 和 Response 的 body (响应体)只能被读取一次!它们有一个属性叫 bodyUsed,读取一次之后设置为 true,就不能再读取了。** 本文只是简单介绍了 fetch API,其实 fetch API 还有很多东西需要进一步了解,如设置请求头、处理 Text/HTML 资源、表单提交、Cookies 与 CORS 处理等。这里就不再展开了,有兴趣的小伙伴,请自行查阅相关资料。
👍 samiurrehman, cqf,