资讯动态

HTML框架从入门到实战:frameset废弃与iframe通信机制解析

发布时间:2026/9/29 13:41:27 来源:尧图企业网站定制
这年头还能看到专门讲HTML框架的标题其实还挺让人感慨的。我记得自己刚接触网页制作那会儿教材里还大篇幅讲frameset怎么切分窗口、frame怎么嵌套结果毕业出去做实习发现公司里一套2012年的后台管理系统界面就是靠一个三层嵌套的frameset撑起来的。那种“学了总比不学强”的庆幸感只有真实碰过老项目维护的人才能体会。不过话说回来现在再聊HTML框架很多人第一反应是frameset和frame不是早就废弃了吗学了有什么用我的看法是学不学是一回事怎么学是另一回事。对于现代前端开发iframe依然是高频使用的内嵌方案而frameset和frame虽然退出了标准舞台但理解它们的切分思路对理解今天后台页面布局的演化依旧有很大帮助。这篇内容主要面向刚学完HTML基础、准备进阶的初学者也适合需要维护老系统、或者面试前想把这些概念彻底理清的开发者。1. 先说结论frameset和frame是不是真的一无是处1.1 为什么这门“老技术”还出现在教材里先给一个直接的回答frameset和frame在HTML5标准里已经被明确废弃现代开发中不应该再为新项目使用它们。但很多高校教材和培训机构还在教原因无非是三点。第一历史项目遗留量非常大。2004年到2014年之间国内大量的OA系统、教务管理系统、企业后台都是用frameset搭的整体布局。这些系统很多还在正常运行后来维护的人必须能看懂frameset和frame的写法。第二考试和面试题还会涉及。初中级前端面试里“frameset和iframe有什么区别”这个问题的出现频率远超想象答不上来很吃亏。第三学习价值不低。frameset的rows和cols切分思路本质上是一种纯粹的“区域划分”思维理解了它再回过头看CSS Grid的grid-template-columns和grid-template-rows会发现核心思想一脉相承。所以我的态度很明确没必要主动学得特别深但基础语法、嵌套结构、和iframe的区别这三件事值得花半天到一天的时间搞清楚。1.2 frameset、frame、iframe三者到底是什么关系这三个词经常被混在一起说但它们解决的是不同层面的问题。frameset是框架集它本身不是一个普通的“页面元素”而是整个页面结构的主框架。用了frameset页面里就不能再写body标签。它负责把浏览器窗口划分为几块区域比如左右两块、上中下三块。frame是框架集中单独的每一个子窗口。每个frame通过src属性加载一个独立的HTML页面。frame不是一个独立使用的标签它必须生活在frameset内部。iframe全称是inline frame也就是内嵌框架。它和frame最大的区别是iframe可以在任何一个普通页面里随意使用页面该有body就有bodyiframe只是body里排在最前面的一个元素。也就是说frameset改变了整个页面的组织方式而iframe只是页面里的一个“小窗口”。用一个生活化的类比frameset就像你把一张桌子用隔板分成几个格子每个格子里单独放一份菜整个桌面上不允许再放其他东西iframe则像是在一张大桌子上摆了一个餐盒餐盒里是独立的菜餐盒外面的桌面你还能自由使用。2. frameset与frame完整语法框架集页面的结构与嵌套规则2.1 基本结构cols与rows怎么切分窗口一个最基础的frameset页面长这样!DOCTYPE html html langzh-cn head meta charsetutf-8 title左右分栏框架集/title /head frameset cols25%,50%,25% frame srcleft.html frame srcmiddle.html frame srcright.html /frameset /html注意几个细节这个页面里没有body标签frameset直接写在html标签内部并且把head闭合后就开始写。cols25%,50%,25%表示把窗口垂直方向切成三列宽度按25%、50%、25%分配。如果想把页面水平切成上下两部分用rowsframeset rows100, * frame srctop.html frame srcbottom.html /frameset这里的100代表第一行固定100像素高*表示剩下的空间全部给第二行。*是框架集切分里的一个非常实用的语法它的优先级低于固定像素高于百分比。换句话说窗口缩放时固定像素的部分不变余下空间尽量分配给*对应的区域。如果多个区域都用*例如rows*, 2*那么第二个区域会拿到两倍于第一个区域的空间。这种比例分配方式在后来CSS Grid的fr单位里也原样延续了下来学一遍在这里回头去看Grid会非常容易。2.2 frame标签的关键属性与name属性的桥梁作用frame标签最常用的属性有这些src加载的页面地址。name给这个框架窗口起名字是后面实现“点击左栏、右栏跳转”的关键。noresize禁止用户拖拽调整框架大小。没有它浏览器默认允许拖动框架分隔线。scrolling取值yes、no、auto控制该框架窗口是否出现滚动条。marginwidth和marginheight设置框架内页面与外边距的距离。name属性是最有价值的因为其他标签可以通过它实现联动。下面是一个典型的登录后后台页面结构frameset rows80,* frame srctop.html nametopFrame noresizenoresize frameset cols180,* frame srcleft.html nameleftFrame frame srchome.html namemainFrame /frameset /frameset在left.html里写导航链接的时候只要给a标签的target指定成mainFrame点击后新页面就会加载到主区域里a hrefuser_list.html targetmainFrame用户管理/a a hrefdata_report.html targetmainFrame数据报表/a这就是老后台框架系统的核心联动机制。现在你可能觉得用前端路由或者Vue/React就能轻松搞定但在那个Ajax还不成熟的年代这套机制的零刷新体验已经算是相当先进了。2.3 嵌套框架集一个经典的三段式页面是如何拼起来的实际项目中很少只用一层frameset因为后台页面通常需要顶部放Logo和欢迎信息左侧放菜单右侧放具体内容。这种布局依靠嵌套frameset来实现!DOCTYPE html html langzh-cn head meta charsetutf-8 title经典后台框架布局/title /head frameset rows80, * frame srctop.html nametopFrame noresizenoresize scrollingno frameset cols180, * frame srcleft.html nameleftFrame noresizenoresize frame srchome.html namemainFrame /frameset /frameset /html这个结构实际上是两种切分方式的叠加第一层先按行切分为80像素的顶部区域和剩余的全部区域第二层再把剩余区域按列切分为180像素的左侧区域和剩下所有区域。对浏览器来说嵌套的方式没有嵌套层数限制你甚至可以继续在右下角再嵌套一层但一般到第三层就已经很难维护了。还有一个标签值得一提noframes。它放在frameset内部给那些不支持框架的浏览器准备替代内容frameset cols200, * frame srcnav.html frame srccontent.html noframes body您的浏览器不支持框架集请升级浏览器或访问 a hrefcontent.html内容页/a /body /noframes /frameset在HTML5标准里noframes同样被废弃了今天几乎没有实际应用场景。但如果你在旧系统里看到它至少要知道它是“兜底内容”不是某种页面特效。3. iframe的现代应用从地图、视频到广告容器3.1 iframe的基本写法与它和frameset的根本差异iframe的语法比frameset简单太多它本质就是一个可以自由放在普通页面任何位置的元素iframe srchttps://example.com/map.html width800 height400 frameborder0 allowfullscreen/iframeframeborder0是控制边框是否显示的老写法现在更推荐用CSS来控制效果更干净。iframe和frameset的根本差异可以从三个维度看对比项frameset / frameiframe页面结构替代body一个页面必须是纯框架集普通页面里的一个元素不影响页面其他部分使用状态已废弃新项目不应使用仍在现代开发中广泛使用独立性每个frame是独立的浏览器窗口区域iframe内部是一个完整的独立文档嵌套方式frameset内部套frameset任意页面里嵌入没有结构性限制iframe之所以到今天还保持着生命力核心原因在于“文档隔离”。它的内部是一个完整的独立文档外部页面的CSS不会污染iframe内部的样式外部JavaScript在默认情况下也无法直接操作内部的DOM这种天然隔离性很适合用来嵌入不可信的第三方内容。3.2 name target 实现“点击左侧、展示右侧”iframe同样支持name属性使用方法与frame场景完全一致。在没有前端框架的年代很多后台系统甚至会用iframe来模拟部分刷新体验iframe srcwelcome.html namecontentFrame stylewidth: 100%; height: 500px;/iframe左侧导航里的链接a hreflist.html targetcontentFrame订单列表/a点击这个链接浏览器会寻找页面上name等于contentFrame的iframe并在其内部加载list.html。这一步很值得亲自上手敲一遍因为理解了它之后你就能理解为什么浏览器的target属性机制可以作用于不同窗口、不同框架、不同iframe这是一个贯穿网页开发始终的概念。3.3 实际项目里的iframe使用场景盘点现在iframe依然有它不可替代的位置。第一类是地图和第三方图表。大众点评、企业官网上的门店地图绝大多数都是通过iframe嵌入百度地图或高德地图的开放页面引入方式就是一句iframe代码。第二类是视频播放器。B站、YouTube、腾讯视频提供的对外分享功能底层都是iframe你能在任意网站里嵌入一段视频靠的也是iframe的隔离能力。第三类是广告位与支付收银台。很多广告联盟为了保护安全性和独立统计都要求以iframe方式渲染广告素材。支付场景里支付宝和微信收银台在部分模式下也会以iframe挂载到页面上这样能有效避免外部页面样式对收银流程的干扰。第四类是应用沙箱。iframe的sandbox属性可以限制内部文档执行脚本、提交表单、弹窗等操作在做富文本编辑器、模板预览、用户上传内容预览时非常好用iframe srcpreview.html sandboxallow-scripts allow-same-origin stylewidth: 100%; height: 400px;/iframesandbox后面如果什么都不写内部所有权限全部禁用allow-scripts表示允许执行脚本allow-same-origin表示允许保持同源。实际项目中要谨慎组合这两个值因为脚本和同源权限同时开放相当于iframe里的内容可以对父页面进行同源访问安全隐患不小。4. iframe通信实战同源操作、跨域postMessage与安全边界4.1 同源规则与父页面直接操作iframe DOM同源规则是一个经常被低估的概念。协议、域名、端口三者完全一致才算同源。同源前提下父页面可以直接访问iframe内部的文档内容。比如同域页面里有个iframeiframe srcchild.html idmyFrame stylewidth: 100%; height: 300px;/iframe父页面里可以这样修改iframe内部的内容var childDoc document.getElementById(myFrame).contentDocument; childDoc.body.style.backgroundColor #f5f5f5;contentDocument获取的是什么是iframe内部的完整文档对象。拿到它之后操作方式和操作当前页面的DOM一致。不过要注意一个时序问题iframe内部页面必须加载完成才能通过contentDocument访问。所以稳妥的做法是监听iframe的load事件var myFrame document.getElementById(myFrame); myFrame.onload function () { var childDoc myFrame.contentDocument; var title childDoc.title; console.log(iframe内部标题 title); };这里容易踩的坑是跨域情况下访问contentDocument会直接抛出安全错误不能用。那跨域时怎么通信答案是postMessage。4.2 跨域情况下用postMessage传递消息postMessage是HTML5提供的跨窗口通信API专门用来解决跨域窗口间的消息传递。父页面想给iframe发消息这样写var myFrame document.getElementById(myFrame); myFrame.onload function () { myFrame.contentWindow.postMessage(父页面发来的消息, https://child-site.com); };postMessage的第一个参数是消息内容第二个参数是目标窗口的origin只有完全匹配才会传递过去。如果实在不想限制可以传*但我建议生产环境里一定要写明确的目标origin这是基本的安全习惯。iframe内部的页面接收消息window.addEventListener(message, function (event) { // 校验消息来源防止其他窗口伪造消息 if (event.origin ! https://parent-site.com) { return; } console.log(收到父页面消息 event.data); // 处理完后可以回信 event.source.postMessage(子页面已收到, event.origin); });接收回调里的event对象包含三个关键成员data是消息内容origin是发送方originsource是发送方的window对象。拿到source之后子页面就能反手给父页面回话。这种双向通信机制在H5页面与小程序WebView交互、插件系统、跨域客服弹窗等场景中都很常见。很多人的第一个跨域通信项目就是这么写出来的。我在这里多说一句写跨域通信一定要先做来源校验再处理数据跳过校验等于给恶意页面留了一扇门。4.3 X-Frame-Options与CSP frame-ancestors为什么有些页面嵌不进来经常有朋友在群里问为什么我写了个iframe嵌入百度地图或者某些页面结果是白屏或者显示被拒绝连接这大概率不是代码写错了而是对方页面通过响应头拒绝了iframe嵌入。最常用的响应头是X-Frame-Options有三个可选值响应头值含义DENY任何情况下都不允许被嵌入iframeSAMEORIGIN只允许同源页面嵌入自身ALLOW-FROM https://xxx.com允许指定来源嵌入多数现代浏览器已不支持比如微信公众平台、抖音的部分页面都设置了X-Frame-Options: DENY你硬嵌只会得到一个空白区域。更现代的方式是CSP策略里的frame-ancestors指令Content-Security-Policy: frame-ancestors self https://allowed-site.comself表示允许同源页面嵌入后面可以追加允许的域名列表。CSP能同时控制iframe的加载来源而X-Frame-Options只管能不能被嵌入两者侧重点不同但会叠加生效。热搜词里提到的“dataease的社区版明确禁止通过iframe嵌入”实质就是这类安全策略在起作用。如果遇到页面无法嵌入可以在浏览器开发者工具的网络面板里查看对方响应头里是否带了这两个字段定位起来非常快。5. 踩坑记录滚动条、高度自适应、嵌套与兼容性5.1 隐藏滚动条的前世今生很多新手都有个需求把iframe里的滚动条隐藏掉。在不同时代做法是完全不同的。在老式框架场景里直接用frame的scrolling属性frame srctop.html scrollingnoiframe也可以用类似写法iframe srcsidebar.html scrollingno styleoverflow: hidden;/iframescrollingno是一个表现属性并不符合现代HTML规范但浏览器兼容性依然很好。纯CSS方案是设置overflow: hidden它作用于iframe本身的外层容器能不能隐藏内部滚动条取决于内部文档是否真的会产生滚动条。如果你的项目需要强控内部滚动条最干净的办法是在iframe内部页面的样式里加上html, body { overflow: hidden; }外部页面是控制不了iframe内部文档的滚动效果的因为内部文档是独立的。这是iframe隔离特性带来的限制也经常是新手卡壳的地方。不太建议强行隐藏内部所有滚动条因为如果内容区域高度超过视口用户拿不到滚动条就等于页面内容被截断可用性很差。5.2 iframe高度自适应同源与跨域两种解法做后台系统时经常遇到这样的需求iframe加载的页面内容高度不固定但外层页面要给iframe一个固定高度导致内部内容显示不全要么出现顶部空白要么出现内部垂直滚动条。同源页面之间可以在新页面加载完成后测量内部文档的高度并回填给iframevar frame document.getElementById(myFrame); frame.onload function () { var innerHeight frame.contentDocument.documentElement.scrollHeight; frame.style.height innerHeight px; };documentElement.scrollHeight拿的是内部文档根元素的总高度包括因为内容撑开产生的滚动区域高度这个方法在绝大多数现代浏览器里都稳定可用。跨域情况下父页面没法直接测量iframe内部内容的高度这时候需要内部页面主动告知。内部页面加载完通过postMessage把自己文档的scrollHeight发给父页面// 子页面内部 var height document.documentElement.scrollHeight; parent.postMessage({ type: resize, height: height }, *);父页面监听消息后调整iframe高度window.addEventListener(message, function (event) { if (event.data.type resize) { document.getElementById(myFrame).style.height event.data.height px; } });在实际项目里体验更好的是借助ResizeObserver监听内部页面内容区域的变化内容一变就自动重新计算高度避免某些异步加载的数据撑开页面后高度不更新的问题。这个方案虽然比简单用setInterval轮询要优雅但要做跨域联合调试复杂度上了一个级别。5.3 主页面如何调用iframe里的函数这是另一个高频热搜问题。先说结论同源情况下可以跨域情况下不能直接调用。同源且iframe加载完成后主页面可以这样调用iframe内部的全局函数var frame document.getElementById(myFrame); frame.contentWindow.insideFunction();前提是insideFunction必须是iframe内部页面挂载在window对象上的全局函数不能是一个模块内部的私有函数。反过来iframe内部调用父页面的函数parent.parentFunction();如果父页面里又嵌了一层iframe内部页面想直接操作祖父级页面可以用parent.parent但层数越多越容易乱最好统一走postMessage消息队列不要滥用这种跨层访问。跨域时想调用对方函数唯一安全路径是postMessage。也就是说通信的本质从“直接调用函数”退化成“发消息请对方执行”。这看起来多绕了一层但换来的是可靠的安全边界值得遵守。5.4 frameset页面里不要放body一个常见新手错误学习frameset时最常见的报错场景是明明框架集页面结构看着对但浏览器里什么都没显示或者显示了普通的空白页。原因绝大多数是页面里同时出现了body和frameset。规范层面上frameset会替代body成为文档的主体结构两者不能共存。如果你写了body frameset cols200, * frame srcleft.html frame srcright.html /frameset /body浏览器会把frameset放到body内部的文档流里但实际上frame标签在HTML5标准里是不允许存在于普通文档流中的结果就是框架根本渲染不出来或者渲染出来却出现各种怪异行为。正确写法是严格省略body标签frameset cols200, * frame srcleft.html frame srcright.html /frameset如果你在维护老框架集页面时发现网页结构错乱第一件事就去检查是不是有人把body标签“好心”地加回来了。6. 现代布局替代方案从框架集到Flexbox与Grid6.1 用Flexbox实现经典三段式布局学了frameset以后很容易产生一个疑问这种上、左、右三分栏布局换成现代CSS能怎么写答案是用Flexbox或Grid都能轻松完成。先看Flexbox版本。顶部固定80px下方左侧180px、右侧自适应!DOCTYPE html html langzh-cn head meta charsetutf-8 titleFlexbox经典后台布局/title style * { margin: 0; padding: 0; box-sizing: border-box; } body { display: flex; flex-direction: column; height: 100vh; } .top { height: 80px; background: #2c3e50; color: #fff; flex-shrink: 0; } .main { flex: 1; display: flex; overflow: hidden; } .sidebar { width: 180px; background: #f8f9fa; flex-shrink: 0; } .content { flex: 1; overflow: auto; padding: 20px; } /style /head body div classtop顶部区域/div div classmain div classsidebar左侧菜单/div div classcontent主内容区/div /div /body /html把浏览器窗口缩小时左侧宽度不变、顶部高度不变只有右侧内容区自适应并出现滚动条效果和当初的frameset几乎完全一致。关键点在于外层用flex-direction: column声明纵向排列内层再让.main继续用display: flex横向排列。Flexbox这种“外层主轴改方向里层再用一次Flex”的嵌套思路是车后台布局最常见的套路。6.2 用Grid实现更复杂的后台框架布局Flexbox擅长一维排列要么横排要么竖排Grid擅长的则是二维网格。同样是上面的例子Grid写法更简练div classlayout div classtop顶部区域/div div classsidebar左侧菜单/div div classcontent主内容区/div /div.layout { display: grid; height: 100vh; grid-template-rows: 80px 1fr; grid-template-columns: 180px 1fr; grid-template-areas: top top sidebar content; } .top { grid-area: top; background: #2c3e50; color: #fff; } .sidebar { grid-area: sidebar; background: #f8f9fa; } .content { grid-area: content; overflow: auto; }这里最核心的是grid-template-areas它用一组字符画直接描述区域分配“top top”表示顶部占满一整行“sidebar content”表示第二行左菜单右内容。这种按区块起名字的写法维护起来比一堆flex嵌套容易理解得多尤其是在你几个月后再回头改代码的时候至少不用一点一点推算布局关系。Grid还能很方便地做出更加复杂的三列、四列布局比如左侧两级菜单、右侧双栏面板这些在框架集时代需要嵌套两三层fremeset才能实现的结构在Grid里就是几行声明的事。6.3 什么情况下仍然值得使用iframe说了这么多那iframe是不是也应该被淘汰完全不是。即便在今天的前端工程体系里iframe依然有几个无法被替代的位置。第一嵌入第三方地图与视频。地图类、视频类产品通常不允许你直接操作其页面DOM只愿意开放iframe嵌入接口这是最简单、最合规的集成方式。第二实现应用级隔离。如果你要把一个完全独立的旧系统以页面方式嵌入到新后台里iframe几乎是唯一的选择。它自带的样式隔离、脚本隔离能避免新老系统在CSS和JavaScript上互相干扰。第三安全沙箱与富文本预览。在后台管理里预览用户上传的HTML邮件、模板文件或者测试一段不可信脚本的执行效果用带sandbox属性的iframe比用window.open安全得多。我在实际项目里有一个经验法则如果嵌入的目标是“外部服务”首选iframe如果目标是自己维护的业务页面优先考虑用统一路由和组件方案实现而不是套iframe。因为iframe内容与主应用的通信成本、样式一致性、SEO支持都比较受限自己团队的页面长期用iframe嵌套维护效率会明显下降。至于frameset和frame我的建议是通读一遍语法理解一个经典嵌套案例知道name和target怎么联动剩下的时间完全可以交给现代布局。这门老技术教会我们的不是继续用它去做新项目而是理解网页从“多窗口组合”到“单文档流布局”的演进逻辑。理解了这段历史你在看今天各种响应式布局方案时会多一层别人看不到的理解。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑