service worker caching
Static Elements Caching
Adding different files to caches one by one , and also serving from service worker
addAll for caching multiple files
Dynamic caching
since we don't know all the requests which we require so we can dynamically cache those ,
if it is not found in the caches then first make request and then store response .
Adding cache versioning
Adding different files to caches one by one , and also serving from service worker
addAll for caching multiple files
Dynamic caching
since we don't know all the requests which we require so we can dynamically cache those ,
if it is not found in the caches then first make request and then store response .
Adding cache versioning
- why do we need cache vesrsions
If we will do some change in a file ,then if that file is already cached changes does not reflect
since service worker will serve same old version of the files .
so we have to maintain different version of cache , also we need to clean previous version so that files will be not served from previous version
code used
- sw.js
var CACHE_STATIC_NAME = 'static-v4'; var CACHE_DYNAMIC_NAME = 'dynamic-v2'; self.addEventListener('install', function(event) { console.log('[Service Worker] Installing Service Worker ...', event); event.waitUntil( caches.open(CACHE_STATIC_NAME) .then(function(cache) { console.log('[Service Worker] Precaching App Shell'); cache.addAll([ '/', '/index.html', '/src/js/app.js', '/src/js/feed.js', '/src/js/promise.js', '/src/js/fetch.js', '/src/js/material.min.js', '/src/css/app.css', '/src/css/feed.css', '/src/images/main-image.jpg', 'https://fonts.googleapis.com/css?family=Roboto:400,700', 'https://fonts.googleapis.com/icon?family=Material+Icons', 'https://cdnjs.cloudflare.com/ajax/libs/material-design-lite/1.3.0/material.indigo-pink.min.css' ]); }) ) }); self.addEventListener('activate', function(event) { console.log('[Service Worker] Activating Service Worker ....', event); event.waitUntil( caches.keys() .then(function(keyList) { return Promise.all(keyList.map(function(key) { if (key !== CACHE_STATIC_NAME && key !== CACHE_DYNAMIC_NAME) { console.log('[Service Worker] Removing old cache.', key); return caches.delete(key); } })); }) ); return self.clients.claim(); }); self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request) .then(function(response) { if (response) { return response; } else { return fetch(event.request) .then(function(res) { return caches.open(CACHE_DYNAMIC_NAME) .then(function(cache) { cache.put(event.request.url, res.clone()); return res; }) }) .catch(function(err) { }); } }) ); })
promiss.js
/** * setImmediate polyfill v1.0.1, supports IE9+ * © 2014–2015 Dmitry Korobkin * Released under the MIT license * github.com/Octane/setImmediate */ window.setImmediate || function () {'use strict'; var uid = 0; var storage = {}; var firstCall = true; var slice = Array.prototype.slice; var message = 'setImmediatePolyfillMessage'; function fastApply(args) { var func = args[0]; switch (args.length) { case 1: return func(); case 2: return func(args[1]); case 3: return func(args[1], args[2]); } return func.apply(window, slice.call(args, 1)); } function callback(event) { var key = event.data; var data; if (typeof key == 'string' && key.indexOf(message) == 0) { data = storage[key]; if (data) { delete storage[key]; fastApply(data); } } } window.setImmediate = function setImmediate() { var id = uid++; var key = message + id; var i = arguments.length; var args = new Array(i); while (i--) { args[i] = arguments[i]; } storage[key] = args; if (firstCall) { firstCall = false; window.addEventListener('message', callback); } window.postMessage(key, '*'); return id; }; window.clearImmediate = function clearImmediate(id) { delete storage[message + id]; }; }(); /** * Promise polyfill v1.0.10 * requires setImmediate * * © 2014–2015 Dmitry Korobkin * Released under the MIT license * github.com/Octane/Promise */ (function (global) {'use strict'; var STATUS = '[[PromiseStatus]]'; var VALUE = '[[PromiseValue]]'; var ON_FUlFILLED = '[[OnFulfilled]]'; var ON_REJECTED = '[[OnRejected]]'; var ORIGINAL_ERROR = '[[OriginalError]]'; var PENDING = 'pending'; var INTERNAL_PENDING = 'internal pending'; var FULFILLED = 'fulfilled'; var REJECTED = 'rejected'; var NOT_ARRAY = 'not an array.'; var REQUIRES_NEW = 'constructor Promise requires "new".'; var CHAINING_CYCLE = 'then() cannot return same Promise that it resolves.'; var setImmediate = global.setImmediate || require('timers').setImmediate; var isArray = Array.isArray || function (anything) { return Object.prototype.toString.call(anything) == '[object Array]'; }; function InternalError(originalError) { this[ORIGINAL_ERROR] = originalError; } function isInternalError(anything) { return anything instanceof InternalError; } function isObject(anything) { //Object.create(null) instanceof Object → false return Object(anything) === anything; } function isCallable(anything) { return typeof anything == 'function'; } function isPromise(anything) { return anything instanceof Promise; } function identity(value) { return value; } function thrower(reason) { throw reason; } function enqueue(promise, onFulfilled, onRejected) { if (!promise[ON_FUlFILLED]) { promise[ON_FUlFILLED] = []; promise[ON_REJECTED] = []; } promise[ON_FUlFILLED].push(onFulfilled); promise[ON_REJECTED].push(onRejected); } function clearAllQueues(promise) { delete promise[ON_FUlFILLED]; delete promise[ON_REJECTED]; } function callEach(queue) { var i; var length = queue.length; for (i = 0; i < length; i++) { queue[i](); } } function call(resolve, reject, value) { var anything = toPromise(value); if (isPromise(anything)) { anything.then(resolve, reject); } else if (isInternalError(anything)) { reject(anything[ORIGINAL_ERROR]); } else { resolve(value); } } function toPromise(anything) { var then; if (isPromise(anything)) { return anything; } if(isObject(anything)) { try { then = anything.then; } catch (error) { return new InternalError(error); } if (isCallable(then)) { return new Promise(function (resolve, reject) { setImmediate(function () { try { then.call(anything, resolve, reject); } catch (error) { reject(error); } }); }); } } return null; } function resolvePromise(promise, resolver) { function resolve(value) { if (promise[STATUS] == PENDING) { fulfillPromise(promise, value); } } function reject(reason) { if (promise[STATUS] == PENDING) { rejectPromise(promise, reason); } } try { resolver(resolve, reject); } catch(error) { reject(error); } } function fulfillPromise(promise, value) { var queue; var anything = toPromise(value); if (isPromise(anything)) { promise[STATUS] = INTERNAL_PENDING; anything.then( function (value) { fulfillPromise(promise, value); }, function (reason) { rejectPromise(promise, reason); } ); } else if (isInternalError(anything)) { rejectPromise(promise, anything[ORIGINAL_ERROR]); } else { promise[STATUS] = FULFILLED; promise[VALUE] = value; queue = promise[ON_FUlFILLED]; if (queue && queue.length) { clearAllQueues(promise); callEach(queue); } } } function rejectPromise(promise, reason) { var queue = promise[ON_REJECTED]; promise[STATUS] = REJECTED; promise[VALUE] = reason; if (queue && queue.length) { clearAllQueues(promise); callEach(queue); } } function Promise(resolver) { var promise = this; if (!isPromise(promise)) { throw new TypeError(REQUIRES_NEW); } promise[STATUS] = PENDING; promise[VALUE] = undefined; resolvePromise(promise, resolver); } Promise.prototype.then = function (onFulfilled, onRejected) { var promise = this; var nextPromise; onFulfilled = isCallable(onFulfilled) ? onFulfilled : identity; onRejected = isCallable(onRejected) ? onRejected : thrower; nextPromise = new Promise(function (resolve, reject) { function tryCall(func) { var value; try { value = func(promise[VALUE]); } catch (error) { reject(error); return; } if (value === nextPromise) { reject(new TypeError(CHAINING_CYCLE)); } else { call(resolve, reject, value); } } function asyncOnFulfilled() { setImmediate(tryCall, onFulfilled); } function asyncOnRejected() { setImmediate(tryCall, onRejected); } switch (promise[STATUS]) { case FULFILLED: asyncOnFulfilled(); break; case REJECTED: asyncOnRejected(); break; default: enqueue(promise, asyncOnFulfilled, asyncOnRejected); } }); return nextPromise; }; Promise.prototype['catch'] = function (onRejected) { return this.then(identity, onRejected); }; Promise.resolve = function (value) { var anything = toPromise(value); if (isPromise(anything)) { return anything; } return new Promise(function (resolve, reject) { if (isInternalError(anything)) { reject(anything[ORIGINAL_ERROR]); } else { resolve(value); } }); }; Promise.reject = function (reason) { return new Promise(function (resolve, reject) { reject(reason); }); }; Promise.race = function (values) { return new Promise(function (resolve, reject) { var i; var length; if (isArray(values)) { length = values.length; for (i = 0; i < length; i++) { call(resolve, reject, values[i]); } } else { reject(new TypeError(NOT_ARRAY)); } }); }; Promise.all = function (values) { return new Promise(function (resolve, reject) { var fulfilledCount = 0; var promiseCount = 0; var anything; var length; var value; var i; if (isArray(values)) { values = values.slice(0); length = values.length; for (i = 0; i < length; i++) { value = values[i]; anything = toPromise(value); if (isPromise(anything)) { promiseCount++; anything.then( function (index) { return function (value) { values[index] = value; fulfilledCount++; if (fulfilledCount == promiseCount) { resolve(values); } }; }(i), reject ); } else if (isInternalError(anything)) { reject(anything[ORIGINAL_ERROR]); } else { //[1, , 3] → [1, undefined, 3] values[i] = value; } } if (!promiseCount) { resolve(values); } } else { reject(new TypeError(NOT_ARRAY)); } }); }; if (typeof module != 'undefined' && module.exports) { module.exports = global.Promise || Promise; } else if (!global.Promise) { global.Promise = Promise; } }(this));
Fetch.js
(function(self) { 'use strict'; if (self.fetch) { return } var support = { searchParams: 'URLSearchParams' in self, iterable: 'Symbol' in self && 'iterator' in Symbol, blob: 'FileReader' in self && 'Blob' in self && (function() { try { new Blob() return true } catch(e) { return false } })(), formData: 'FormData' in self, arrayBuffer: 'ArrayBuffer' in self } if (support.arrayBuffer) { var viewClasses = [ '[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]' ] var isDataView = function(obj) { return obj && DataView.prototype.isPrototypeOf(obj) } var isArrayBufferView = ArrayBuffer.isView || function(obj) { return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 } } function normalizeName(name) { if (typeof name !== 'string') { name = String(name) } if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { throw new TypeError('Invalid character in header field name') } return name.toLowerCase() } function normalizeValue(value) { if (typeof value !== 'string') { value = String(value) } return value } // Build a destructive iterator for the value list function iteratorFor(items) { var iterator = { next: function() { var value = items.shift() return {done: value === undefined, value: value} } } if (support.iterable) { iterator[Symbol.iterator] = function() { return iterator } } return iterator } function Headers(headers) { this.map = {} if (headers instanceof Headers) { headers.forEach(function(value, name) { this.append(name, value) }, this) } else if (Array.isArray(headers)) { headers.forEach(function(header) { this.append(header[0], header[1]) }, this) } else if (headers) { Object.getOwnPropertyNames(headers).forEach(function(name) { this.append(name, headers[name]) }, this) } } Headers.prototype.append = function(name, value) { name = normalizeName(name) value = normalizeValue(value) var oldValue = this.map[name] this.map[name] = oldValue ? oldValue+','+value : value } Headers.prototype['delete'] = function(name) { delete this.map[normalizeName(name)] } Headers.prototype.get = function(name) { name = normalizeName(name) return this.has(name) ? this.map[name] : null } Headers.prototype.has = function(name) { return this.map.hasOwnProperty(normalizeName(name)) } Headers.prototype.set = function(name, value) { this.map[normalizeName(name)] = normalizeValue(value) } Headers.prototype.forEach = function(callback, thisArg) { for (var name in this.map) { if (this.map.hasOwnProperty(name)) { callback.call(thisArg, this.map[name], name, this) } } } Headers.prototype.keys = function() { var items = [] this.forEach(function(value, name) { items.push(name) }) return iteratorFor(items) } Headers.prototype.values = function() { var items = [] this.forEach(function(value) { items.push(value) }) return iteratorFor(items) } Headers.prototype.entries = function() { var items = [] this.forEach(function(value, name) { items.push([name, value]) }) return iteratorFor(items) } if (support.iterable) { Headers.prototype[Symbol.iterator] = Headers.prototype.entries } function consumed(body) { if (body.bodyUsed) { return Promise.reject(new TypeError('Already read')) } body.bodyUsed = true } function fileReaderReady(reader) { return new Promise(function(resolve, reject) { reader.onload = function() { resolve(reader.result) } reader.onerror = function() { reject(reader.error) } }) } function readBlobAsArrayBuffer(blob) { var reader = new FileReader() var promise = fileReaderReady(reader) reader.readAsArrayBuffer(blob) return promise } function readBlobAsText(blob) { var reader = new FileReader() var promise = fileReaderReady(reader) reader.readAsText(blob) return promise } function readArrayBufferAsText(buf) { var view = new Uint8Array(buf) var chars = new Array(view.length) for (var i = 0; i < view.length; i++) { chars[i] = String.fromCharCode(view[i]) } return chars.join('') } function bufferClone(buf) { if (buf.slice) { return buf.slice(0) } else { var view = new Uint8Array(buf.byteLength) view.set(new Uint8Array(buf)) return view.buffer } } function Body() { this.bodyUsed = false this._initBody = function(body) { this._bodyInit = body if (!body) { this._bodyText = '' } else if (typeof body === 'string') { this._bodyText = body } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { this._bodyBlob = body } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { this._bodyFormData = body } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this._bodyText = body.toString() } else if (support.arrayBuffer && support.blob && isDataView(body)) { this._bodyArrayBuffer = bufferClone(body.buffer) // IE 10-11 can't handle a DataView body. this._bodyInit = new Blob([this._bodyArrayBuffer]) } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { this._bodyArrayBuffer = bufferClone(body) } else { throw new Error('unsupported BodyInit type') } if (!this.headers.get('content-type')) { if (typeof body === 'string') { this.headers.set('content-type', 'text/plain;charset=UTF-8') } else if (this._bodyBlob && this._bodyBlob.type) { this.headers.set('content-type', this._bodyBlob.type) } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') } } } if (support.blob) { this.blob = function() { var rejected = consumed(this) if (rejected) { return rejected } if (this._bodyBlob) { return Promise.resolve(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(new Blob([this._bodyArrayBuffer])) } else if (this._bodyFormData) { throw new Error('could not read FormData body as blob') } else { return Promise.resolve(new Blob([this._bodyText])) } } this.arrayBuffer = function() { if (this._bodyArrayBuffer) { return consumed(this) || Promise.resolve(this._bodyArrayBuffer) } else { return this.blob().then(readBlobAsArrayBuffer) } } } this.text = function() { var rejected = consumed(this) if (rejected) { return rejected } if (this._bodyBlob) { return readBlobAsText(this._bodyBlob) } else if (this._bodyArrayBuffer) { return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) } else if (this._bodyFormData) { throw new Error('could not read FormData body as text') } else { return Promise.resolve(this._bodyText) } } if (support.formData) { this.formData = function() { return this.text().then(decode) } } this.json = function() { return this.text().then(JSON.parse) } return this } // HTTP methods whose capitalization should be normalized var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] function normalizeMethod(method) { var upcased = method.toUpperCase() return (methods.indexOf(upcased) > -1) ? upcased : method } function Request(input, options) { options = options || {} var body = options.body if (input instanceof Request) { if (input.bodyUsed) { throw new TypeError('Already read') } this.url = input.url this.credentials = input.credentials if (!options.headers) { this.headers = new Headers(input.headers) } this.method = input.method this.mode = input.mode if (!body && input._bodyInit != null) { body = input._bodyInit input.bodyUsed = true } } else { this.url = String(input) } this.credentials = options.credentials || this.credentials || 'omit' if (options.headers || !this.headers) { this.headers = new Headers(options.headers) } this.method = normalizeMethod(options.method || this.method || 'GET') this.mode = options.mode || this.mode || null this.referrer = null if ((this.method === 'GET' || this.method === 'HEAD') && body) { throw new TypeError('Body not allowed for GET or HEAD requests') } this._initBody(body) } Request.prototype.clone = function() { return new Request(this, { body: this._bodyInit }) } function decode(body) { var form = new FormData() body.trim().split('&').forEach(function(bytes) { if (bytes) { var split = bytes.split('=') var name = split.shift().replace(/\+/g, ' ') var value = split.join('=').replace(/\+/g, ' ') form.append(decodeURIComponent(name), decodeURIComponent(value)) } }) return form } function parseHeaders(rawHeaders) { var headers = new Headers() rawHeaders.split(/\r?\n/).forEach(function(line) { var parts = line.split(':') var key = parts.shift().trim() if (key) { var value = parts.join(':').trim() headers.append(key, value) } }) return headers } Body.call(Request.prototype) function Response(bodyInit, options) { if (!options) { options = {} } this.type = 'default' this.status = 'status' in options ? options.status : 200 this.ok = this.status >= 200 && this.status < 300 this.statusText = 'statusText' in options ? options.statusText : 'OK' this.headers = new Headers(options.headers) this.url = options.url || '' this._initBody(bodyInit) } Body.call(Response.prototype) Response.prototype.clone = function() { return new Response(this._bodyInit, { status: this.status, statusText: this.statusText, headers: new Headers(this.headers), url: this.url }) } Response.error = function() { var response = new Response(null, {status: 0, statusText: ''}) response.type = 'error' return response } var redirectStatuses = [301, 302, 303, 307, 308] Response.redirect = function(url, status) { if (redirectStatuses.indexOf(status) === -1) { throw new RangeError('Invalid status code') } return new Response(null, {status: status, headers: {location: url}}) } self.Headers = Headers self.Request = Request self.Response = Response self.fetch = function(input, init) { return new Promise(function(resolve, reject) { var request = new Request(input, init) var xhr = new XMLHttpRequest() xhr.onload = function() { var options = { status: xhr.status, statusText: xhr.statusText, headers: parseHeaders(xhr.getAllResponseHeaders() || '') } options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL') var body = 'response' in xhr ? xhr.response : xhr.responseText resolve(new Response(body, options)) } xhr.onerror = function() { reject(new TypeError('Network request failed')) } xhr.ontimeout = function() { reject(new TypeError('Network request failed')) } xhr.open(request.method, request.url, true) if (request.credentials === 'include') { xhr.withCredentials = true } if ('responseType' in xhr && support.blob) { xhr.responseType = 'blob' } request.headers.forEach(function(value, name) { xhr.setRequestHeader(name, value) }) xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) }) } self.fetch.polyfill = true })(typeof self !== 'undefined' ? self : this);
Comments
Post a Comment