[譯] 離線友好的表單

網絡不佳時網頁表單的表現一般並不理想。若是你試圖在離線狀態下提交表單,那就極可能丟失剛剛填好的數據。下面就看看咱們是如何修復這個問題的。前端

太長,勿點:這裏是本文的 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前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索