本文翻譯自

Offline-Friendly Forms。

在網路不佳的情況下表單的表現並不理想。如果提交表單的時候恰好斷網了,辛苦填好的資料有可能就找不回來了。下面就分享一下我們是如何修復這個問題的。

先睹為快,CodePen 上的 Demo。

自從有了 Service Worker,開發者可以為使用者提供離線版的 Web 應用。靜態資源的快取相對比較容易,但對於需要和服務端互動的表單來說就有點困難了。不過還好,我們還是有辦法為離線提供一種 fallback 的方案。

首先,建立一個與離線友好表單相對應的 Class。將表單的 id、action 儲存下來,並繫結表單的 submit 事件。

class

OfflineForm

{

// setup the instance。

constructor

form

{

this

id

=

form

id

this

action

=

form

action

this

data

=

{};

form

addEventListener

‘submit’

e

=>

this

handleSubmit

e

));

}

}

在 submit 的處理函式中,可以使用 navigator。onLine 來檢查網路連結情況,這個 API 瀏覽器都支援得差不多了,就算要實現也比較簡單。

但是這個 API 存在誤報的可能。因為這個屬性只能表示存在網路連結,並不保證網路是通的。反過來,如果結果是 false 就可以明確表示是斷網的。因此據此判斷是否離線是一種相對靠譜的方式。

如果使用者處於離線狀態,我們將表單提 hold 住,把表單資料儲存在本地。

handleSubmit

e

{

e

preventDefault

();

// parse form inputs into data object。

this

getFormData

();

if

navigator

onLine

{

// user is offline, store data on device。

this

storeData

();

}

else

{

// user is online, send data via ajax。

this

sendData

();

}

}

儲存表單

我們有數種在使用者裝置商儲存資料的方式。根據資料的特點,你可以使用 sessionStorage,如果不想把資料儲存在記憶體種,也可儲存在localStorage 中。

給表單資料付上一個時間戳,掛在一個新物件上。然後使用 localStorage。setItem 儲存。這個方法接受兩個引數,

key

(表單 id) 和

value

(通常是一個 JSON 字串)。

storeData

()

{

// check if localStorage is available。

if

typeof

Storage

!==

‘undefined’

{

const

entry

=

{

time

new

Date

()。

getTime

(),

data

this

data

};

// save data as JSON string。

localStorage

setItem

this

id

JSON

stringify

entry

));

return

true

}

return

false

}

注意:可以在 Chrome 的開發者工具的 Application 標籤中檢視 storage 資料。如果不出差錯,裡面的內容如下:

如何製作離線友好的表單?

如何製作離線友好的表單?

還有最好將離線的情況告知使用者,告訴他們填寫的資料並不會丟失。補充 handleSubmit 方法,將這些資訊反饋給使用者。

如何製作離線友好的表單?

如何製作離線友好的表單?

考慮得真是周到呀!

檢查儲存的資料

一旦使用者聯網,就需要檢查一下本地是否存在未提交的表單。我們需要監聽 online 事件跟蹤網路連結的變化,或者頁面重新整理觸發的 load 事件。

constructor

form

){

。。。

window

addEventListener

‘online’

()

=>

this

checkStorage

());

window

addEventListener

‘load’

()

=>

this

checkStorage

());

}

當這些事件觸發時,我們只需檢查在 storage 中是否存在與表單 id 相匹配的資料。根據表單資料型別的不同,可能需要新增一個過期時間的判斷,只允許在特定的時間內的表單。這一點對於偶爾網路連結異常的情況很有效果,避免錯誤地把兩個月以前儲存在本地的表單提交。

checkStorage

()

{

if

typeof

Storage

!==

‘undefined’

{

// check if we have saved data in localStorage。

const

item

=

localStorage

getItem

this

id

);

const

entry

=

item

&&

JSON

parse

item

);

if

entry

{

// discard submissions older than one day。 (optional)

const

now

=

new

Date

()。

getTime

();

const

day

=

24

*

60

*

60

*

1000

if

now

-

day

>

entry

time

{

localStorage

removeItem

this

id

);

return

}

// we have valid form data, try to submit it。

this

data

=

entry

data

this

sendData

();

}

}

}

如果表單成功提交,則還需要最後一步,將表單資料從 localStorage 清除。比如說一個 Ajax 表單,我們可以在服務端成功返回後馬上實施清除動作。這裡簡單使用 storage removeItem() 方法即可。

sendData

()

{

// send ajax request to server

axios

post

this

action

this

data

then

((

response

=>

{

if

response

status

===

200

{

// remove stored data on success

localStorage

removeItem

this

id

);

}

})

catch

((

error

=>

{

console

warn

error

);

});

}

如果實在不想使用 Ajax 提交,另外一種處理方案就是將資料回填表單,直接呼叫 form。submit()提交,或者讓使用者自己點選提交按鈕提交。

注意:簡單起見,我略去了一些環節,比如表單驗證以及安全 token 驗證等等。這些步驟在真正的產品開發中是必不可少的。還有一個問題是關於敏感資料的處理,避免在本地明文儲存使用者密碼信用卡資訊等等。

如果有興趣,可以在 CodePen 裡檢視完整的例子。

如何製作離線友好的表單?

如何製作離線友好的表單?