[译] 离线友好的表单

网络不佳时网页表单的表现一般并不理想。若是你试图在离线状态下提交表单,那就极可能丢失刚刚填好的数据。下面就看看咱们是如何修复这个问题的。前端

太长,勿点:这里是本文的 CodePen Demoreact

随着 Service Workers 的推行,如今开发者们甚至能够实现离线版的网页了。静态资源的缓存相对容易,而像表单这样须要服务器交互的状况就很难优化了。即便这样,提供一些有用的离线回退方案仍是有可能的。android

首先,咱们为离线友好的表单建立一个新的类。接着咱们保存一些 <form> 元素的属性而后绑定一个触发 submit 事件的函数:ios

class OfflineForm {
  // 配置实例。
  constructor(form) {
    this.id = form.id;
    this.action = form.action;
    this.data = {};

    form.addEventListener('submit', e => this.handleSubmit(e));
  }
}复制代码

在 submit 处理函数中,咱们使用 navigator.onLine 属性内置一个简单的网络检查器。浏览器对它的支持很好,并且实现它也不难。git

⚠️ 但它仍是有必定误报的可能,由于这个属性只能检查客户端是否链接到网络,而不能检测实际的网络连通性。另外一方面,一个 false 值意味着“离线”是相对肯定的。所以,比起其余方式这个判断方法是最好的。github

若是一个用户当前处于离线状态,咱们就暂停表单的提交,把数据存储在本地。ajax

handleSubmit(e) {
  e.preventDefault();
  // 解析表单输入,存储到对象中
  this.getFormData();

  if (!navigator.onLine) {
    // 用户离线,在设备中存储数据
    this.storeData();
  } else {
    // 用户在线,经过 ajax 发送数据 
    this.sendData();
  }
}复制代码

存储表单数据

存储数据到用户设备有几种不一样的方式。根据数据的不一样,若是你不但愿本地副本持久存储在内存中,可使用 sessionStorage。在咱们的例子中,咱们能够一块儿使用 localStorageaxios

咱们能够给表单数据附上时间戳,把它赋值给一个新的对象,而且使用 localStorage.setItem 保存。这个方法接受两个参数:key(表单 id)和 value(数据的 JSON 串)。后端

storeData() {
  // 检测 localStorage 是否可用
  if (typeof Storage !== 'undefined') {
    const entry = {
      time: new Date().getTime(),
      data: this.data,
    };
    // 把数据存储为 JSON 串
    localStorage.setItem(this.id, JSON.stringify(entry));
    return true;
  }
  return false;
}复制代码

提示:你能够在 Chrome 的开发者工具 “Application” 中查看存储数据。若是不出差错,你能够看到内容以下:浏览器

通知用户发生了什么也是个好主意,这样他们会知道他们的数据不会丢失。咱们能够扩展 handleSubmit 函数来显示某些反馈信息。

多么周到的表单!

检查保存的数据

一旦用户联网,咱们想检查一下是否有被存储的提交。咱们能够监听 online 事件来捕获网络连接的改变,还有页面刷新时的 load 事件:

constructor(form){
  ...
  window.addEventListener('online', () => this.checkStorage());
  window.addEventListener('load', () => this.checkStorage());
}复制代码
checkStorage() {
  if (typeof Storage !== 'undefined') {
    // 检测咱们是否在 localStorage 之中存储了数据
    const item = localStorage.getItem(this.id);
    const entry = item && JSON.parse(item);

    if (entry) {
      // 舍弃超过一天的提交。 (可选)
      const now = new Date().getTime();
      const day = 24 * 60 * 60 * 1000;
      if (now - day > entry.time) {
        localStorage.removeItem(this.id);
        return;
      }

      // 咱们已经验证了表单数据,尝试提交它
      this.data = entry.data;
      this.sendData();
    }
  }
}复制代码

一旦咱们成功提交了表单,那最后一步就是移除 localStorage 中的数据,来避免重复提交。假设是一个 ajax 表单,咱们能够在服务器响应成功的回调里作这件事。很简单,这里咱们可使用 storage 对象的 removeItem() 方法。

sendData() {
  // 向服务器发送 ajax 请求
  axios.post(this.action, this.data)
    .then((response) => {
      if (response.status === 200) {
        // 成功时移除存储的数据
        localStorage.removeItem(this.id);
      }
    })
    .catch((error) => {
      console.warn(error);
    });
}复制代码

若是你不想使用 ajax 提交,另外一个方案是将存储的数据回填到表单,而后调用 form.submit() 或让用户本身点击提交按钮。

☝️ 注意:简单起见,我在这个案例中省略了一些其余部分,好比表单验证和安全 token 验证等,这些东西在真正的生产环境是必不可少的。这里的另外一个问题是处理敏感数据,就是说你不能在本地存储一些密码或者信用卡数据等私密信息。

若是你感兴趣,请查阅 CodePen 上的所有示例


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索