小白前端别慌:iframe到底能干啥?3天搞懂用法+避坑指南(附实战技巧)

小白前端别慌:iframe到底能干啥?3天搞懂用法+避坑指南(附实战技巧)
在这里插入图片描述


小白前端别慌:iframe到底能干啥?3天搞懂用法+避坑指南(附实战技巧)

小白前端别慌:iframe到底能干啥?3天搞懂用法+避坑指南(附实战技巧)

说实话,我刚入行那会儿看到项目里满屏的iframe,心里直犯嘀咕:这玩意儿不是上古遗物吗?咋还这么多人用呢?后来踩了无数个坑才明白,iframe这货就像你家那把用了十年的螺丝刀——看着土,关键时刻还真离不开。

为啥老项目里总藏着一堆iframe

这事儿得从互联网的古早年代说起。那会儿前后端还没分家,PHP、JSP、ASP满天飞,一个页面可能就是几十个表格拼起来的。后来业务越来越复杂,团队开始搞"模块化"——说白了就是把页面切成一块一块的,你管你的用户中心,我管我的订单列表,最后找个"容器"把它们糊在一起。

iframe就是那个容器。

我见过最离谱的项目,一个后台管理系统里嵌了二十多个iframe,点左侧菜单就切换不同的iframe的src。当时负责维护的老哥跟我说:“这系统比我工龄还长,你敢动架构,业务部门就敢跟你拼命。”

所以别嫌弃这些"老古董",它们可能是某个时代的最优解。现在虽然微前端、模块联邦这些新概念很火,但iframe依然稳如老狗地躺在无数代码库里,等着你去维护。

iframe是啥玩意儿——浏览器里的"套娃神器"

官方定义说iframe是"内联框架",用来在当前页面里嵌另一个HTML文档。但我觉得这解释太正经了,不如叫它"浏览器套娃"——你在一个网页里打开另一个网页,那个网页里可能还套着第三个网页…

技术上讲,每个iframe都会创建一个独立的浏览上下文。啥意思?就是它有自己独立的window、document,甚至自己的JavaScript执行环境。这就像是给里面的内容开了个"独立包间",跟外面的页面互不干扰(当然,只是理论上)。

<!-- 最基础的用法,就像放张图片一样简单 --><iframesrc="https://www.example.com"></iframe><!-- 但实际项目中,你肯定得加一堆属性 --><iframesrc="https://www.example.com"width="800"height="600"frameborder="0"allowfullscreen></iframe>

看到没?最简单的用法确实简单,但真要拿到生产环境用,没几个属性镇场子是不行的。

这标签到底能干点啥正经事

别觉得iframe只是用来嵌广告的(虽然早期确实很多广告用它)。正经场景多着呢:

第三方内容嵌入是最常见的。地图、视频播放器、支付页面、客服聊天窗口…这些你不可能自己写吧?直接拿别人现成的iframe嵌进来,省心省力。比如高德地图的嵌入代码:

<!-- 嵌个地图,几行代码搞定 --><iframewidth="600"height="400"frameborder="0"scrolling="no"src="https://uri.amap.com/marker?position=116.397428,39.90923&name=天安门&src=mypage&coordinate=gaode&callnative=0"></iframe>

微前端架构里,iframe也是一种简单粗暴的实现方式。虽然业界现在更推崇single-spa、qiankun这些方案,但iframe的优势在于完全隔离——CSS不会冲突,JS不会互相污染,一个子应用挂了不会影响其他应用。对于需要快速整合的历史遗留系统,iframe往往是"最快能用"的方案。

预览功能也很实用。比如你要做个代码编辑器,需要实时预览效果,用iframe嵌个动态生成的页面,既安全又方便。或者后台管理系统里预览PDF、Office文档,很多也是用iframe加载第三方服务的预览页面。

沙箱环境更是iframe的杀手锏。后面会详细讲,但简单来说,如果你需要运行一些不可信的代码(比如用户提交的HTML片段),扔iframe里是最省事的隔离方案。

属性全家桶:src、sandbox、loading…每个都得盘明白

iframe的属性不少,但真正常用的就那几个。咱们一个个掰扯清楚。

src和srcdoc:从哪加载内容

src是最常用的,指向一个外部URL。但有时候你想直接塞HTML字符串进去,这时候srcdoc就派上用场了:

<!-- 传统方式,加载外部页面 --><iframesrc="https://example.com/page.html"></iframe><!-- srcdoc方式,直接写HTML --><iframesrcdoc="<html><body><h1>Hello World</h1></body></html>"></iframe>

srcdoc在动态生成内容时特别好用。比如你要展示一段用户提交的HTML代码,但又怕有XSS风险,可以先过滤一遍,然后用srcdoc塞进iframe:

// 假设这是用户提交的代码,需要清理后展示const userHtml =`<div onclick="alert('xss')">点击我</div>`;// 简单的XSS过滤(实际项目要用更严谨的库)const sanitizedHtml = userHtml.replace(/on\w+="[^"]*"/g,'');// 动态创建iframe并写入内容const iframe = document.createElement('iframe'); iframe.srcdoc = sanitizedHtml; document.body.appendChild(iframe);

sandbox:安全沙箱的配置手册

这是iframe最重要的安全属性,没有之一。它就像给iframe加了个"牢房",限制里面能干什么、不能干什么。

<!-- 最严格的限制,啥也不让干 --><iframesrc="https://example.com"sandbox></iframe><!-- 放开一些权限,允许提交表单和打开弹窗 --><iframesrc="https://example.com"sandbox="allow-forms allow-popups"></iframe><!-- 比较宽松但仍有底线 --><iframesrc="https://example.com"sandbox="allow-scripts allow-same-origin allow-forms"></iframe>

sandbox的可选值有这些,每个都要掂量清楚再用:

  • allow-scripts:允许执行JavaScript。很多第三方页面需要这个,但也是最危险的。
  • allow-same-origin:允许把iframe当成同源页面。注意,如果同时开了allow-scripts和allow-same-origin,iframe里的代码就能访问父页面的cookie,风险很大。
  • allow-forms:允许提交表单。支付页面通常需要这个。
  • allow-popups:允许打开新窗口(window.open)。
  • allow-top-navigation:允许iframe改变父页面的地址(top.location)。这个要特别小心,恶意网站可能用它做钓鱼跳转。
  • allow-pointer-lock:允许指针锁定(游戏常用)。
  • allow-modals:允许模态对话框(alert、confirm等)。

一个常见的坑:你嵌了个第三方登录页面,发现点登录没反应。大概率是忘了加allow-formsallow-scripts。但别一股脑全开,按需给权限才是正经做法。

loading:懒加载救性能

现代浏览器支持loading="lazy",让iframe像图片一样延迟加载:

<!-- 页面滚动到附近才加载,省流量省性能 --><iframesrc="https://example.com"loading="lazy"width="600"height="400"></iframe>

但要注意,懒加载只在iframe初始时不在视口内才有效。如果你用js动态改src,这个属性就不起作用了。那时候得自己用Intersection Observer或者简单的scroll监听来实现。

// 自己实现懒加载,控制更精细const iframe = document.createElement('iframe'); iframe.dataset.src ='https://example.com';// 先存起来const observer =newIntersectionObserver((entries)=>{ entries.forEach(entry=>{if(entry.isIntersecting){// 进入视口了,真正加载 iframe.src = iframe.dataset.src; observer.unobserve(iframe);}});}); observer.observe(iframe); document.body.appendChild(iframe);

其他常用属性

frameborder="0":去掉边框,现在更推荐用CSS的border:none。

allowfullscreen:允许全屏,嵌视频播放器必备。

name:给iframe起个名字,配合<a target="iframename">可以做导航,老派但好用。

referrerpolicy:控制referer头怎么发,隐私敏感场景有用。

安全沙箱怎么配才不被老板骂

安全这事儿,配松了怕出事,配严了怕功能跑不起来。这里说几个血泪教训。

绝对不要同时给allow-scripts和allow-top-navigation。恶意脚本可以直接把父页面跳转到钓鱼网站。如果业务需要跳转,考虑用postMessage让父页面来处理。

第三方内容一定要加sandbox。哪怕是你信任的供应商,也难保他们的页面被注入恶意代码。最小权限原则永远没错。

<!-- 比如嵌个支付页面,只需要表单提交和脚本执行 --><iframesrc="https://payment.provider.com/checkout"sandbox="allow-scripts allow-forms allow-same-origin"referrerpolicy="no-referrer"></iframe>

敏感操作加confirm。如果iframe里的内容需要做一些危险操作(比如删除数据),最好让父页面弹个确认框,而不是完全信任iframe里的逻辑。

// 父页面监听消息,做二次确认 window.addEventListener('message',(event)=>{// 验证来源,后面会讲if(event.origin !=='https://trusted-domain.com')return;if(event.data.type ==='DELETE_REQUEST'){if(confirm('确定要删除吗?此操作不可恢复')){// 告诉iframe可以执行了 event.source.postMessage({type:'DELETE_CONFIRMED'}, event.origin);}}});

CSP(内容安全策略)也要配合上。在HTTP头里设置Content-Security-Policy: frame-ancestors 'self' https://trusted.com,可以防止你的页面被恶意网站嵌到他们的iframe里(点击劫持攻击)。

跨域通信那点破事儿:postMessage真香但容易翻车

同源策略是前端安全的基础,但业务上总需要iframe和父页面传个话。postMessage就是干这个的,但用不好照样翻车。

基础用法很简单:

// 父页面向iframe发消息const iframe = document.getElementById('myIframe'); iframe.contentWindow.postMessage('你好啊','https://child-domain.com');// iframe向父页面发消息 window.parent.postMessage('收到','https://parent-domain.com');

但这里有几个坑,我逐个给你标红:

坑1:不验证origin就是找死

// 错误示范:来者不拒,危险! window.addEventListener('message',(event)=>{ console.log(event.data);// 直接用了,万一来源不对呢?});// 正确做法:先验明正身 window.addEventListener('message',(event)=>{// 严格检查来源,白名单机制const allowedOrigins =['https://trusted1.com','https://trusted2.com'];if(!allowedOrigins.includes(event.origin)){ console.warn('可疑消息来源:', event.origin);return;}// 还要检查数据格式,别拿到个对象就瞎用if(typeof event.data !=='object'||!event.data.type){return;}// 现在可以安心处理了handleMessage(event.data);});

坑2:targetOrigin别写*

// 偷懒写法,消息发给所有人,极其危险 iframe.contentWindow.postMessage(secretData,'*');// 正确写法,指名道姓发给谁 iframe.contentWindow.postMessage(secretData,'https://exact-domain.com');

坑3:复杂数据要序列化

postMessage传递的是结构化克隆算法处理的数据,基本类型、对象、数组都没问题,但函数和DOM节点不行。如果要用函数回调,得用其他套路:

// 父页面const callbacks =newMap();let callbackId =0;functioncallChildMethod(method, params, callback){const id =++callbackId; callbacks.set(id, callback); iframe.contentWindow.postMessage({type:'CALL_METHOD', method, params,callbackId: id },'https://child-domain.com');} window.addEventListener('message',(event)=>{if(event.data.type ==='CALLBACK_RESULT'&& callbacks.has(event.data.callbackId)){ callbacks.get(event.data.callbackId)(event.data.result); callbacks.delete(event.data.callbackId);}});// iframe里 window.addEventListener('message',(event)=>{if(event.data.type ==='CALL_METHOD'){const result =someLocalMethod(event.data.params); event.source.postMessage({type:'CALLBACK_RESULT',callbackId: event.data.callbackId, result }, event.origin);}});

坑4:内存泄漏

如果你频繁创建和销毁iframe,记得把postMessage的监听器也清理掉,不然闭包里的引用可能导致内存泄漏:

functioncreateIframe(){const iframe = document.createElement('iframe');constmessageHandler=(event)=>{// 处理消息...}; window.addEventListener('message', messageHandler);// 清理时要移除监听 iframe.addEventListener('load',()=>{// iframe加载新内容前的清理});return{destroy(){ window.removeEventListener('message', messageHandler); iframe.remove();}};}

性能拖后腿?懒加载+按需渲染救你狗命

iframe是性能黑洞,这话一点不夸张。每个iframe都要独立解析HTML、加载资源、执行脚本,跟开一个新标签页差不多。如果一页嵌五六个iframe,低端机直接卡成PPT。

懒加载前面说过了,不在视口内的坚决不加载。但有些场景需要更精细的控制:

// 按需加载:点击tab时才加载对应iframeclassTabManager{constructor(){this.loadedTabs =newSet();this.iframes =newMap();}registerTab(tabId, url){// 先占个坑,不真加载const placeholder = document.createElement('div'); placeholder.dataset.url = url; placeholder.dataset.tabId = tabId;this.iframes.set(tabId, placeholder);return placeholder;}activateTab(tabId){const placeholder =this.iframes.get(tabId);if(!placeholder ||this.loadedTabs.has(tabId))return;// 真要显示了,换成iframeconst iframe = document.createElement('iframe'); iframe.src = placeholder.dataset.url; iframe.loading ='eager';// 立即加载 placeholder.replaceWith(iframe);this.iframes.set(tabId, iframe);this.loadedTabs.add(tabId);// 可以设置最大缓存数量,超了的销毁this.cleanupOldTabs();}cleanupOldTabs(){const maxCache =3;if(this.loadedTabs.size > maxCache){const oldest =this.loadedTabs.values().next().value;// 销毁逻辑...}}}

资源预加载也有技巧。如果确定某个iframe很快要用,可以提前建立连接,但不真加载内容:

<!-- 预解析DNS --><linkrel="dns-prefetch"href="https://third-party.com"><!-- 预建立TCP连接 --><linkrel="preconnect"href="https://third-party.com"><!-- 预加载关键资源(慎用,会占用带宽) --><linkrel="prefetch"href="https://third-party.com/widget.html">

避免重复初始化。如果iframe只是暂时隐藏(display:none),不要销毁它,下次显示时直接恢复,省得重新加载。但如果确定很久不用,还是destroy掉释放内存。

实际开发中那些又爱又恨的场景

嵌第三方地图

地图组件是iframe的经典用例。但问题来了:怎么知道地图加载完了?怎么在地图上标点?怎么响应地图点击?

// 封装个地图组件,把脏活累活藏起来classMapWidget{constructor(container, apiKey){this.container = container;this.callbacks =newMap();this.ready =false;this.iframe = document.createElement('iframe');this.iframe.src =`https://maps.example.com/embed?key=${apiKey}&callback=init`;this.iframe.sandbox ='allow-scripts allow-same-origin';// 监听地图ready消息this._setupListener(); container.appendChild(this.iframe);}_setupListener(){ window.addEventListener('message',(e)=>{if(e.origin !=='https://maps.example.com')return;switch(e.data.type){case'MAP_READY':this.ready =true;this._flushQueue();// 处理队列中的调用break;case'MARKER_CLICKED':const cb =this.callbacks.get(`marker-${e.data.markerId}`);if(cb)cb(e.data.latLng);break;}});}// 添加标记点,如果地图没准备好先存队列addMarker(lat, lng, options){constcommand=()=>{this.iframe.contentWindow.postMessage({type:'ADD_MARKER', lat, lng, options },'*');};if(this.ready)command();elsethis.commandQueue.push(command);}// 销毁时清理destroy(){this.iframe.remove();// 清理监听器...}}

嵌支付页面

支付场景对安全要求极高,而且通常需要知道支付结果。这里的关键是支付完成后怎么通知父页面,以及怎么防止中间人攻击

<!-- 支付iframe,沙箱要开得恰到好处 --><iframeid="paymentFrame"src="https://secure-payment.com/checkout?orderId=123"sandbox="allow-scripts allow-forms allow-top-navigation-by-user-activation"allow="payment"></iframe><script>// 监听支付结果 window.addEventListener('message',(e)=>{if(e.origin !=='https://secure-payment.com')return;if(e.data.type ==='PAYMENT_SUCCESS'){// 验证签名,防止伪造const isValid =verifySignature(e.data.payload, e.data.signature);if(isValid){showSuccessPage(e.data.orderId);}else{reportSecurityIssue('签名验证失败');}}});// 安全起见,支付完成后应该跳转回你的页面,而不是依赖postMessage// 所以还要处理iframe的load事件,检查URL是否变成了回调地址 document.getElementById('paymentFrame').addEventListener('load',(e)=>{try{const url =newURL(e.target.src);if(url.pathname ==='/payment/callback'){const params =newURLSearchParams(url.search);handlePaymentCallback(params);}}catch(err){// 跨域时读src可能报错,要做好容错}});</script>

嵌旧系统

这可能是iframe最无奈的用法——新系统做好了,但老系统迁移成本太高,只能先嵌着。这时候最大的问题是样式不统一通信困难

// 封装个适配器,让老系统看起来像是新系统的一部分classLegacySystemAdapter{constructor(iframe, legacyOrigin){this.iframe = iframe;this.legacyOrigin = legacyOrigin;this.styleInjected =false;// 尝试注入CSS让外观统一this._injectStyles();// 监听老系统的导航请求,拦截在新系统里处理this._setupNavigationInterceptor();}_injectStyles(){// 等iframe加载完,尝试注入统一样式this.iframe.addEventListener('load',()=>{try{// 如果同源,可以直接操作const doc =this.iframe.contentDocument;const style = doc.createElement('style'); style.textContent =` body { font-family: 'Your New Font', sans-serif !important; } .legacy-header { display: none !important; } /* 隐藏老系统的header */ `; doc.head.appendChild(style);}catch(e){// 跨域的话,只能让老系统自己支持主题参数了this._requestThemeChange();}});}_requestThemeChange(){// 通过postMessage让老系统自己换肤this.iframe.contentWindow.postMessage({type:'SET_THEME',theme:'modern',primaryColor:'#1890ff'},this.legacyOrigin);}// 拦截老系统的页面跳转,改成单页应用的路由切换_setupNavigationInterceptor(){ window.addEventListener('message',(e)=>{if(e.origin !==this.legacyOrigin)return;if(e.data.type ==='NAVIGATE'){ e.preventDefault();// 阻止默认跳转// 在新系统的路由里处理 window.history.pushState(null,'',`/legacy/${e.data.path}`);// 告诉iframe更新内容,而不是整页刷新this.iframe.contentWindow.postMessage({type:'UPDATE_ROUTE',path: e.data.path },this.legacyOrigin);}});}}

一刷新就白屏?高度不对?点不动?常见翻车现场急救包

白屏问题

iframe白屏原因太多了,逐个排查:

// 调试神器:监听所有错误 iframe.addEventListener('error',(e)=>{ console.error('iframe加载失败:', e);});// 检查CSP限制,有些网站禁止被嵌套// 看控制台有没有"Refused to display 'xxx' in a frame because..."的报错// 检查X-Frame-Options头,DENY或SAMEORIGIN都会阻止嵌入// 这个得服务端配合,前端无解// 超时处理,长时间加载不出就提示const loadTimeout =setTimeout(()=>{showError('加载超时,请检查网络或刷新重试');},10000); iframe.addEventListener('load',()=>{clearTimeout(loadTimeout);// 但load事件触发不代表真的加载成功了,还要检查内容try{if(iframe.contentDocument.body.innerHTML ===''){showError('页面内容为空');}}catch(e){// 跨域时读不了,只能看天吃饭}});

高度自适应

iframe高度不会自动跟着内容变,这是老大难问题。几种解决方案:

// 方案1:如果同源,直接读scrollHeightfunctionadjustHeight(iframe){try{const height = iframe.contentDocument.body.scrollHeight; iframe.style.height = height +'px';}catch(e){// 跨域报错,用方案2}}// 方案2:子页面主动上报高度// 子页面里:setInterval(()=>{const height = document.body.scrollHeight; window.parent.postMessage({type:'RESIZE', height},'*');},200);// 节流一下,别发太频繁// 父页面里: window.addEventListener('message',(e)=>{if(e.data.type ==='RESIZE'){ iframe.style.height = e.data.height +'px';}});// 方案3:用ResizeObserver(现代浏览器)// 子页面:const observer =newResizeObserver((entries)=>{const height = entries[0].contentRect.height; window.parent.postMessage({type:'RESIZE', height}, parentOrigin);}); observer.observe(document.body);

点击穿透或点不动

有时候iframe上面的蒙层遮不住它,或者点击没反应。这是因为iframe是独立的窗口,z-index在它身上表现怪异。

/* 方案1:用pointer-events控制 */.overlay{position: fixed;top: 0;left: 0;right: 0;bottom: 0;background:rgba(0,0,0,0.5);z-index: 9999;}/* 当显示蒙层时,让iframe不接受鼠标事件 */.has-overlay iframe{pointer-events: none;}/* 方案2:如果必须和iframe交互,用wrapper包一层 */.iframe-wrapper{position: relative;}.iframe-wrapper::before{content:'';position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 1;/* 在iframe之上 */display: none;}.iframe-wrapper.disabled::before{display: block;/* 显示遮罩 */}

移动端适配灾难

移动端iframe问题特别多:缩放不对、滚动冲突、键盘弹起时布局错乱…

<!-- 确保viewport设置正确 --><iframesrc="https://example.com"style="width: 100%;border: none;"scrolling="no"><!-- 移动端通常需要禁用iframe内部滚动,由父容器控制 --></iframe><script>// 处理iOS的橡皮筋效果导致的滚动问题let startY =0; iframe.addEventListener('touchstart',(e)=>{ startY = e.touches[0].pageY;}); iframe.addEventListener('touchmove',(e)=>{const scrollTop = iframe.contentWindow.pageYOffset;const scrollHeight = iframe.contentWindow.document.body.scrollHeight;const clientHeight = iframe.contentWindow.innerHeight;const y = e.touches[0].pageY;// 在顶部还往上拉,或者在底部还往下拉,就阻止默认行为if((scrollTop <=0&& y > startY)||(scrollTop + clientHeight >= scrollHeight && y < startY)){ e.preventDefault();}});</script>

老司机私藏技巧:动态注入、父子通信封装、防XSS小妙招

动态注入内容的高级玩法

有时候你需要往iframe里动态塞代码,但srcdoc不够灵活。可以用Blob URL:

functioncreateIframeWithContent(html, css, js){const fullHtml =` <!DOCTYPE html> <html> <head> <style>${css}</style> </head> <body> ${html} <script> try { ${js} } catch(e) { window.parent.postMessage({type: 'ERROR', message: e.message}, '*'); } <\/script> </body> </html> `;// 创建Blob URL,比data URL长度限制小,而且性能更好const blob =newBlob([fullHtml],{type:'text/html'});const url =URL.createObjectURL(blob);const iframe = document.createElement('iframe'); iframe.src = url;// 清理Blob URL(iframe加载完后就可以清了) iframe.addEventListener('load',()=>{URL.revokeObjectURL(url);});return iframe;}// 应用场景:实时预览用户输入的代码const previewIframe =createIframeWithContent('<div></div>','body { margin: 0; padding: 20px; }','document.getElementById("app").innerHTML = "Hello World";');

父子通信封装成Promise风格

原始的postMessage是回调式的,封装成Promise更好用:

classIframeBridge{constructor(iframe, targetOrigin){this.iframe = iframe;this.targetOrigin = targetOrigin;this.pending =newMap();this.id =0; window.addEventListener('message',this._handleMessage.bind(this));}// 调用子页面的方法,返回Promiseinvoke(method, params, timeout =5000){returnnewPromise((resolve, reject)=>{const id =++this.id;const timer =setTimeout(()=>{this.pending.delete(id);reject(newError('调用超时'));}, timeout);this.pending.set(id,{ resolve, reject, timer });this.iframe.contentWindow.postMessage({type:'INVOKE', id, method, params },this.targetOrigin);});}_handleMessage(event){if(event.origin !==this.targetOrigin)return;const data = event.data;if(data.type ==='RESULT'&&this.pending.has(data.id)){const{ resolve, reject, timer }=this.pending.get(data.id);clearTimeout(timer);this.pending.delete(data.id);if(data.error)reject(newError(data.error));elseresolve(data.result);}}// 销毁时清理destroy(){this.pending.forEach(({ timer })=>clearTimeout(timer));this.pending.clear();}}// 使用const bridge =newIframeBridge(iframe,'https://child.com');try{const result =await bridge.invoke('getUserInfo',{userId:123}); console.log(result);}catch(e){ console.error('调用失败:', e);}

防XSS的终极方案

如果iframe里的内容完全不可信(比如用户提交的HTML),除了sandbox,还可以:

// 用CSP进一步限制const csp =` default-src 'none'; script-src 'none'; /* 完全禁止脚本 */ style-src 'unsafe-inline'; /* 允许内联样式,但最好也限制 */ img-src data: https:; `;const html =` <meta http-equiv="Content-Security-Policy" content="${csp.replace(/\n/g,' ')}"> <div>${userContent}</div> `;// 或者用srcdoc配合sandbox="allow-same-origin",然后自己控制所有交互// 这样即使内容有恶意脚本,也执行不了

你以为这就完了?其实iframe还能这么玩

微前端沙箱的野路子

有些极端场景,你需要在一个页面里跑多个版本的React或Vue,常规方案会冲突。用iframe做沙箱,每个子应用跑在自己的环境里,再通过postMessage通信,虽然通信 overhead 大点,但隔离性无敌。

// 搞个"代理"iframe,里面只跑JS逻辑,不显示const sandboxIframe = document.createElement('iframe'); sandboxIframe.style.display ='none'; sandboxIframe.srcdoc =` <script> // 在这里加载React 16,而父页面用React 18 importScripts('https://cdn.react16.com/bundle.js'); window.addEventListener('message', (e) => { // 执行渲染逻辑,把结果通过postMessage传回去 const result = React16.renderToString(e.data.component); e.source.postMessage({result}, e.origin); }); <\/script> `; document.body.appendChild(sandboxIframe);

跨域存储方案

localStorage不能跨域,但你可以用iframe做个"存储服务":

// 在storage.example.com建个页面,专门管存取// 其他域通过iframe嵌它,用postMessage读写// 存储服务页面代码: window.addEventListener('message',(e)=>{const allowedOrigins =['https://app1.com','https://app2.com'];if(!allowedOrigins.includes(e.origin))return;if(e.data.type ==='SET'){ localStorage.setItem(e.data.key, e.data.value); e.source.postMessage({success:true}, e.origin);}elseif(e.data.type ==='GET'){const value = localStorage.getItem(e.data.key); e.source.postMessage({value}, e.origin);}});

打印控制

iframe可以用来做打印预览,只打印特定区域:

functionprintSection(htmlContent){const iframe = document.createElement('iframe'); iframe.style.position ='absolute'; iframe.style.left ='-9999px'; iframe.srcdoc =` <html> <head> <link rel="stylesheet" href="/print-styles.css"> <style>@media print { body { margin: 0; } }</style> </head> <body>${htmlContent}</body> </html> `; document.body.appendChild(iframe); iframe.addEventListener('load',()=>{ iframe.contentWindow.print();// 调用打印对话框// 打印完后清理setTimeout(()=> iframe.remove(),1000);});}

安全地执行第三方脚本

有些业务需要嵌入第三方统计代码或广告脚本,但又怕它们乱搞。可以扔iframe里,严格控制权限:

<iframesandbox="allow-scripts allow-same-origin"srcdoc=" <script> // 第三方脚本在这里跑,访问不了父页面DOM // 但可以通过postMessage上报数据 window.parent.postMessage({type: 'TRACK', event: 'pageview'}, '*'); <\/script> "></iframe>

写到这差不多也该收笔了。iframe这东西,你说它老旧吧,确实有点;但要说它能被完全替代,那也不现实。关键是理解它的原理、掌握它的脾气,该用的时候大胆用,该避的坑提前绕。

最后送各位一句话:技术没有银弹,iframe也不是洪水猛兽。把它当成工具箱里的一把钝刀,虽然不够锋利,但有时候偏偏只有它能砸开那个核桃。好好打磨,照样能切菜。

欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。
推荐:DTcode7的博客首页。
一个做过前端开发的产品经理,经历过睿智产品的折磨导致脱发之后,励志要翻身农奴把歌唱,一边打入敌人内部一边持续提升自己,为我们广大开发同胞谋福祉,坚决抵制睿智产品折磨我们码农兄弟!

专栏系列(点击解锁)学习路线(点击解锁)知识定位
《微信小程序相关博客》持续更新中~结合微信官方原生框架、uniapp等小程序框架,记录请求、封装、tabbar、UI组件的学习记录和使用技巧等
《AIGC相关博客》持续更新中~AIGC、AI生产力工具的介绍,例如stable diffusion这种的AI绘画工具安装、使用、技巧等总结
《HTML网站开发相关》《前端基础入门三大核心之html相关博客》前端基础入门三大核心之html板块的内容,入坑前端或者辅助学习的必看知识
《前端基础入门三大核心之JS相关博客》前端JS是JavaScript语言在网页开发中的应用,负责实现交互效果和动态内容。它与HTML和CSS并称前端三剑客,共同构建用户界面。
通过操作DOM元素、响应事件、发起网络请求等,JS使页面能够响应用户行为,实现数据动态展示和页面流畅跳转,是现代Web开发的核心
《前端基础入门三大核心之CSS相关博客》介绍前端开发中遇到的CSS疑问和各种奇妙的CSS语法,同时收集精美的CSS效果代码,用来丰富你的web网页
《canvas绘图相关博客》Canvas是HTML5中用于绘制图形的元素,通过JavaScript及其提供的绘图API,开发者可以在网页上绘制出各种复杂的图形、动画和图像效果。Canvas提供了高度的灵活性和控制力,使得前端绘图技术更加丰富和多样化
《Vue实战相关博客》持续更新中~详细总结了常用UI库elementUI的使用技巧以及Vue的学习之旅
《python相关博客》持续更新中~Python,简洁易学的编程语言,强大到足以应对各种应用场景,是编程新手的理想选择,也是专业人士的得力工具
《sql数据库相关博客》持续更新中~SQL数据库:高效管理数据的利器,学会SQL,轻松驾驭结构化数据,解锁数据分析与挖掘的无限可能
《算法系列相关博客》持续更新中~算法与数据结构学习总结,通过JS来编写处理复杂有趣的算法问题,提升你的技术思维
《IT信息技术相关博客》持续更新中~作为信息化人员所需要掌握的底层技术,涉及软件开发、网络建设、系统维护等领域的知识
《信息化人员基础技能知识相关博客》无论你是开发、产品、实施、经理,只要是从事信息化相关行业的人员,都应该掌握这些信息化的基础知识,可以不精通但是一定要了解,避免日常工作中贻笑大方
《信息化技能面试宝典相关博客》涉及信息化相关工作基础知识和面试技巧,提升自我能力与面试通过率,扩展知识面
《前端开发习惯与小技巧相关博客》持续更新中~罗列常用的开发工具使用技巧,如 Vscode快捷键操作、Git、CMD、游览器控制台等
《photoshop相关博客》持续更新中~基础的PS学习记录,含括PPI与DPI、物理像素dp、逻辑像素dip、矢量图和位图以及帧动画等的学习总结
日常开发&办公&生产【实用工具】分享相关博客》持续更新中~分享介绍各种开发中、工作中、个人生产以及学习上的工具,丰富阅历,给大家提供处理事情的更多角度,学习了解更多的便利工具,如Fiddler抓包、办公快捷键、虚拟机VMware等工具

吾辈才疏学浅,摹写之作,恐有瑕疵。望诸君海涵赐教。望轻喷,嘤嘤嘤

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。愿斯文对汝有所裨益,纵其简陋未及渊博,亦足以略尽绵薄之力。倘若尚存阙漏,敬请不吝斧正,俾便精进!
在这里插入图片描述

Read more

AI 办公成职场标配,别再用错拖后腿!7 套书教你精准用 AI 提效

AI 办公成职场标配,别再用错拖后腿!7 套书教你精准用 AI 提效

2026三掌柜赠书活动第十八期 AI 办公成职场标配,别再用错拖后腿!7 套书教你精准用 AI 提效 目录 Part.0 前言 Part.1 开会汇报没重点?AI当“嘴替” Part.2 不想加班,还不知道搭个智能体帮你干? Part.3 主业涨薪难,想抓AI风口做副业? Part.4 DeepSeek总get不到你的点? Part.5 Office内置AI不会用? Part.6 不想被“职场体力活”耗空? Part.7 对抗工具墒增,实现职场进阶! Part.8 彩蛋:赠书! Part.9 结束语 Part.0 前言

OpenClaw 实操指南 07:飞书 CLI 开源:让 AI 真正接管你的飞书全流程

OpenClaw 实操指南 07:飞书 CLI 开源:让 AI 真正接管你的飞书全流程

2026年3月28日,飞书官方开源larksuite/cli(v1.0.0),以200+命令、19个AI Agent Skills,将飞书2500+开放API封装为命令行接口,面向人类开发者与AI Agent双用户,重构办公协作的操作范式。这不仅是工具升级,更是飞书从“GUI服务人”到“GUI+CLI双态并行”的战略跃迁——GUI给人交互,CLI给AI执行,让AI真正成为办公的“执行者”而非“旁观者”。 一、飞书CLI是什么:从API到命令行的能力跃迁 1. 核心定位与架构 飞书CLI是官方开源、MIT协议、免费商用的命令行工具,核心定位是让AI Agent直接操控飞书全量数据与业务,而非仅做信息查询。其三层架构清晰划分能力边界: * Shortcuts层:高频快捷命令(如lark-cli calendar +agenda查今日日程),降低人类使用门槛。 * API Commands层:200+

内存暴涨700%背后的惊天真相:AI正在吞噬一切!能源·隐私·绿色三大维度深度拆解

内存暴涨700%背后的惊天真相:AI正在吞噬一切!能源·隐私·绿色三大维度深度拆解

🔥作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习 🎬擅长领域:驱动开发,嵌入式软件开发,BSP开发 ❄️作者主页:一个平凡而乐于分享的小比特的个人主页 ✨收录专栏:未来思考,本专栏结合当前国家战略和实时政治,对未来行业发展的思考 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖 🔥内存暴涨700%背后的惊天真相:AI正在吞噬一切!能源·隐私·绿色三大维度深度拆解 |前言| 最近装机的小伙伴们欲哭无泪:DDR5内存价格一路狂飙,部分DRAM现货价格在过去一年暴涨近700% 。大家习惯性吐槽“厂商放火”、“产能不足”,但很少有人看到,这场涨价风暴的真正推手,是那只名为“AI”的巨兽。 当你还在为多花几百块钱买内存心疼时,国家正在西部荒漠建起一座座数据中心,科技巨头正在为“吃电怪兽”抢购每一颗芯片。2026年,大型科技公司的AI相关投资预计将达到6500亿美元,较去年增长约80% 。 今天,我们从能源供应、隐私安全、绿色AI 三个维度,结合东数西算、算电协同、

深度评测:5款AI流程图生成工具——图像识别、Mermaid支持与文档解析能力对比

深度评测:5款AI流程图生成工具——图像识别、Mermaid支持与文档解析能力对比

流程图在软件工程实践中是一个高频需求:系统架构设计、业务流程梳理、API交互时序、数据流向描述……这些场景都离不开流程图。 传统的痛点有两个,一是白板会议结束后,手绘草图需要人工转录为数字图表,耗时且容易引入错误;二是流程图的版本管理混乱,源文件分散在PPT、截图、Visio等多个载体中,变更追踪困难。 当前市场上出现了一批以“AI图像识别”和“文档解析”为核心能力的流程图生成工具,本文从技术视角对5款代表性工具进行深度评测,重点关注以下维度: •图像识别(Image-to-Diagram)的实现方式与精度 •Mermaid代码支持与语法容错能力 •文档格式兼容性与隐性逻辑提取能力 •多人协作架构 •数据导出格式与集成能力 AI流程图工具核心能力评测维度 图像识别生成(Image-to-Diagram): 工具能否从手绘草图、白板照片、屏幕截图中准确提取节点形状、箭头指向和文本内容,并重建为结构化的可编辑图表?底层依赖OCR引擎与图形识别模型的协同。 Mermaid代码支持: 对于技术团队,Mermaid是最常用的图表描述语言之一。工具是否支持Mermaid代码的