[重构倒数第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


<<:  DAY 18- 杂凑函数 SHA-256

>>:  [Day-15] - Spring 标示说明性注解运用与设计

自我笔记 - django 系列 [基础篇]

django 基础篇 主要说明如何创建一个基本的 django 服务。 基本工具 env虚拟机 - ...

[day20]谈购物流程设计

本来想除了管理功能外全部都在Line介面里面解决,但做了一阵子觉得越想越不对劲,重新考量了一下思路,...

【D20】修改食谱#1:根据市价,模拟改价

前言 假日没有行情,所以只能平日来做取得行情资料的工作,所以今天的文章是根据期货行情,模拟价格修改的...

[Day19] Tableau 轻松学 - Data Extract

前言 每当我们修改工作表或者仪表板的时候,Tableau Desktop 会立即进行运算以显示出对应...

如何快速上手第三方套件

在现在这种讲求快速开发的开发模式,我们通常不太会自己将所有功能都自己硬刻出来,而是会去使用第三方的套...