vue v-for 列表更新導致 iframe 重新整理而狀態丟失的原因以及恢復狀態的方案

钰琪發表於2024-09-06

因為當列表資料變化,vue 重新渲染列表時,會導致受影響的列表項的 DOM 從頁面移除,而 iframe 被移除後再新增回頁面一定會重新整理。

如果向列表的資料陣列的開頭或中間插入元素,又或者移除元素,vue 會將操作位置對應的 DOM 元素和之後的 DOM 元素統統移除,然後在新增回去。

沒看過 vue 原始碼,但是可以推斷,這一過程可能是,先移除所有受影響的元素,然後再將可用的元素新增到合適的位置;這一過程也有可能是使用 Element.replaceWith() 完成的,而該 API 也會將 DOM 元素從頁面中移除。從頁面移除 iframe,iframe 的 contentWindow 會銷燬載入的內容,重新新增到頁面,contentWindow 會重新載入 iframe 指定的 src,這一來一回,就導致了 iframe 狀態丟失。

基於 vue 這個更新列表 DOM 的原理,最好不在列表中使用 iframe。

如果列表資料不變,或者將列表資料當作一個棧來使用,那麼不會有 DOM 被移除後重新新增回頁面,這種情況下可以在列表中使用 iframe 。

如果列表資料多變,不僅僅是隻運算元據尾部,還會向列表其它位置進行插入或刪除操作,可以考慮透過視窗通訊的方式來實現狀態的儲存與恢復。

  1. 同域名的網頁,可以直接取得 iframe 的 contentWindow 物件,然後就可以讀取狀態了。contentWindow unload 的時候儲存狀態,再將狀態附加到 iframe 的 src 屬性上,iframe 重新新增回頁面時,內部頁面透過 location.href 來恢復狀態。
  2. 跨域的頁面,使用 window.postMessage 來通訊,contentWindow unload 的時候將其狀態傳送給呼叫它的頁面,然後呼叫它的頁面將狀態資料格式化後附加到 iframe 的 src 屬性上。資料恢復的過程和同源網頁沒差。

儲存與恢復 iframe 狀態的做法還是有些複雜的,如果在 vue 列表中使用 iframe 是必要的,就好好設計一下 iframe 狀態的儲存與恢復的功能。
如果是不必要的,果斷放棄。

相關文章