iT邦幫忙

2021 iThome 鐵人賽

DAY 30
1
Modern Web

[ 重構倒數30天,你的網站不Vue白不Vue ] 系列 第 30

[重構倒數第01天] - Vue的表單自動暫存

前言

該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。

你是否有在使用網頁的時候填了很多資料,但是一個不小心重整網頁就導致辛苦填寫的資料都消失不見了,今天我們就要來針對輸入表單的使用者體驗來進行優化的部分。

我們希望可以達到像是下面的效果。

Vue mike

你會發現在我輸入了內容之後重新整理網頁,表單裡面的內容都還有留下,所以今天不管我怎麼輸入內容,他都會暫存在你的網頁之中,今天我們就要來看看再 Vue 裡面是如何實作的。

暫存的部分你可以選用 cookie 或是 localStorage,在這邊我選用 localStorage 來暫存。

用純 JavaSrcipt 是如何實作?

純 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

那再 Vue 的實作呢?

首先針對表單一個個去綁定事件會有點太重工了,而且要處理也不是這麼方便,所以這時候我就有一個想法,我們不是透過 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裡面我們要去監聽 inputchange事件。

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 那樣,為什麼? 因為我需要在 unmountedremoveEventListener ,但是我又不想把這個 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 上 cookielocalStorage 的使用最多就是 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的使用方式也可以在下面留言分享給大家知道。

QRcode

那如果對於Vue3不夠熟的話呢?

Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。

我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/bundles/9WwPNYRpz?s=tc

那如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/bundles/b9Rovqy7z?s=tc

訂閱Mike的頻道享受精彩的教學與分享

Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng


上一篇
[重構倒數第02天] - Slots 與 Render Functions 的進階心法
下一篇
[重構倒數第0天] - 鐵人賽後記
系列文
[ 重構倒數30天,你的網站不Vue白不Vue ] 32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言