frameset、frame、iframe 彻底讲透:用法区别与实战技巧 聊到 HTML 框架很多人第一反应是“这不是早淘汰了吗”。确实frameset 和 frame 已经在 HTML5 里被正式废弃但只要你翻过老项目、考过网页设计证书、或者维护过十年前的企业内网系统就一定见过这种页面左侧菜单、右侧内容点一下菜单右边换个页面。这种布局就是 frameset frame 干的活。而 iframe 到今天仍然活得好好的视频网站、地图、第三方表单、BI 报表都在用它。这篇文章就把 frameset、frame、iframe 这三兄弟一次讲透告诉你它们分别怎么用、各自坑在哪、以及现在真正值得学的是哪部分。适合正在学 HTML 的初学者、高校计算机相关专业学生以及需要维护老系统的开发人员参考。这几年你搜“框架”出来的可能是 Spring Boot、PyTorch、agent 框架甚至企业风险管理框架它们和 HTML 里的框架完全是两码事。HTML 框架特指用 frameset、frame、iframe 这几个标签把页面“拼”起来的做法。有些设备报错信息里也带 frame 字样那属于另一个领域的术语和网页无关别混为一谈。1. 先把“框架”这个词掰开揉碎1.1 网页框架解决的是“复用”问题在没有框架的年代做一个多页面网站最痛苦的事情是什么重复。每一页都要复制一遍导航栏、顶部 Logo、底部版权信息改一个菜单文字就要把所有页面全部改一遍漏改一个就是事故。框架出现之后思路变了把公共部分做成单独页面再用一个“壳页面”把它们组合起来浏览器同时加载多个子窗口拼成一个完整页面给用户看。打个比方框架页就像一份 Word 主文档里面用“插入对象”嵌入了好几份子文档你编辑子文档的时候主文档不用动。这个思路在 Web 早期非常先进因为它真正解决了“一处修改全局生效”的维护问题。理解了这个动机你就明白 frameset 为什么会在那个年代流行也就知道为什么它后来会被更现代的组件化方案替代。1.2 frameset、frame、iframe 三者到底是啥关系很多初学者把这三个标签混在一起其实它们的关系很清晰frameset框架集负责把浏览器窗口切成几个区域它是文档级的结构标签不能和 body 同时存在。frame框架集中的单个子窗口通过 src 属性加载一个页面必须放在 frameset 里面。iframe内联框架它只是一个普通标签可以放在 body 里的任意位置作用是把另一个页面“嵌”到当前页面的某个矩形区域里。简单说frameset 是“拆窗口”iframe 是“套盒子”。frameset 必须配合 frame 使用而 iframe 是独立存在的。考试里常考的判断题“iframe 是 frameset 的子标签”是错的两者根本不是父子关系。它们唯一的共同点就是都能在一个页面里展示另一个页面但底层逻辑完全不同后面我会详细讲。2. frameset 与 frame被淘汰但必须会看的“页面组合术”2.1 一个最典型的上下左右结构实例先上一个经典的老式框架页结构是“顶部标题 左侧导航 右侧内容”!DOCTYPE html PUBLIC -//W3C//DTD HTML 4.01 Frameset//EN http://www.w3.org/TR/html4/frameset.dtd html head meta charsetutf-8 title经典框架页/title /head frameset rows80,* frameborder1 border2 frame srctop.html nametopFrame scrollingno noresizenoresize frameset cols180,* frame srcleft.html nameleftFrame frame srcmain.html namemainFrame /frameset /frameset /html注意几个关键点。第一这个文档没有 body 标签frameset 直接出现在 head 之后这是框架页和老式 DTD文档类型定义的强制要求。如果你用 HTML5 的!DOCTYPE html写这套代码浏览器直接不认页面会白屏。第二rows 表示上下切分上面 80 像素给顶部栏剩下的用星号*表示“占满剩余空间”。第三我在右侧又嵌套了一层 frameset用 cols 切成左右两栏左边 180 像素右边是剩余部分。frameset 是可以层层嵌套的这也是当年实现复杂布局的唯一手段。2.2 常用属性与高频考点框架页的属性不算多但每一个都很重要考试和面试也喜欢在这里出题属性作用说明rows行切分支持像素、百分比、星号如rows80,*cols列切分用法同 rows如cols180,*src子窗口加载的页面加在 frame 上必填name给框架命名配合链接的 target 使用非常关键noresize禁止拖动框架边框没有值写上就生效scrolling是否显示滚动条yes、no、auto 三种frameborder是否显示边框0 或 1老式页面常用 0 去边框marginwidth / marginheight子窗口内容边距控制内部页面与框架边的间距name 属性值得单独说一下。你可以在左侧导航里写这样一段代码a hrefnews.html targetmainFrame在右侧打开新闻/atarget 指向 mainFrame点击链接后news.html 只会在右侧的框架里打开其他区域完全不动。这是当年“局部刷新”的经典做法现在的单页应用其实也是这个思路只是实现方式完全不同。理解 target name 的配合对后面学 iframe 的 name 属性也很有帮助。2.3 为什么后来被淘汰四个致命伤frameset 被废弃不是挤牙膏式的缓慢淘汰而是整个行业的集体抛弃原因非常充分。第一SEO 是硬伤。搜索引擎的爬虫抓取框架页时看到的只是一个空壳真正有价值的内部页面无法被有效索引网站关键词排名做不上去这对商业网站是致命的。第二URL 无法定位内部状态。用户在右侧翻了很多页把地址复制给朋友对方打开后看到的还是默认的首页因为地址栏的 URL 从头到尾没有变过收藏夹和浏览器后退键也经常失灵。第三可访问性和移动端适配极差。屏幕阅读器解析框架页经常混乱手机小屏幕上切出多个固定区域更是灾难。第四安全风险。框架页容易被人恶意套壳做点击劫持内嵌页面也无法感知自己是不是被嵌在了别人的页面上。那为什么还要学三个理由。一是大量老系统、老网站还在跑你不会读代码就没法维护二是很多高校的网页设计课程和计算机等级考试内容里还有它不学就拿不到分三是了解了框架页的局限你才能理解现代前端为什么要用组件化、路由、单页应用那一套方案技术演进是有因果关系的。3. iframe至今仍在用的“页面嵌套器”3.1 最基本、最完整的 iframe 写法frameset 被淘汰了iframe 却越活越好。原因很简单frameset 是“拆掉整个窗口重来”动静太大iframe 只是当前页面里的一个小盒子不影响页面整体结构和 SEO。看一个现代标准写法!DOCTYPE html html langzh-cn head meta charsetutf-8 titleiframe 基础示例/title /head body h1下面是内嵌的地图/h1 iframe srchttps://example.com/map width100% height450 styleborder:0 allowfullscreen loadinglazy title公司地址地图/iframe /body /htmlsrc 指向要加载的页面width 和 height 控制尺寸可以用百分比实现响应式。styleborder: 0 是去掉默认边框的现代写法比老式的 frameborder0 更推荐。loadinglazy 是 HTML5 新增的懒加载属性页面滚动到 iframe 附近才加载内容能明显提升首屏速度。title 给屏幕阅读器提供描述这是无障碍要求的必备项很多教程都会漏掉。3.2 常见的应用场景iframe 在真实项目里出镜率相当高地图嵌入百度地图、高德地图、腾讯地图都提供“生成嵌入代码”复制出来就是一个 iframe。视频外链B站、YouTube 的视频分享功能本质上也是给出一个 iframe 地址。第三方表单金数据、腾讯问卷、石墨文档的公开分享都可以用 iframe 嵌到自己的页面里。老后台改造很多企业内部系统至今仍是“左侧菜单 右侧 iframe”的结构改造成本最低。BI 报表集成部分数据分析产品支持用 iframe 把报表面板嵌进门户首页。每个场景的注意事项不一样。地图还算开放但很多视频平台会限制外链播放第三方表单要关注跨域通信后台嵌入要注意会话保持和权限控制。所以用 iframe 之前先确认对方允不允许你嵌以及对方支不支持你需要的交互方式这比写代码更重要。3.3 和 frameset 的本质区别谁嵌套谁这是高频笔试简答题iframe 和 frameset 有什么区别我的回答分三层。首先文档结构不同。frameset 会替代 body整个文档就是一个框架集页面本身没有实际内容iframe 则是 body 内部的一个普通元素本身也是当前页面只是内部又嵌套了一个页面。其次使用方式不同。frameset 必须配合多个 frame 才能实现布局是一种页面组织方案iframe 只是内容展示组件用哪个页面、放多大、放在哪全由当前页面决定。第三生命周期不同。frame 子页面之间没有严格的父子关系更像平级排列iframe 有明确的父页面和子页面关系也就有了后续要讲的父子通信问题。一句话总结frameset 把浏览器窗口切成了几块iframe 是在一块已经存在的内容里又开了一扇窗。4. 父页面与 iframe 的通信不只是嵌套4.1 同源场景获取 iframe 内容和调用函数很多人把 iframe 嵌进去之后发现“我就想拿到里面某个元素的值怎么这么难”难不难取决于同不同源。同源指的是协议、域名、端口完全一致。这种情况下父页面可以直接操作子页面。示例// 父页面获取 iframe 文档对象 var iframe document.getElementById(myFrame); var iframeDoc iframe.contentDocument || iframe.contentWindow.document; // 读取 iframe 里的元素内容 var msg iframeDoc.getElementById(msg).innerHTML; console.log(msg); // 父页面调用 iframe 里的全局函数 iframe.contentWindow.someFunction(); // iframe 内部调用父页面的函数 window.parent.someParentFunction();主页面能不能调用 iframe 的函数能前提就是同源并且 iframe 已经加载完成。新手最常见的坑是在 iframe 还没加载完就去取内容拿到的是 null。稳妥做法是监听 iframe 的 load 事件或者用iframe.contentWindow.document.readyState做判断。另外用 jQuery 找“上一层 iframe”时没有专门的 API核心还是window.frameElement// 在 iframe 内部 var $frame $(window.frameElement); // 包裹当前页面的 iframe 元素 var $parent $(window.parent.document); // 父页面的 documentwindow.frameElement是子页面获取“我是被哪个 iframe 包进来”的原生方法掌握它比记一堆 jQuery 方法管用。4.2 跨域场景为什么“拿到了却动不了”跨域是 iframe 学习里最容易让人崩溃的部分。最典型的现象是父页面明明加载了另一个域名的 iframe你在控制台里也能看到整个 DOM 结构但一执行contentDocument就抛异常。这不是代码问题是浏览器在保护跨域页面的安全。同源策略是浏览器的基础安全机制可以理解为不同源的两个页面只能“看到对方的存在”但“不能翻对方的抽屉”。绕过同源策略的合法且推荐方式是使用window.postMessage。基本流程分两步。父页面监听消息window.addEventListener(message, function (e) { // 用 origin 校验消息来源这一步绝对不能省 if (e.origin ! https://child.example.com) return; console.log(收到子页面的消息, e.data); });子页面往父页面发消息window.parent.postMessage( { type: height, value: document.body.scrollHeight }, https://parent.example.com );postMessage 的第一个参数是消息内容第二个参数是目标源写上具体域名而不是*能避免消息被无关页面接收。很多跨域安全问题都是因为开发者偷懒写*导致敏感信息泄露。4.3 实操隐藏滚动条、自适应高度一次做对iframe 有个老大难问题内容多高iframe 不会自动跟着长高默认就是固定高度于是出现双滚动条或者内容被截断。先说隐藏滚动条。老派做法是scrollingno能去掉 iframe 自己的滚动条。但很多情况下滚动条来自子页面 body只设这个属性没用还要在子页面加html, body { height: 100%; overflow: hidden; }注意把 overflow 设置成 hidden 之后子页面内容超长就无法滚动了。所以如果子页面内容本身就需要滚动就不要想着藏滚动条而是应该做自适应高度。自适应高度的核心思路是“先让内容自然展开再把真实高度回传给父页面”。同源场景最简单window.addEventListener(load, function () { var f document.getElementById(contentFrame); f.style.height f.contentWindow.document.body.scrollHeight px; });跨域场景就要结合前面说的 postMessage子页面测到自己的高度后发给父页面父页面动态修改 iframe 的高度。内容里有图片时图片加载前后高度变化很大最好在子页面所有图片 onload 之后再测高度或者用ResizeObserver监听内容尺寸变化持续更新高度。这一步踩过坑的人都知道固定写死600px或者只在页面加载时算一次内容稍一变就前功尽弃。5. 常见问题与排查技巧实录5.1 高频问题速查表把我在实际项目里遇到过的 iframe 相关问题整理成一张表基本覆盖了 80% 的排查场景现象常见原因处理方法iframe 区域空白src 路径错误、页面被拒绝嵌入、协议不匹配先直接在浏览器打开 src 地址看能不能访问提示 403 / 拒绝连接目标站点明确禁止被 iframe 嵌入换官方嵌入接口或用后端代理转发控制台出现 X-Frame-Options 报错对方返回X-Frame-Options: DENY/SAMEORIGIN只能放弃直接嵌入找官方嵌入 SDK双滚动条问题iframe 和子页面各自出现滚动条按 4.3 节方式处理要么隐藏要么自适应iframe 高度一直不对没考虑图片加载、动态内容、懒加载用 load 事件 ResizeObserver 动态更新嵌入页面登录状态丢失第三方 Cookie 被浏览器限制使用同一主域下的页面或引导用户允许第三方 Cookie主页面取不到 iframe 内容跨域或 iframe 未加载完成跨域用 postMessage同源记得等 load 事件iframe 里的页面被点击后跳转整个父页面子页面用了top.location跳转这不是 bug是子页面主动行为需对方配合出现空白时最快的方法是手动打开 iframe 的 src 地址。能打开说明问题出在嵌入策略打不开就是路径或服务器问题。我见过太多人一看到空白就怀疑是 JS 错误最后发现只是 URL 少写了一个字母。5.2 平台禁止被嵌入的现实情况现在很多知名平台都默认禁止别人用 iframe 嵌入自己网上搜一下“某产品禁止 iframe 嵌入”就能看到大量真实例子。常见的有数据分析产品、低代码平台、文档协作工具等这些平台的社区版或免费版经常明确写明“不支持 iframe 嵌入”背后通常是X-Frame-Options响应头或Content-Security-Policy: frame-ancestors限制了来源。遇到这种情况第一个动作不是想办法绕过而是先查官方文档看有没有提供专门的嵌入方案、白名单配置或者 JS SDK。很多商业软件其实开放了嵌入能力只是需要通过配置或付费获得权限。第二个备选方案是后端写一个转发接口把目标页面内容通过自家服务器拉回来再输出但这种方式有版权和合规风险务必先确认授权也别拿去硬怼对方的封锁机制。安全规范上讲一旦对方明确设置了禁止嵌入的响应头继续强行破解是不合适的做法。5.3 我踩过的两个坑第一个坑是自适应高度算不准原因是 iframe 里的内容包含大量图片和懒加载模块。页面初次加载时的高度和图片完全渲染后的高度可能差出几千像素。后来我改成在子页面里遍历所有 img 元素等它们的 onload 全部触发之后再测高度配合 ResizeObserver 监听后续变化才算彻底解决。第二个坑是“我以为同源其实不同源”。当时主站域名是 www.example.comiframe 用的是 static.example.com以为都是 example.com 就放心用 contentDocument 直接操作结果控制台疯狂报错。后来在测试环境用 nginx 把两个域名反代成同一个域名问题消失。记住同源策略看的是完整域名不是“看起来像同一个公司”。6. 给新人的学习顺序建议与个人体会6.1 frameset 学到什么程度就够了如果你不是专门维护几年前的政府网站或企业内网项目frameset 真的不用深钻。会看、会改、能应付考试就够了。所谓会看是看到frameset rows80,*能说出页面结构会改是能调整 rows 和 cols 的数值改变布局能修改 frame 的 src 换内容应付考试就是能把 rows、cols、name、noresize、scrolling 这些属性的作用写清楚能解释它为什么被 HTML5 废弃。不建议在这个已经被淘汰的技术上花超过半天时间时间应该留给更通用、更有生命力的部分。6.2 iframe 值得重点掌握的四个方向iframe 在现代前端开发里的地位比很多人想象的重要重点掌握这四点一是安全边界包括同源策略、postMessage 的来源校验、X-Frame-Options 和 CSP 的理解二是父子通信同源和跨域两套方案都要熟练这是后面做复杂嵌套页面的基础三是性能优化对应 loadinglazy、减少不必要的 iframe 数量、避免阻塞主页面渲染四是无障碍和体验给 iframe 加 title处理好滚动和键盘焦点让屏幕阅读器用户也能正常使用。6.3 一句掏心窝的话我个人在带新人和维护老项目的过程中最深的一个体会是技术会被淘汰但解决问题的思路不会。frameset 的“页面组合”思想和今天前后端分离、组件化、微前端里的很多理念是一脉相承的iframe 的“嵌入”能力到现在也没有被完全替代。所以学这两个东西别急着背标签多想想它为什么这么设计你就能在以后遇到类似需求时多一个可选的方案。如果你现在正在学 HTML我的建议是frameset 看一眼就好iframe 认认真真练一遍把通信和适配这两个硬骨头啃下来你会在后面很多项目里感谢自己。