Try   HackMD

透過Websocket提交Form無法抓到資料

tags: vue event loop two way binding nextTick Proxy form

需求

是要畫面顯示條碼,要給店家掃描的
店家掃描完,會有websocket來通知我的畫面做表單送出

解決思路

我這邊的作法是寫一個隱藏的form表單,
然後每一個input上面用vue的雙向綁定去綁定我要的資料,
然後給websocket一個callback去執行我表單送出

碰到的問題

從network的payload可以觀察到,我送出的form表單會抓不到我在data上設定的值

具體程式碼

  • 這邊有Codepen更詳細的程式碼可以做測試!
<template>
 <form ref="form" style="display: none;">
   <input name="data" v-model="data"/>
 </form>
</template>
const data = ref('');
const form = ref<HTMLFromElement>();

// 這個是給websocket呼叫的callback
function sendForm() {
    data.value = 'test data';
    form.value!.submit();
}

目前測試出的可行方法

1. 再設定完資料後,用await nextTick

async function sendForm() {
    data.value = 'test data';
    await nextTick();
    form.value!.submit();
}

2. 將data.value = 'test data'用非同步包起來

async function sendForm() {
    await (async () => data.value = 'test data')();
    form.value!.submit();
}

3. 在設定完資料後,再寫一個Promise

async function sendForm() {
    data.value = 'test data';
    await new Promise((res, rej) => {
        res();
    });
    form.value!.submit();
}

困惑的點及猜測

  1. 在可行方法中第一點,還相對好理解,反正就是等待vue該tick內的所有程序完成才將表單送出。

  2. 在可行方法中的第二點,是我猜測vue3用proxy在做setter,其實是個非同步方法?

    但因為我是使用typescript去寫,編譯器上也沒有提示他是一個非同步方法!

    另外我也不確定proxy的setter是否可以寫成非同步的方法。

  3. 在可行方法中的第三點,則是我完全無法理解的方法。

    我是依據我第二點的假設是成立,那麼setter應該是有機率在Promise完成後,setter卻還沒完成,因為setter我並沒有去await他!

    我這邊的猜測是因為Promise的執行時間的長度,可能剛好比vue當中的一個tick的執行時間還長,所以才導致form表單都是有抓到資料的。

  4. 該部分我在認知上認為他們兩個是等價的

function setData(data) {
  return new Promise((res, rej) => {
    setTimeout(() => {
      console.log(data);
    }, 2000);
  });
}

// 類似sendForm
(async () => {
  setData('data');
  const data = await new Promise((res, rej) => {
    res('test');
  });
  console.log(data2);
  // Result:
  // test
  // data
})();
async function sendForm() {
  data.value = 'test data';
  await new Promise((res, rej) => {
    res();
  });
  form.value!.submit();
}

結論

  1. 論點data.value = 'test',屬實是同步操作,而只是透過Proxy的setter去操作
  2. 論點Vue所實做的Proxy setter並不是個非同步的方法
  3. Proxy setter內會先做Assign value後再去呼叫畫面重新render,而這個部分是屬於Web engine實做,所以我們無法去預期render到底會何時做完
  4. 當Assign value後再加一個Promise做等待,或者是將Assign value丟進去Promise內等待,都會讓Form抓的到資料,,是因為當前Queue內沒有其餘的任務,所以才會有這種錯覺產生
  • 以下是在Event loop下執行的過程,感謝Hello大大的細心講解