該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。
你是否有在使用網頁的時候填了很多資料,但是一個不小心重整網頁就導致辛苦填寫的資料都消失不見了,今天我們就要來針對輸入表單的使用者體驗來進行優化的部分。
我們希望可以達到像是下面的效果。
你會發現在我輸入了內容之後重新整理網頁,表單裡面的內容都還有留下,所以今天不管我怎麼輸入內容,他都會暫存在你的網頁之中,今天我們就要來看看再 Vue 裡面是如何實作的。
暫存的部分你可以選用 cookie
或是 localStorage
,在這邊我選用 localStorage
來暫存。
純 JavaSrcipt 這邊我就提供一個思路,不全部寫出來了
// 1. 抓取 input 表單實體
const el = document.getElementById("#name");
// 3. localStorage 寫入暫存資料
const handSave = (e) => {
localStorage.setItem('form-name', e.target.value);
}
// 2. 監聽 input 跟 change 事件,不監聽 keyUp 是因為手機鍵盤無法觸發
el.addEventListener("input", handSave);
el.addEventListener("change", handSave);
總之就是去監控表單是否有被填入內容,如果觸發了 input
或是 change
,就把輸入的內容給寫入 localStorage
。
首先針對表單一個個去綁定事件會有點太重工了,而且要處理也不是這麼方便,所以這時候我就有一個想法,我們不是透過 v-model
可以去同步表單的資料嗎?那如果再有一個跟 v-model
相似的語法,但是具有暫存的功能的話,那不是方便很多,所以今天就來自己寫一個有暫存功能的 v-model
。
我希望這樣,有一個 v-model-save
的語法,它的功能跟 v-model
一模一樣,然後還增加了儲存 localStorage
的功能,我們來看一下怎麼做。
const name = ref("");
<input
class="name"
type="text"
placeholder="輸入使用者名稱"
v-model-save="name"
/>
首先要做這樣一個 v-model-save
語法就需要我們之前所教的 vue 的 directive
這個功能,首先在 directive
裡面我們要去監聽 input
跟 change
事件。
app.directive('model-save', {
mounted(el, binding) {
el.handSave = (e) => {}
el.addEventListener("input", el.handSave);
el.addEventListener("change", el.handSave);
},
unmounted(el) {
el.removeEventListener("input", el.handSave);
el.removeEventListener("change", el.handSave);
},
})
在這邊你會看到我在 mounted
傳進來的 el
塞入一個 handSave
的 function,而不是我們一般去定義新的function 那樣,為什麼? 因為我需要在 unmounted
去 removeEventListener
,但是我又不想把這個 function 丟到塞到 vue 或是 window 裡面,所以這邊我選擇塞到 el
裡面。
接下來我們要來定義一下儲存的部分,這邊有要定義三個儲存的 function
// 1. 寫入 Input DOM 的 value
const setInputValue = (value) => el.value = value;
// 2. 同步 Vue 中宣告的的變數(資料)
const setSyncDate = (value) => binding.instance[el.className] = value;
// 3. 塞入 localStorage 暫存資料
const setLocalStorage = (value) => localStorage.setItem(`form-${el.className}`, value);
這邊有個重點,就是你的 class name
!
在這邊我們是透過你的 class name
還有你宣告變數的名稱,讓它們保持一致來做比對,讓它可以更簡單的去同步表單上面以及變數上面的資料。
你會看到我使用了 binding.instance
,這個 instance
裡面就是你透過 directive 傳入的 proxy
物件,所以只去修改裡面的 proxy
物件參數,自然上層傳入的資料就會跟者被修改了。
app.directive('model-save', {
mounted(el, binding) {
const setInputValue = (value) => el.value = value;
const setSyncDate = (value) => binding.instance[el.className] = value;
const setLocalStorage = (value) => localStorage.setItem(`form-${el.className}`, value);
// 檢查目前是 localStorage 有沒有暫存的資料
if(!localStorage[`form-${el.className}`]){
setLocalStorage(binding.value);
}
setInputValue(localStorage[`form-${el.className}`]);
setSyncDate(el.value);
el.handSave = (e) => {
setLocalStorage(e.target.value);
setSyncDate(e.target.value);
}
el.addEventListener("input", el.handSave);
el.addEventListener("change", el.handSave);
},
unmounted(el) {
el.removeEventListener("input", el.handSave);
el.removeEventListener("change", el.handSave);
},
})
在這邊我會特別在 localStorage 儲存的 key
前面加上一個自己定義的字串,去做區分,畢竟整個網站還會有其他地方用到localStorage,避免 key
的衝突,所以我前面就會加一個前綴字做區分。
接下來你就可以把你要的資料都定義好
setup(){
const name = ref("");
const password = ref("");
const submitLogin = () => {
console.log({name, password})
for (var key in localStorage) {
if (/^form-(.+)/.test(key)) {
localStorage.removeItem(key)
}
}
alert("登入成功");
}
return {
name,
password,
submitLogin
}
}
這邊你會看到我在 submitLogin
的 function 中去清掉了跟表單有關的 localStorage,因為畢竟表單送出了,自然暫存也就不需要了,那接下來我們就可以在 input 上面使用 v-model-save
。
<div class="center">
<div class="input-box">
<p>NAME</p>
<input
class="name"
type="text"
placeholder="輸入使用者名稱"
v-model-save="name"
/>
</div>
<div class="input-box mb-20">
<p>PASSWORD</p>
<input
class="password"
type="password"
placeholder="輸入密碼"
v-model-save="password"
/>
</div>
<button class="btn" @click="submitLogin">登入</button>
</div>
codepen 範例:https://codepen.io/MikeCheng1208/pen/vYZQeVq
在 Safari 上 cookie 跟 localStorage 的使用最多就是 7 天,所以如果你的暫存功能是會需要一個很長時間的話,可能會需要改變其他做法,相關內容可以參考一下文件。
Intelligent Tracking Prevention 2.1 : https://webkit.org/blog/8613/intelligent-tracking-prevention-2-1/
Apple adds a 7-Day Cap on All Script-Writable Storage : https://support.didomi.io/apple-adds-a-7-day-cap-on-all-script-writable-storage
今天介紹了一個很實際可以防呆且增加使用者體驗的方式,透過 directive
能玩的花樣其實還蠻多的,就看我們自己在實作上面要依照何種情境來做選擇,如果你有其他 directive
的使用方式也可以在下面留言分享給大家知道。
Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。
我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/bundles/9WwPNYRpz?s=tc
那如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/bundles/b9Rovqy7z?s=tc
Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng