UNPKG

keep-alive-iframe

Version:

A Vue component for managing iframe lifecycle with keep-alive functionality

3 lines (2 loc) 6.87 kB
(function(n,i){typeof exports=="object"&&typeof module<"u"?i(exports,require("vue"),require("@vueuse/core")):typeof define=="function"&&define.amd?define(["exports","vue","@vueuse/core"],i):(n=typeof globalThis<"u"?globalThis:n||self,i(n.KeepAliveIframe={},n.Vue,n.VueUse))})(this,function(n,i,p){"use strict";var T=Object.defineProperty;var j=(n,i,p)=>i in n?T(n,i,{enumerable:!0,configurable:!0,writable:!0,value:p}):n[i]=p;var m=(n,i,p)=>j(n,typeof i!="symbol"?i+"":i,p);class l{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],[,s])=>t.lastUsed-s.lastUsed);for(;this.frameMap.size>this.MAX_CACHE_SIZE;){const[t]=e.shift();this.destroy(t)}}static create(e){const{uid:t}=e,s=this.get(t);s&&s.destroy();const a=new w(e);return this.frameMap.set(t,{frame:a,lastUsed:Date.now()}),this.enforceCacheLimit(),a}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 s=this.frameMap.get(e);s&&(s.frame.resize(t),this.updateLastUsed(e))}static update(e,t){const s=this.frameMap.get(e);s&&(s.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){x("缓存大小必须大于0");return}this.MAX_CACHE_SIZE=e,this.enforceCacheLimit()}}m(l,"frameMap",new Map),m(l,"MAX_CACHE_SIZE",10);class w{constructor(e){m(this,"el",null);m(this,"options");m(this,"originalRect",{width:0,height:0,top:0,left:0});m(this,"scrollHandler",null);this.options=e,this.init()}init(){const{src:e,zIndex:t,attrs:s,onLoaded:a,onError:f,keepAlive:o,container:c,parentContainer:u}=this.options;if(!e){x("请填写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"),a&&(this.el.onload=a),f&&(this.el.onerror=f),this.setAttrs(s),this.resize(this.options),o?(document.body.appendChild(this.el),u&&this.addScrollListener(u)):c&&c.appendChild(this.el)}catch(d){x(`初始化iframe失败: ${d instanceof Error?d.message:String(d)}`)}}resize(e){if(!this.el)return;const{left:t,top:s,width:a,height:f}=e;if(this.originalRect={left:t,top:s,width:a,height:f},this.options.keepAlive)if(this.options.parentContainer){const o=this.options.parentContainer.scrollTop,c=this.options.parentContainer.scrollLeft;this.setStyle({position:"fixed",left:`${t-c}px`,top:`${s-o}px`,width:`${a}px`,height:`${f}px`})}else this.setStyle({position:"fixed",left:`${t}px`,top:`${s}px`,width:`${a}px`,height:`${f}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,s])=>{this.el&&this.el.setAttribute(t,String(s))})}addScrollListener(e){this.el&&(this.scrollHandler=()=>{if(!this.el||!this.options.keepAlive)return;const t=e.scrollTop,s=e.scrollLeft;this.setStyle({position:"fixed",left:`${this.originalRect.left-s}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 E=0;function M(){return`iframe_${E++}`}function x(g){console.error(`[KeepAliveFrame]: ${g}`)}const z=i.defineComponent({__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(g,{expose:e,emit:t}){const s=g,a=t,f=i.useTemplateRef("iframeContainerRef"),o=M(),c=i.ref(!1),u=i.ref(!1);let d=!1,y=!1;function S(){!l.get(o)&&s.src&&v()}e({getFrame:()=>{var r;return(r=l.get(o))==null?void 0:r.getEl()}}),p.useResizeObserver(f,p.useThrottleFn(F,300,!0)),i.watch(f,r=>{s.src&&r?v():C()}),i.watch(()=>s.src,r=>{L(r)}),i.watch(()=>s.maxCacheSize,r=>{l.setMaxCacheSize(r)}),i.onActivated(()=>{if(y=!0,s.keepAlive){S(),k(),a("activated");return}v(),a("activated")}),i.onDeactivated(()=>{if(y=!1,s.keepAlive){$(),a("deactivated");return}C(),a("deactivated"),d=!1}),i.onMounted(()=>{y=!0,S(),l.setMaxCacheSize(s.maxCacheSize)}),i.onUnmounted(()=>{C(),d=!1,y=!1,a("destroy")});function v(){C(),c.value=!0,u.value=!1;const{width:r,height:h,left:H,top:U}=A(),b=l.get(o);a(b?"cacheHit":"cacheMiss"),l.create({uid:o,width:r,height:h,left:H,top:U,src:s.src,zIndex:s.zIndex||0,attrs:s.iframeAttrs||{},onLoaded:I,onError:R,keepAlive:s.keepAlive,container:s.keepAlive?void 0:f.value,parentContainer:s.parentContainer})}function C(){l.destroy(o)}function L(r){l.get(o)?l.update(o,r):v()}function k(){l.get(o)?l.show(o):v()}function $(){l.get(o)&&l.hide(o)}function F(){l.resize(o,A()),d&&y&&a("resize",A())}function I(r){c.value=!1,d=!0,a("load",r)}function R(r){c.value=!1,u.value=!0,a("error",r)}function A(){var r;return((r=f.value)==null?void 0:r.getBoundingClientRect())||{width:0,height:0,top:0,left:0}}return(r,h)=>(i.openBlock(),i.createElementBlock("div",{ref_key:"iframeContainerRef",ref:f,class:"relative w-full h-full",role:"keep-alive-frame-container"},[r.src?c.value?i.renderSlot(r.$slots,"loading",{key:1,zIndex:r.zIndex+1},()=>[i.createElementVNode("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:i.normalizeStyle({zIndex:r.zIndex+1})},h[1]||(h[1]=[i.createElementVNode("div",{class:"keep-alive-loading-spinner"},null,-1)]),4)]):u.value?i.renderSlot(r.$slots,"error",{key:2},()=>[h[2]||(h[2]=i.createElementVNode("div",{class:"flex justify-center items-center w-full h-full text-gray-500"}," 出错了! ",-1))]):i.createCommentVNode("v-if",!0):i.renderSlot(r.$slots,"empty",{key:0},()=>[h[0]||(h[0]=i.createElementVNode("div",{class:"flex justify-center items-center w-full h-full text-gray-500"}," 请输入iframe的地址 ",-1))])],512))}});n.FrameManager=l,n.KAliveFrame=w,n.KeepAliveFrame=z,n.default=z,n.generateId=M,Object.defineProperties(n,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); //# sourceMappingURL=keep-alive-iframe.umd.js.map