iframe的优缺点
# 优缺点
# 优点:
重载页面时不需要重载整个页面,只需要重载页面中的一个框架页(减少了数据的传输,加快了网页下载速度)
技术易于掌握,使用方便,使用者众多,可主要应用于不需搜索引擎来搜索的页面
方便制作导航栏
如果遇到加载缓慢的第三方内容如图标和广告,这些问题可以由 iframe 来解决。
网页如果为了统一风格,头部和版本都是一样的,就可以写成一个页面,用 iframe 来嵌套,可以增加代码的可重用。
# 缺点:
会产生很多页面,不容易管理
不容易打印(目前只能实现分框架页面的打印,不能实现对 frameset 的打印)
浏览器的后退按钮无效(只能针对实现当前光标所在页面的前进与后退,无法实现 frameset 整个页面的前进与后退)
代码复杂,无法被一些搜索引擎索引到(有些搜索引擎对框架结构的页面不能正确处理,会影响到搜索结果的排列名次)
多数小型的移动设备(手机)无法完全显示框架
多框架的页面会增加服务器的 http 请求,影响页面的并行加载。
(并行加载:同一时间针对同一域名下的请求。一般情况,iframe 和所在页面在同一个域下面,而浏览器的并加载的数量是有限制的。)
- 框架结构有时会让人感到迷惑,特别是在几个框架中都出现上下、左右滚动条的时候。这些滚动条除了会挤占已经非常有限的页面空间外,还会分散访问者的注意力。访问者遇到这种网站往往会立刻转身离开。他们会想,既然你的主页如此混乱,那么网站的其他部分也许更不值得浏览。
使用 js 动态给 iframe 的 src 加载页面内容,示例代码如下:
<iframe id="fram"></frame>
document.getelementbyid("fram").src="a2.html"
2
# 为什么尽量少用 iframe
iframes 提供了一个简单的方式把一个网站的内容嵌入到另一个网站中。
iframe 的创建比其它包括 scripts 和 css 的 DOM 元素的创建慢了 1-2 个数量级。
使用 iframe 的页面一般不会包含太多 iframe,所以创建 DOM 节点所花费的时间不会占很大的比重。
但带来一些其它的问题:onload 事件以及连接池(connection pool),即 iframe 会阻塞主页面的 Onload 事件及 iframe 和主页面共享连接池,会影响页面的并行加载。
- iframes 阻塞页面加载,影响网页加载速度
及时触发 window 的 onload 事件是非常重要的。onload 事件触发使浏览器的 “忙” 指示器停止,告诉用户当前网页已经加载完毕。当 onload 事件加载延迟后,它给用户的感觉就是这个网页非常慢。
window 的 onload 事件需要在所有 iframe 加载完毕后(包含里面的元素)才会触发,就会影响网页加载速度。通过 JavaScript 动态设置 iframe 的 SRC 可以避免这种阻塞情况。
- 唯一的连接池
对每个 web 服务器来说,浏览器只打开极少的几个连接数。老的浏览器,包括 IE 6/7 和 Firefox 2,每个主机只有 2 个连接。
在新的浏览器中,连接数增加了。Safari 3+和 Opera 9+增至 4 个,Chrome 1+、IE 8 及 Firefox 3 增至 6 个。
在大多数浏览器中,连接被主页面和它的 iframe 所共享,这意味着有可能 iframe 中的资源占用了可用连接而阻塞了主页面的资源加载。
如果 iframe 中的内容同等重要,或比主页面更重要,这很好。
然而在通常情况下 iframe 中的内容对页面来说不太重要,iframe 占用连接数是不可取的。一个解决方案是在优先级更高的资源下载完成后再动态的给 iframe 的 src 赋值。
总之,iframe 会给你的页面性能带来冲击,尽可能不使用 iframe,当确实需要时,谨慎地使用他们。目前框架的优点可以使用 Ajax 实现,这在某种角度也是一种替代方案。
# iframe 的一些应用场景
iframe 的页面和父页面(parent)是分开的,所以它意味着,这是一个独立的区域,不受 parent 的 CSS 或者全局的 JavaScript 的影响。
典型的,比如所见即所得的网页编辑器;
跨域通信。
JavaScript 跨域总结与解决办法 ,类似的还有浏览器多页面通信,比如音乐播放器,用户如果打开了多个 tab 页,应该只有一个在播放;
- 历史记录管理
解决 ajax 化网站响应浏览器前进后退按钮的方案,在 html5 的 history api 不可用时作为一种替代;
- 纯前端的 utf8 和 gbk 编码互转。
比如在 utf8 页面需要生成一个 gbk 的 encodeURIComponent 字符串,可以通过页面加载一个 gbk 的 iframe,然后主页面与子页面通信的方式实现转换;这样就不用在页面上插入一个非常巨大的编码映射表文件了,其中子页面内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="gbk" />
<script>
window.encoding = function (str) {
//利用a元素的href属性来encode
var a = document.createElement('a')
a.href = '/?q=' + str
var url = a.href //这里读取的时候会自动编码
a.href = '/?q='
return url.replace(a.href, '')
}
</script>
</head>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
把这个 iframe 部署到父页面的同源服务上,就能在父页面直接调用 iframe 中的 encoding 接口了。
用 iframe 实现无刷新文件上传,在 FormData 不可用时作为替代方案;
创建一个全新的独立的宿主环境。
iframe 还可以用于创建新的宿主环境,用于隔离或者访问原始接口及对象,比如有些前端安全的防范会覆盖一些原生的方法防止恶意调用,通过创建一个 iframe,然后从 iframe 中取回原始对象和方法来破解这种防范;
类似的还有贺师俊 曾经提到的 javascript 裸对象创建中的一种方法:如何创建一个 JavaScript 裸对象 (opens new window)
用来加载广告,例如联盟广告;
一般邮箱使用 iframe,如 QQ 邮箱;
IE6 下用于遮罩 select
曾经在 ie6 时代,想搞一个模态窗口,如果窗口叠加在 select 元素上面,是遮不住 select 的,为了解决这个问题,可以通过在模态窗口元素下面垫一个 iframe 来实现遮罩。
- 网页调起客户端应用
在移动端用于从网页调起客户端应用(此方法在 iphone 上并不安全,慎用!)。
比如想在网页中调起支付宝,我们可以创建一个 iframe,src 为:
alipayqr://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode={支付二维码扫描的url}
浏览器接收到这个 url 请求发现未知协议,会交给系统处理,系统就能调起支付宝客户端了。
我们还能趁机检查一下用户是否安装客户端:给 iframe 设置一个 3-5 秒的 css3 的 transition 过渡动画,然后监听动画完成事件,如果用户安装了客户端,那么系统会调起,并将浏览器转入后台运行,进入后台的浏览器一般不会再执行 css 动画,这样,我们就能通过判断 css 动画执行的时长是否超过预设来判断用户是否安装某个客户端了:
module.exports = function (url, onSuccess, onFail) {
// 记录起始时间
var last = Date.now()
// 创建一个iframe
var ifr = document.createElement('IFRAME')
ifr.src = url
// 飘出屏幕外
ifr.style.position = 'absolute'
ifr.style.left = '-1000px'
ifr.style.top = '-1000px'
ifr.style.width = '1px'
ifr.style.height = '1px'
// 设置一个4秒的动画用于检查客户端是否被调起
ifr.style.webkitTransition = 'all 4s'
document.body.appendChild(ifr)
setTimeout(function () {
// 监听动画完成时间
ifr.addEventListener(
'webkitTransitionEnd',
function () {
document.body.removeChild(ifr)
if (Date.now() - last < 6000) {
// 如果动画执行时间在预设范围内,就认为没有调起客户端
if (typeof onFail === 'function') {
onFail()
}
} else if (typeof onSuccess === 'function') {
// 动画执行超过预设范围,认为调起成功
onSuccess()
}
},
false
)
// 启动动画
ifr.style.left = '-10px'
}, 0)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 关于 iframe 在跨域的使用
跨域:因为 JavaScript 同源策略的限制,a.com 域名下的 js 无法操作 b.com 或是 c.a.com 域名下的对象。
JavaScript 出于安全方面的考虑,不允许跨域调用其他页面的对象,但在安全限制的同时也给注入 iframe 或是 ajax 应用上带来了不少麻烦。
网络上已经有非常多可行的方案,此文只限定在 iframe 中的几种跨域方案。
- document.domain+iframe 的设置
document.domain,这是浏览器暴露出来的一个准只读属性(之所以说它是准只读属性,是因为它可以设置为当前域名的超级域),利用这个特性,可以实现主域名相同子域名不同的网页实现通信。
- 使用 HTML5 postMessage
HTML5 提供的 API,可以安全的启用跨域通信。
语法:targetWindow.postMessage(data, targetOrigin),data 参数是指要传递的数据。
如何在目标窗口接收到数据呢?编写如下代码即可:
window.addEventListener(
'message',
function (evt) {
console.log(evt.data)
// evt.data 即是 postMessage 中传递过来的数据。
},
false
)
2
3
4
5
6
7
8
特别注意两点:
如果是协议和端口造成的跨域问题“前台”是无能为力的。
在跨域问题上,域仅仅是通过“URL 的首部”来识别而不会去尝试判断相同的 ip 地址对应着两个域或两个域是否在同一个 ip 上。