目录
1.问题描述
有一个监控平台的项目,数据界面化展示使用Grafana,但是又不能完全使用Grafana作为前台,还有自己系统中相关业务页面,关于数据展示页只能通过嵌入Grafana的panel的方式来实现,当然,如果页面的所有视图全部都可以通过Grafana来配置的话,那可以直接嵌入整个dashboard的方式来实现,这种情况就不用考虑今天要说的问题了。但项目中的数据视图有自己开发的,也有使用Grafana的,所以一个视图中是混合着来的,只能引入多个Grafana的panel分享链接。
Grafana的panel分享链接如下
可以明显看到,这是一个iframe串。
当一个页面嵌入了多iframe的时候,每一个iframe都会下载对应的静态js和css,以Grafana为例,会有两个比较大的js要下载,一个是3.6M的vendor.js,一个是1.7M的app.js。如果有6个页面,那就会下载6次,这体验实在是太糟糕了。
2.解决思路
最直观的想法就是有没有什么办法让这些iframe之间共享缓存?但是通过加载的过程,可以看到它们全部都是自己下载自己的,并没有使用缓存,加载完毕后,F5刷新,会看到是走缓存了,但是第一次的体验始终是糟糕和不够优雅的。
有以下几种思路
2.1 使用load
既然iframe每次都要加载静态资源,那么如果我们使用jquery的load来加载页面,所有被load的页面都在一个页面中,那css/js是否就可以使用缓存了呢?
尝试之后发现,这种方式不适合目前的情况,因为会引起跨域请求的问题,Grafana的panel面板url和前台程序是两套,必然会引起跨域请求,所以放弃了。
jquery的load主要用于大页面分割,加载不同小页面的情况下,把每一块单独写到独立的html中,然后load进来,都是同一个项目域下的页面才行。
2.2 代理模式
尝试使用中间代理的方式,通过代理缓存公用js,iframe请求代理,然后代理和前台程序部署在一块。直观上感觉这样好像是不错的解决方案,因为代理会缓存那些js和css,不用每个iframe再远程请求了。
但实际上,这种方式没不能带来多大的优化效果,因为通过浏览器的调试模式可以看到,每个iframe还是会各自加载各自的静态资源,你无法避免每个iframe的请求,即这个缓存是代理那里,并没有到浏览器中,所以没有太大的效果。
2.3 父页面引入公用js
尝试把比较大的两个js拿出来,直接在父页面引入,然后让iframe使用父页面的缓存,这种方案是参考这里的。
首先要说的是,这种方案是可行的,但是有一些注意事项。
我写了一个简单的例子进行测试,如下
<body>
<div style="padding:1rem;border: 1px solid #be590a; ">
<iframe id="f1" src="http://192.168.1.116:3000/d-solo/xkqZOmqiz/zai-xian-jie-ru-ye-wu-shi-tu?orgId=1&panelId=6" width="300px" height="300px;"></iframe>
</div>
<div style="padding:1rem;border: 1px solid #be590a; ">
<iframe id ="f2" src="http://192.168.1.116:3000/d-solo/xkqZOmqiz/zai-xian-jie-ru-ye-wu-shi-tu?orgId=1&panelId=8" width="300px" height="300px;"></iframe>
</div>
<script type="text/javascript" src="../assets/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="http://192.168.1.116:3000/public/build/vendor.d3fff3b4305980ef147d.js"></script>
<script type="text/javascript" src="http://192.168.1.116:3000/public/build/app.d3fff3b4305980ef147d.js"></script>
</body>
按照上面这样写,运行起来之后,F12,查看f1和f2这两个iframe并没有走缓存,还是各自走各自的。
为什么不行?
造成不走缓存的原因在于,父页面加载到iframe时,iframe是独立的,浏览器新开一个线程,独立去渲染iframe,同时父页面继续渲染,也就意味着,iframe在渲染的时候,父页面中引入的全局js还没有缓存到浏览器中,那么iframe中自然是无法使用缓存了。
改成如下方式,就可以了
<body>
<div style="padding:1rem;border: 1px solid #be590a; ">
<iframe id="f1" src="" width="300px" height="300px;"></iframe>
</div>
<div style="padding:1rem;border: 1px solid #be590a; ">
<iframe id ="f2" src="" width="300px" height="300px;"></iframe>
</div>
<script type="text/javascript" src="../assets/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="http://192.168.1.116:3000/public/build/vendor.d3fff3b4305980ef147d.js"></script>
<script type="text/javascript" src="http://192.168.1.116:3000/public/build/app.d3fff3b4305980ef147d.js"></script>
<script type="text/javascript">
window.onload=function(){
document.getElementById('f1').src="http://192.168.1.116:3000/d-solo/xkqZOmqiz/zai-xian-jie-ru-ye-wu-shi-tu?orgId=1&panelId=6";
document.getElementById("f2").src="http://192.168.1.116:3000/d-solo/xkqZOmqiz/zai-xian-jie-ru-ye-wu-shi-tu?orgId=1&panelId=8";
}
</script>
</body>
即:等父页面加载完毕后,此时静态资源已经在浏览器中缓存了,再动态指定iframe的src,iframe加载时候会先从缓存取,重新运行页面,F12,能看到iframe的请求已经走缓存,大功告成!
总结
所谓缓存,其实是浏览器缓存,和iframe是没关系,只要浏览器有这个缓存,请求的时候不论是什么页面,无论是那个iframe,只要请求资源的域不变,浏览器中有缓存,就直接从缓存中取了,一切并不像想象的那么神秘!
小插曲
再按照上面的方式确认可以走缓存之后,同事的电脑却一直不行,不管怎样每个iframe每次都会各自加载各自的,想来想去,是不是浏览器兼容性的问题?但是同样都是谷歌浏览器,没道理啊。
然后打开同事的浏览器,F12
看到如上设置,笑哭了~~。
Disable cache 禁用cache,那自然是无法使用浏览器缓存了,一般情况,在调试模式的时候,都会这样设置,免得每次都强制刷新,但是这次在这个问题上却刚好适得其反了。