- 原文地址:Offline-Friendly Forms
- 原文作者:mxbck
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:sunui
- 校對者:yanyixin、Tina92
網路不佳時網頁表單的表現通常並不理想。如果你試圖在離線狀態下提交表單,那就很可能丟失剛剛填好的資料。下面就看看我們是如何修復這個問題的。
太長,勿點:這裡是本文的 CodePen Demo。
隨著 Service Workers 的推行,現在開發者們甚至可以實現離線版的網頁了。靜態資源的快取相對容易,而像表單這樣需要伺服器互動的情況就很難優化了。即使這樣,提供一些有用的離線回退方案還是有可能的。
首先,我們為離線友好的表單建立一個新的類。接著我們儲存一些 <form>
元素的屬性然後繫結一個觸發 submit 事件的函式:
class OfflineForm {
// 配置例項。
constructor(form) {
this.id = form.id;
this.action = form.action;
this.data = {};
form.addEventListener('submit', e => this.handleSubmit(e));
}
}複製程式碼
在 submit 處理函式中,我們使用 navigator.onLine
屬性內建一個簡單的網路檢查器。瀏覽器對它的支援很好,而且實現它也不難。
⚠️ 但它還是有一定誤報的可能,因為這個屬性只能檢查客戶端是否連線到網路,而不能檢測實際的網路連通性。另一方面,一個 false
值意味著“離線”是相對確定的。因此,比起其他方式這個判斷方法是最好的。
如果一個使用者當前處於離線狀態,我們就暫停表單的提交,把資料儲存在本地。
handleSubmit(e) {
e.preventDefault();
// 解析表單輸入,儲存到物件中
this.getFormData();
if (!navigator.onLine) {
// 使用者離線,在裝置中儲存資料
this.storeData();
} else {
// 使用者線上,通過 ajax 傳送資料
this.sendData();
}
}複製程式碼
儲存表單資料
儲存資料到使用者裝置有幾種不同的方式。根據資料的不同,如果你不希望本地副本持久儲存在記憶體中,可以使用 sessionStorage
。在我們的例子中,我們可以一起使用 localStorage
。
我們可以給表單資料附上時間戳,把它賦值給一個新的物件,並且使用 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 上的全部示例。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、React、前端、後端、產品、設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。