Service Worker 在浏览器离线时保存表单数据 [英] Service worker to save form data when browser is offline

查看:36
本文介绍了Service Worker 在浏览器离线时保存表单数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Service Worker 的新手,并且浏览了各种文档(

(在 Firefox 中发生了类似的错误 - 它在 ServiceWorkerWare.js 的第 409 行失败)

 ServiceWorkerWare.prototype.executeMiddleware = 函数(中间件,要求) {var response = this.runMiddleware(middleware, 0, request, null);response.catch(function (error) { console.error(error); });返回响应;};

解决方案

Service Workers 倾向于缓存静态 HTML、CSS、JavaScript 和图像文件.

我需要使用 PouchDB 并与 CouchDB 同步

为什么选择 CouchDB?

  • CouchDB 是一个 NoSQL 数据库,由许多文档组成使用 JSON 创建.
  • 它有版本控制(每个文档都有一个 _rev上次修改日期的属性)
  • 它可以与PouchDB,一个将数据存储在本地的本地 JavaScript 应用程序使用 IndexedDB 通过浏览器存储.这允许我们创建离线应用.
  • 这两个数据库都是主"副本数据.

PouchDB 是 CouchDB 的本地 JavaScript 实现.

I am new to Service Workers, and have had a look through the various bits of documentation (Google, Mozilla, serviceworke.rs, Github, StackOverflow questions). The most helpful is the ServiceWorkers cookbook.

Most of the documentation seems to point to caching entire pages so that the app works completely offline, or redirecting the user to an offline page until the browser can redirect to the internet.

What I want to do, however, is store my form data locally so my web app can upload it to the server when the user's connection is restored. Which "recipe" should I use? I think it is Request Deferrer. Do I need anything else to ensure that Request Deferrer will work (apart from the service worker detector script in my web page)? Any hints and tips much appreciated.

Console errors

The Request Deferrer recipe and code doesn't seem to work on its own as it doesn't include file caching. I have added some caching for the service worker library files, but I am still getting this error when I submit the form while offline:

Console: {"lineNumber":0,"message":
"The FetchEvent for [the form URL] resulted in a network error response: 
the promise was rejected.","message_level":2,"sourceIdentifier":1,"sourceURL":""}

My Service Worker

/* eslint-env es6 */
/* eslint no-unused-vars: 0 */
/* global importScripts, ServiceWorkerWare, localforage */
importScripts('/js/lib/ServiceWorkerWare.js');
importScripts('/js/lib/localforage.js');

//Determine the root for the routes. I.e, if the Service Worker URL is http://example.com/path/to/sw.js, then the root is http://example.com/path/to/


var root = (function() {
  var tokens = (self.location + '').split('/');
  tokens[tokens.length - 1] = '';
  return tokens.join('/');
})();

//By using Mozilla’s ServiceWorkerWare we can quickly setup some routes for a virtual server. It is convenient you review the virtual server recipe before seeing this.


var worker = new ServiceWorkerWare();

//So here is the idea. We will check if we are online or not. In case we are not online, enqueue the request and provide a fake response. 
//Else, flush the queue and let the new request to reach the network.


//This function factory does exactly that.


function tryOrFallback(fakeResponse) {

//Return a handler that…


  return function(req, res) {

//If offline, enqueue and answer with the fake response.


    if (!navigator.onLine) {
      console.log('No network availability, enqueuing');
      return enqueue(req).then(function() {

//As the fake response will be reused but Response objects are one use only, we need to clone it each time we use it.


        return fakeResponse.clone();
});
}

//If online, flush the queue and answer from network.


    console.log('Network available! Flushing queue.');
    return flushQueue().then(function() {
      return fetch(req);
});
};
}

//A fake response with a joke for when there is no connection. A real implementation could have cached the last collection of updates and keep a local model. For simplicity, not implemented here.


worker.get(root + 'api/updates?*', tryOrFallback(new Response(
  JSON.stringify([{
    text: 'You are offline.',
    author: 'Oxford Brookes University',
    id: 1,
    isSticky: true
}]),
  { headers: { 'Content-Type': 'application/json' } }
)));

//For deletion, let’s simulate that all went OK. Notice we are omitting the body of the response. Trying to add a body with a 204, deleted, as status throws an error.


worker.delete(root + 'api/updates/:id?*', tryOrFallback(new Response({
    status: 204
})));

//Creation is another story. We can not reach the server so we can not get the id for the new updates. 
//No problem, just say we accept the creation and we will process it later, as soon as we recover connectivity.


worker.post(root + 'api/updates?*', tryOrFallback(new Response(null, {
    status: 202
})));

//Start the service worker.


worker.init();

//By using Mozilla’s localforage db wrapper, we can count on a fast setup for a versatile key-value database. We use it to store queue of deferred requests.


//Enqueue consists of adding a request to the list. Due to the limitations of IndexedDB, Request and Response objects can not be saved so we need an alternative representations. 
//This is why we call to serialize().`


function enqueue(request) {
  return serialize(request).then(function(serialized) {
    localforage.getItem('queue').then(function(queue) {
      /* eslint no-param-reassign: 0 */
      queue = queue || [];
      queue.push(serialized);
      return localforage.setItem('queue', queue).then(function() {
        console.log(serialized.method, serialized.url, 'enqueued!');
      });
    });
  });
}

//Flush is a little more complicated. It consists of getting the elements of the queue in order and sending each one, keeping track of not yet sent request. 
//Before sending a request we need to recreate it from the alternative representation stored in IndexedDB.


function flushQueue() {

//Get the queue


  return localforage.getItem('queue').then(function(queue) {
    /* eslint no-param-reassign: 0 */
    queue = queue || [];

//If empty, nothing to do!


    if (!queue.length) {
      return Promise.resolve();
    }

//Else, send the requests in order…


    console.log('Sending ', queue.length, ' requests...');
    return sendInOrder(queue).then(function() {

        //Requires error handling. Actually, this is assuming all the requests in queue are a success when reaching the Network. 
        //    So it should empty the queue step by step, only popping from the queue if the request completes with success.


      return localforage.setItem('queue', []);
    });
  });
}

//Send the requests inside the queue in order. Waiting for the current before sending the next one.


function sendInOrder(requests) {

//The reduce() chains one promise per serialized request, not allowing to progress to the next one until completing the current.


  var sending = requests.reduce(function(prevPromise, serialized) {
    console.log('Sending', serialized.method, serialized.url);
    return prevPromise.then(function() {
      return deserialize(serialized).then(function(request) {
        return fetch(request);
      });
    });
  }, Promise.resolve());
  return sending;
}

//Serialize is a little bit convolved due to headers is not a simple object.


function serialize(request) {
  var headers = {};

//for(... of ...) is ES6 notation but current browsers supporting SW, support this notation as well and this is the only way of retrieving all the headers.


  for (var entry of request.headers.entries()) {
    headers[entry[0]] = entry[1];
  }
  var serialized = {
    url: request.url,
    headers: headers,
    method: request.method,
    mode: request.mode,
    credentials: request.credentials,
    cache: request.cache,
    redirect: request.redirect,
    referrer: request.referrer
  };

//Only if method is not GET or HEAD is the request allowed to have body.


  if (request.method !== 'GET' && request.method !== 'HEAD') {
    return request.clone().text().then(function(body) {
      serialized.body = body;
      return Promise.resolve(serialized);
    });
  }
  return Promise.resolve(serialized);
}

//Compared, deserialize is pretty simple.


function deserialize(data) {
  return Promise.resolve(new Request(data.url, data));
}

var CACHE = 'cache-only';

// On install, cache some resources.
self.addEventListener('install', function(evt) {
    console.log('The service worker is being installed.');

    // Ask the service worker to keep installing until the returning promise
    // resolves.
    evt.waitUntil(precache());
});

// On fetch, use cache only strategy.
self.addEventListener('fetch', function(evt) {
    console.log('The service worker is serving the asset.');
    evt.respondWith(fromCache(evt.request));
});

// Open a cache and use `addAll()` with an array of assets to add all of them
// to the cache. Return a promise resolving when all the assets are added.
function precache() {
    return caches.open(CACHE).then(function (cache) {
        return cache.addAll([
          '/js/lib/ServiceWorkerWare.js',
          '/js/lib/localforage.js',
          '/js/settings.js'
        ]);
    });
}

// Open the cache where the assets were stored and search for the requested
// resource. Notice that in case of no matching, the promise still resolves
// but it does with `undefined` as value.
function fromCache(request) {
    return caches.open(CACHE).then(function (cache) {
        return cache.match(request).then(function (matching) {
            return matching || Promise.reject('no-match');
        });
    });
}

Here is the error message I am getting in Chrome when I go offline:

(A similar error occurred in Firefox - it falls over at line 409 of ServiceWorkerWare.js)

   ServiceWorkerWare.prototype.executeMiddleware = function (middleware, 
request) {
        var response = this.runMiddleware(middleware, 0, request, null);
        response.catch(function (error) { console.error(error); });
        return response;
    };

解决方案

Service Workers tend to cache the static HTML, CSS, JavaScript, and image files.

I need to use PouchDB and sync it with CouchDB

Why CouchDB?

  • CouchDB is a NoSQL database consisting of a number of Documents created with JSON.
  • It has versioning (each document has a _rev property with the last modified date)
  • It can be synchronised with PouchDB, a local JavaScript application that stores data in local storage via the browser using IndexedDB. This allows us to create offline applications.
  • The two databases are both "master" copies of the data.

PouchDB is a local JavaScript implementation of CouchDB.

这篇关于Service Worker 在浏览器离线时保存表单数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆