vue v-for 列表更新导致 iframe 刷新而状态丢失的原因以及恢复状态的方案
因为当列表数据变化,vue 重新渲染列表时,会导致受影响的列表项的 DOM 从页面移除,而 iframe 被移除后再添加回页面一定会刷新。
如果向列表的数据数组的开头或中间插入元素,又或者移除元素,vue 会将操作位置对应的 DOM 元素和之后的 DOM 元素统统移除,然后在添加回去。
没看过 vue 源码,但是可以推断,这一过程可能是,先移除所有受影响的元素,然后再将可用的元素添加到合适的位置;这一过程也有可能是使用 Element.replaceWith()
完成的,而该 API 也会将 DOM 元素从页面中移除。从页面移除 iframe,iframe 的 contentWindow 会销毁加载的内容,重新添加到页面,contentWindow 会重新加载 iframe 指定的 src,这一来一回,就导致了 iframe 状态丢失。
基于 vue 这个更新列表 DOM 的原理,最好不在列表中使用 iframe。
如果列表数据不变,或者将列表数据当作一个栈来使用,那么不会有 DOM 被移除后重新添加回页面,这种情况下可以在列表中使用 iframe 。
如果列表数据多变,不仅仅是只操作数据尾部,还会向列表其它位置进行插入或删除操作,可以考虑通过窗口通信的方式来实现状态的保存与恢复。
- 同域名的网页,可以直接取得 iframe 的 contentWindow 对象,然后就可以读取状态了。contentWindow unload 的时候保存状态,再将状态附加到 iframe 的 src 属性上,iframe 重新添加回页面时,内部页面通过 location.href 来恢复状态。
- 跨域的页面,使用
window.postMessage
来通信,contentWindow unload 的时候将其状态发送给调用它的页面,然后调用它的页面将状态数据格式化后附加到 iframe 的 src 属性上。数据恢复的过程和同源网页没差。
保存与恢复 iframe 状态的做法还是有些复杂的,如果在 vue 列表中使用 iframe 是必要的,就好好设计一下 iframe 状态的保存与恢复的功能。
如果是不必要的,果断放弃。