keep-alive-iframe
Version:
A Vue component for managing iframe lifecycle with keep-alive functionality
322 lines (321 loc) • 9.47 kB
JavaScript
var H = Object.defineProperty;
var U = (o, e, t) => e in o ? H(o, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : o[e] = t;
var d = (o, e, t) => U(o, typeof e != "symbol" ? e + "" : e, t);
import { defineComponent as b, useTemplateRef as T, ref as z, watch as C, onActivated as j, onDeactivated as B, onMounted as K, onUnmounted as X, createElementBlock as Z, openBlock as _, renderSlot as x, createCommentVNode as D, createElementVNode as v, normalizeStyle as O } from "vue";
import { useResizeObserver as N, useThrottleFn as V } from "@vueuse/core";
class n {
// 最大缓存数量
static updateLastUsed(e) {
const t = this.frameMap.get(e);
t && (t.lastUsed = Date.now());
}
static enforceCacheLimit() {
if (this.frameMap.size <= this.MAX_CACHE_SIZE) return;
const e = Array.from(this.frameMap.entries()).sort(([, t], [, i]) => t.lastUsed - i.lastUsed);
for (; this.frameMap.size > this.MAX_CACHE_SIZE; ) {
const [t] = e.shift();
this.destroy(t);
}
}
static create(e) {
const { uid: t } = e, i = this.get(t);
i && i.destroy();
const r = new P(e);
return this.frameMap.set(t, {
frame: r,
lastUsed: Date.now()
}), this.enforceCacheLimit(), r;
}
static destroy(e) {
const t = this.frameMap.get(e);
t && (t.frame.destroy(), this.frameMap.delete(e));
}
static show(e) {
const t = this.frameMap.get(e);
t && (t.frame.show(), this.updateLastUsed(e));
}
static hide(e) {
const t = this.frameMap.get(e);
t && (t.frame.hide(), this.updateLastUsed(e));
}
static resize(e, t) {
const i = this.frameMap.get(e);
i && (i.frame.resize(t), this.updateLastUsed(e));
}
static update(e, t) {
const i = this.frameMap.get(e);
i && (i.frame.update(t), this.updateLastUsed(e));
}
static get(e) {
const t = this.frameMap.get(e);
if (t)
return this.updateLastUsed(e), t.frame;
}
static clear() {
this.frameMap.forEach((e) => e.frame.destroy()), this.frameMap.clear();
}
// 设置最大缓存数量
static setMaxCacheSize(e) {
if (e < 1) {
A("缓存大小必须大于0");
return;
}
this.MAX_CACHE_SIZE = e, this.enforceCacheLimit();
}
}
d(n, "frameMap", /* @__PURE__ */ new Map()), d(n, "MAX_CACHE_SIZE", 10);
class P {
constructor(e) {
d(this, "el", null);
d(this, "options");
d(this, "originalRect", { width: 0, height: 0, top: 0, left: 0 });
d(this, "scrollHandler", null);
this.options = e, this.init();
}
init() {
const { src: e, zIndex: t, attrs: i, onLoaded: r, onError: l, keepAlive: a, container: f, parentContainer: p } = this.options;
if (!e) {
A("请填写iframe的src");
return;
}
try {
this.el = document.createElement("iframe"), this.el.src = e, this.el.style.setProperty("z-index", t.toString()), this.el.classList.add("keep-alive-frame"), r && (this.el.onload = r), l && (this.el.onerror = l), this.setAttrs(i), this.resize(this.options), a ? (document.body.appendChild(this.el), p && this.addScrollListener(p)) : f && f.appendChild(this.el);
} catch (h) {
A(`初始化iframe失败: ${h instanceof Error ? h.message : String(h)}`);
}
}
resize(e) {
if (!this.el) return;
const { left: t, top: i, width: r, height: l } = e;
if (this.originalRect = { left: t, top: i, width: r, height: l }, this.options.keepAlive)
if (this.options.parentContainer) {
const a = this.options.parentContainer.scrollTop, f = this.options.parentContainer.scrollLeft;
this.setStyle({
position: "fixed",
left: `${t - f}px`,
top: `${i - a}px`,
width: `${r}px`,
height: `${l}px`
});
} else
this.setStyle({
position: "fixed",
left: `${t}px`,
top: `${i}px`,
width: `${r}px`,
height: `${l}px`
});
else
this.setStyle({
width: "100%",
height: "100%"
});
}
destroy() {
this.el && (this.el.onload = null, this.el.onerror = null, this.el.remove(), this.el = null, this.scrollHandler && this.options.parentContainer && (this.options.parentContainer.removeEventListener("scroll", this.scrollHandler), this.scrollHandler = null));
}
show() {
this.el && this.el.classList.remove("is-hidden");
}
hide() {
this.el && this.el.classList.add("is-hidden");
}
update(e) {
this.el && (this.el.src = e);
}
setStyle(e) {
this.el && Object.assign(this.el.style, e);
}
setAttrs(e) {
this.el && Object.entries(e).forEach(([t, i]) => {
this.el && this.el.setAttribute(t, String(i));
});
}
addScrollListener(e) {
this.el && (this.scrollHandler = () => {
if (!this.el || !this.options.keepAlive) return;
const t = e.scrollTop, i = e.scrollLeft;
this.setStyle({
position: "fixed",
left: `${this.originalRect.left - i}px`,
top: `${this.originalRect.top - t}px`,
width: `${this.originalRect.width}px`,
height: `${this.originalRect.height}px`
});
}, e.addEventListener("scroll", this.scrollHandler));
}
getEl() {
return this.el;
}
}
let q = 0;
function G() {
return `iframe_${q++}`;
}
function A(o) {
console.error(`[KeepAliveFrame]: ${o}`);
}
const Y = /* @__PURE__ */ b({
__name: "KeepAliveFrame",
props: {
src: {},
keepAlive: { type: Boolean, default: !0 },
iframeAttrs: {},
maxCacheSize: { default: 10 },
parentContainer: {},
zIndex: { default: 0 }
},
emits: ["load", "error", "activated", "deactivated", "destroy", "resize", "cacheHit", "cacheMiss"],
setup(o, { expose: e, emit: t }) {
const i = o, r = t, l = T("iframeContainerRef"), a = G(), f = z(!1), p = z(!1);
let h = !1, u = !1;
function w() {
!n.get(a) && i.src && m();
}
e({
getFrame: () => {
var s;
return (s = n.get(a)) == null ? void 0 : s.getEl();
}
}), N(
l,
V(S, 300, !0)
), C(l, (s) => {
i.src && s ? m() : g();
}), C(() => i.src, (s) => {
M(s);
}), C(() => i.maxCacheSize, (s) => {
n.setMaxCacheSize(s);
}), j(() => {
if (u = !0, i.keepAlive) {
w(), L(), r("activated");
return;
}
m(), r("activated");
}), B(() => {
if (u = !1, i.keepAlive) {
E(), r("deactivated");
return;
}
g(), r("deactivated"), h = !1;
}), K(() => {
u = !0, w(), n.setMaxCacheSize(i.maxCacheSize);
}), X(() => {
g(), h = !1, u = !1, r("destroy");
});
function m() {
g(), f.value = !0, p.value = !1;
const {
width: s,
height: c,
left: F,
top: I
} = y(), R = n.get(a);
r(R ? "cacheHit" : "cacheMiss"), n.create({
uid: a,
width: s,
height: c,
left: F,
top: I,
src: i.src,
zIndex: i.zIndex || 0,
attrs: i.iframeAttrs || {},
onLoaded: k,
onError: $,
keepAlive: i.keepAlive,
container: i.keepAlive ? void 0 : l.value,
parentContainer: i.parentContainer
});
}
function g() {
n.destroy(a);
}
function M(s) {
n.get(a) ? n.update(a, s) : m();
}
function L() {
n.get(a) ? n.show(a) : m();
}
function E() {
n.get(a) && n.hide(a);
}
function S() {
n.resize(a, y()), h && u && r("resize", y());
}
function k(s) {
f.value = !1, h = !0, r("load", s);
}
function $(s) {
f.value = !1, p.value = !0, r("error", s);
}
function y() {
var s;
return ((s = l.value) == null ? void 0 : s.getBoundingClientRect()) || {
width: 0,
height: 0,
top: 0,
left: 0
};
}
return (s, c) => (_(), Z(
"div",
{
ref_key: "iframeContainerRef",
ref: l,
class: "relative w-full h-full",
role: "keep-alive-frame-container"
},
[
s.src ? f.value ? x(s.$slots, "loading", {
key: 1,
zIndex: s.zIndex + 1
}, () => [
v(
"div",
{
class: "w-full h-full absolute left-0 top-0 inset-0 bg-white/80 backdrop-blur-sm flex items-center justify-center",
style: O({ zIndex: s.zIndex + 1 })
},
c[1] || (c[1] = [
v(
"div",
{ class: "keep-alive-loading-spinner" },
null,
-1
/* HOISTED */
)
]),
4
/* STYLE */
)
]) : p.value ? x(s.$slots, "error", { key: 2 }, () => [
c[2] || (c[2] = v(
"div",
{ class: "flex justify-center items-center w-full h-full text-gray-500" },
" 出错了! ",
-1
/* HOISTED */
))
]) : D("v-if", !0) : x(s.$slots, "empty", { key: 0 }, () => [
c[0] || (c[0] = v(
"div",
{ class: "flex justify-center items-center w-full h-full text-gray-500" },
" 请输入iframe的地址 ",
-1
/* HOISTED */
))
])
],
512
/* NEED_PATCH */
));
}
});
export {
n as FrameManager,
P as KAliveFrame,
Y as KeepAliveFrame,
Y as default,
G as generateId
};
//# sourceMappingURL=keep-alive-iframe.es.js.map