純前端實現保存表單數據功能

  • 2019 年 12 月 4 日
  • 筆記

本文作者:IMWeb zzbozheng 原文出處:IMWeb社區 未經同意,禁止轉載

最近在用管理後台配置數據時,發現辛辛苦苦配置好的表單無緣無故地被覆蓋,之後了解到由於我們都是在同一台開發機上做開發,難免會遇到其他同學做數據變更時覆蓋掉自己的配置數據。於是我決定在表單配置里增加一項「配置操作」功能來解放自己雙手以及惠及他人。

用什麼方式保存?

  1. 找後端同學去幫忙做保存?
  2. 把配置數據都保存到 localStorage?
  3. 把配置數據都保存到本地文本?

然而看到後端同學繁忙的景象之下,默默地放棄了,所以忽略第一點。如果把數據都保存到 localStorage,那麼我是不是還要做一個介面來管理這個配置數據的版本呢,而且還可以選中某個版本快速還原,但這些都需要一定的工作量,localStorage 的數據也不方便導出給別的同學。如果我只用前端技術直接把配置文件保存到本地,那前面兩個問題都不存在了,還會帶來一個好處就是:拿到這些文件,發布到現網時我可以直接導入,而後端同學只需要運行創建表文件和上傳相關的java文件就足夠了,減少後端同學的工作量。

實現方式

回想時以前做過的一個需求:當用戶點擊鏈接時是下載一個PDF文件,而不是直接使用自帶的PDF閱讀器打開。

使用a標籤的download屬性(Chrome和FF已支援),如:

<a href="/uploadfolder/lalala.pdf" download="filename.pdf">點擊下載</a>

這明顯需要伺服器來支援。我們知道href屬性可以是一個鏈接,也可以是一個錨點、偽協議。

但也可以是blobURI、dataURI、fileURI

如果要實現前端保存文本,那麼使用dataURI即可實現。 dataURI 的語法結構如下:

data:[<mediatype>][;base64],<data>

如果想了解更多關於data URI的知識,可以點擊這裡

下載指定的文本的需求就可以快速實現了:

<a href="data:text/plain,text_text_text_text" download="filename.txt">點擊下載</a>

如果想通過調用的方式來觸發,需要稍微封裝:

function downloadText (fileName, textCtx) {      var aTag;        if (fileName && textCtx) {          aTag = document.createElement('a');          aTag.setAttribute('href', 'data:text/plain,' + textCtx);          aTag.setAttribute('download',  fileName);          aTag.click();      }  }

雖然管理後台不需要支援IE,但這裡順便扯一下IE,IE要達到該效果需要藉助execCommand

基本思路是創建一個隱藏iframe並添加到頁面上,把需要保存的內容寫到iframe內並調起iframe的execCommand命令來保存頁面。

var ifr = document.createElement('iframe');    ifr.style.display = 'none';  document.body.appendChild(ifr);  ifr.contentWindow.(textCtx);  ifr.contentWindow.document.execCommand('SaveAs', false, fileName);  document.body.removeChild(ifr);

如果你想了解更多關於IE下的execCommand,可以點擊這裡

整理上述:

function downloadText (fileName, textCtx) {      var aTag, ifr;        if (fileName && textCtx) {          if (!~navigator.userAgent.indexOf('MSIE')) {              aTag = document.createElement('a');              aTag.setAttribute('href', 'data:text/plain,' + textCtx);              aTag.setAttribute('download',  fileName);              aTag.click();          } else {              ifr = document.createElement('iframe');              ifr.style.display = 'none';              document.body.appendChild(ifr);              ifr.contentWindow.(textCtx);              ifr.contentWindow.document.execCommand('SaveAs', false, fileName);              document.body.removeChild(ifr);          }      }  }  //let's go  downloadText('lala.txt', 'dolemifaso');

關於讀取

使用FileReader可以快速實現,基本思路是 input -> onchange -> new FileReader() -> onload -> readAsDataURL

關於FileRader和相關例子, 可以點擊這裡

關於發送

這裡使用了FormDataXMLHttpRequest 如果你想了解更多關於FormData, 可以點擊這裡

function postForm (formData) {      var separator, postTarget, rKV, oForm, oReq, keyVals;        if (formData && ~formData.indexOf(separator)) {          separator = '____|____'; //每個欄位的鏈接符,如: 'a:1____|____b:{"c":"d"}'          postTarget = 'http://test.com/update.do';          rKV = /([^:]+):([sS]+)/;          oForm = new FormData();          oReq = new XMLHttpRequest();          keyVals = formData.split(separator); //切割為 [a:1, b:{"c":"d"}]            keyVals.forEach(function (keyVal) {                  var pairs = keyVal.match(rKV);                    if (pairs) {                      oForm.append(pairs[1], pairs[2]);                  }          });            //here we go          oReq.open("POST", postTarget);          oReq.send(oForm);          oReq.onload = function (data) {              var resText, resData;                if (data.target.status === 200                  && data.target.readyState === 4) {                  resText = data.target.responseText;                  resData = JSON.parse(resText);                    if (resData.retcode === 0) {                      alert('導入配置成功');                  } else {                      alert('導入配置失敗:' + resText);                  }              }          }      } else {          alert('文件格式不合法');      }  }

全文完