Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

219 строки
7.4 KiB

  1. /*
  2. Copyright 2018 Google Inc. All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. import {assert} from 'workbox-core/_private/assert.mjs';
  14. import {cacheNames} from 'workbox-core/_private/cacheNames.mjs';
  15. import {cacheWrapper} from 'workbox-core/_private/cacheWrapper.mjs';
  16. import {fetchWrapper} from 'workbox-core/_private/fetchWrapper.mjs';
  17. import {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.mjs';
  18. import {logger} from 'workbox-core/_private/logger.mjs';
  19. import messages from './utils/messages.mjs';
  20. import cacheOkAndOpaquePlugin from './plugins/cacheOkAndOpaquePlugin.mjs';
  21. import './_version.mjs';
  22. /**
  23. * An implementation of a
  24. * [stale-while-revalidate]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate}
  25. * request strategy.
  26. *
  27. * Resources are requested from both the cache and the network in parallel.
  28. * The strategy will respond with the cached version if available, otherwise
  29. * wait for the network response. The cache is updated with the network response
  30. * with each successful request.
  31. *
  32. * By default, this strategy will cache responses with a 200 status code as
  33. * well as [opaque responses]{@link https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests}.
  34. * Opaque responses are are cross-origin requests where the response doesn't
  35. * support [CORS]{@link https://enable-cors.org/}.
  36. *
  37. * @memberof workbox.strategies
  38. */
  39. class StaleWhileRevalidate {
  40. /**
  41. * @param {Object} options
  42. * @param {string} options.cacheName Cache name to store and retrieve
  43. * requests. Defaults to cache names provided by
  44. * [workbox-core]{@link workbox.core.cacheNames}.
  45. * @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  46. * to use in conjunction with this caching strategy.
  47. * @param {Object} options.fetchOptions Values passed along to the
  48. * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
  49. * of all fetch() requests made by this strategy.
  50. * @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
  51. */
  52. constructor(options = {}) {
  53. this._cacheName = cacheNames.getRuntimeName(options.cacheName);
  54. this._plugins = options.plugins || [];
  55. if (options.plugins) {
  56. let isUsingCacheWillUpdate =
  57. options.plugins.some((plugin) => !!plugin.cacheWillUpdate);
  58. this._plugins = isUsingCacheWillUpdate ?
  59. options.plugins : [cacheOkAndOpaquePlugin, ...options.plugins];
  60. } else {
  61. // No plugins passed in, use the default plugin.
  62. this._plugins = [cacheOkAndOpaquePlugin];
  63. }
  64. this._fetchOptions = options.fetchOptions || null;
  65. this._matchOptions = options.matchOptions || null;
  66. }
  67. /**
  68. * This method will perform a request strategy and follows an API that
  69. * will work with the
  70. * [Workbox Router]{@link workbox.routing.Router}.
  71. *
  72. * @param {Object} options
  73. * @param {FetchEvent} options.event The fetch event to run this strategy
  74. * against.
  75. * @return {Promise<Response>}
  76. */
  77. async handle({event}) {
  78. if (process.env.NODE_ENV !== 'production') {
  79. assert.isInstance(event, FetchEvent, {
  80. moduleName: 'workbox-strategies',
  81. className: 'StaleWhileRevalidate',
  82. funcName: 'handle',
  83. paramName: 'event',
  84. });
  85. }
  86. return this.makeRequest({
  87. event,
  88. request: event.request,
  89. });
  90. }
  91. /**
  92. * This method can be used to perform a make a standalone request outside the
  93. * context of the [Workbox Router]{@link workbox.routing.Router}.
  94. *
  95. * See "[Advanced Recipes](https://developers.google.com/web/tools/workbox/guides/advanced-recipes#make-requests)"
  96. * for more usage information.
  97. *
  98. * @param {Object} options
  99. * @param {Request|string} options.request Either a
  100. * [`Request`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Request}
  101. * object, or a string URL, corresponding to the request to be made.
  102. * @param {FetchEvent} [options.event] If provided, `event.waitUntil()` will
  103. * be called automatically to extend the service worker's lifetime.
  104. * @return {Promise<Response>}
  105. */
  106. async makeRequest({event, request}) {
  107. const logs = [];
  108. if (typeof request === 'string') {
  109. request = new Request(request);
  110. }
  111. if (process.env.NODE_ENV !== 'production') {
  112. assert.isInstance(request, Request, {
  113. moduleName: 'workbox-strategies',
  114. className: 'StaleWhileRevalidate',
  115. funcName: 'handle',
  116. paramName: 'request',
  117. });
  118. }
  119. const fetchAndCachePromise = this._getFromNetwork({request, event});
  120. let response = await cacheWrapper.match({
  121. cacheName: this._cacheName,
  122. request,
  123. event,
  124. matchOptions: this._matchOptions,
  125. plugins: this._plugins,
  126. });
  127. if (response) {
  128. if (process.env.NODE_ENV !== 'production') {
  129. logs.push(`Found a cached response in the '${this._cacheName}'` +
  130. ` cache. Will update with the network response in the background.`);
  131. }
  132. if (event) {
  133. try {
  134. event.waitUntil(fetchAndCachePromise);
  135. } catch (error) {
  136. if (process.env.NODE_ENV !== 'production') {
  137. logger.warn(`Unable to ensure service worker stays alive when ` +
  138. `updating cache for '${getFriendlyURL(event.request.url)}'.`);
  139. }
  140. }
  141. }
  142. } else {
  143. if (process.env.NODE_ENV !== 'production') {
  144. logs.push(`No response found in the '${this._cacheName}' cache. ` +
  145. `Will wait for the network response.`);
  146. }
  147. response = await fetchAndCachePromise;
  148. }
  149. if (process.env.NODE_ENV !== 'production') {
  150. logger.groupCollapsed(
  151. messages.strategyStart('StaleWhileRevalidate', request));
  152. for (let log of logs) {
  153. logger.log(log);
  154. }
  155. messages.printFinalResponse(response);
  156. logger.groupEnd();
  157. }
  158. return response;
  159. }
  160. /**
  161. * @param {Object} options
  162. * @param {Request} options.request
  163. * @param {Event} [options.event]
  164. * @return {Promise<Response>}
  165. *
  166. * @private
  167. */
  168. async _getFromNetwork({request, event}) {
  169. const response = await fetchWrapper.fetch({
  170. request,
  171. event,
  172. fetchOptions: this._fetchOptions,
  173. plugins: this._plugins,
  174. });
  175. const cachePutPromise = cacheWrapper.put({
  176. cacheName: this._cacheName,
  177. request,
  178. response: response.clone(),
  179. event,
  180. plugins: this._plugins,
  181. });
  182. if (event) {
  183. try {
  184. event.waitUntil(cachePutPromise);
  185. } catch (error) {
  186. if (process.env.NODE_ENV !== 'production') {
  187. logger.warn(`Unable to ensure service worker stays alive when ` +
  188. `updating cache for '${getFriendlyURL(event.request.url)}'.`);
  189. }
  190. }
  191. }
  192. return response;
  193. }
  194. }
  195. export {StaleWhileRevalidate};