借助HTML5 Web Worker 解决资源预加载 的一些顽疾.
时间:2010-12-09 来源:Franky
有时.为了访问下一个页面时,可以更快.我们可能在当前页面 onload之后,利用空闲 去预加载下一个页面,以及相关资源.
为了不影响当前页面.我们必须保证.这些预加载的html css js 资源不被解析或执行.
yahoo的加载.js .css 且不解析不渲染的解决方法如下: <script type="text/cache" src="url"></script> (text/这里是随意的.我们是要故意让浏览器不认识.) 如此浏览器可以预先加载任何资源 而不会对资源进行解析和执行.比如某个.js. 但是 Firefox 并不支持这样做. 如果Firefox不认识type.那么就不会发起一个http请求. yahoo的办法是用object Element 来加载 ,Firefox3.0+的浏览器允许你这么做. 但是如果我们需要加载一个text/html资源.那么object标签加载它会导致浏览器解析这个html,并执行里面的脚本. 这个是我们不能容忍的. 想象一下.我们当前页面跑的好好的. 突然因为预加载某个页面 而导致播放音乐.或执行某页的脚本. 那么对于Firefox 我们需要借助 Link Element (rel = "stylesheet")来做. 它可以保证加载 HTML不解析. 但是Link Element 在Firefox中.没有onload ,onerror . 一但我们需要在Firefox解析加载完当前资源后.我们是需要自动把 动态添加的 Link 标签从DOM上移除. script标签也是如此. Opera浏览器对于script 标签无能为力的地方同Firefox 的Link Element 一样.一单不认识type .那么onload onerror都无效. 为了代码更健壮. 我想到了使用 HTML5 Web Worker 的 importScripts()方法. 不熟悉该方法的朋友可以到 http://www.cnblogs.com/_franky/archive/2010/11/23/1885773.html 显然 Worker 为我们提供了一个相对完美的沙箱机制. (并不是完全完美.原因最后说.) 可惜的是.Worker+script方案 仍然无法解决 Opera9.6- Firefox3.0- 的浏览器. 如果你需要解决这两个浏览器.的早期版本.那么需要借助一些反面教材的经验. 对于opera的早期版本.你可需要借助轮询来解决. 因为 虽然opera 对于不认识 type的script标签. onloda onerror都失效. 但是.实际上. scriptElement.readyState 是变化的. 我们可以自己轮询 来查看其 readyState的值.(就好像ie那样做). 来判断资源是否加载完毕. 如果有疑惑可以看看 http://www.cnblogs.com/_franky/archive/2010/06/20/1761370.html 当然.我最希望听到的就是.我们的项目部需要考虑 opera 9.6 FF3.0 以及之前的那些老古董们.. 好.最后给出两组伪代码. 当然其实只是代码依托的一些基础对象不存在,而导致无法执行:
yahoo的加载.js .css 且不解析不渲染的解决方法如下: <script type="text/cache" src="url"></script> (text/这里是随意的.我们是要故意让浏览器不认识.) 如此浏览器可以预先加载任何资源 而不会对资源进行解析和执行.比如某个.js. 但是 Firefox 并不支持这样做. 如果Firefox不认识type.那么就不会发起一个http请求. yahoo的办法是用object Element 来加载 ,Firefox3.0+的浏览器允许你这么做. 但是如果我们需要加载一个text/html资源.那么object标签加载它会导致浏览器解析这个html,并执行里面的脚本. 这个是我们不能容忍的. 想象一下.我们当前页面跑的好好的. 突然因为预加载某个页面 而导致播放音乐.或执行某页的脚本. 那么对于Firefox 我们需要借助 Link Element (rel = "stylesheet")来做. 它可以保证加载 HTML不解析. 但是Link Element 在Firefox中.没有onload ,onerror . 一但我们需要在Firefox解析加载完当前资源后.我们是需要自动把 动态添加的 Link 标签从DOM上移除. script标签也是如此. Opera浏览器对于script 标签无能为力的地方同Firefox 的Link Element 一样.一单不认识type .那么onload onerror都无效. 为了代码更健壮. 我想到了使用 HTML5 Web Worker 的 importScripts()方法. 不熟悉该方法的朋友可以到 http://www.cnblogs.com/_franky/archive/2010/11/23/1885773.html 显然 Worker 为我们提供了一个相对完美的沙箱机制. (并不是完全完美.原因最后说.) 可惜的是.Worker+script方案 仍然无法解决 Opera9.6- Firefox3.0- 的浏览器. 如果你需要解决这两个浏览器.的早期版本.那么需要借助一些反面教材的经验. 对于opera的早期版本.你可需要借助轮询来解决. 因为 虽然opera 对于不认识 type的script标签. onloda onerror都失效. 但是.实际上. scriptElement.readyState 是变化的. 我们可以自己轮询 来查看其 readyState的值.(就好像ie那样做). 来判断资源是否加载完毕. 如果有疑惑可以看看 http://www.cnblogs.com/_franky/archive/2010/06/20/1761370.html 当然.我最希望听到的就是.我们的项目部需要考虑 opera 9.6 FF3.0 以及之前的那些老古董们.. 好.最后给出两组伪代码. 当然其实只是代码依托的一些基础对象不存在,而导致无法执行:
//反面教材. //script Element + link Element(FireFox加载HTML) + object ELement (FireFox JS CSS ...) //需要在恰当时间 调用dispose()方法用来 移除过多的节点.(受Firefox 和 Opera拖累) void function (win,doc,head,ns){ var _class = '__PRELOAD_CLEARTEMPELEMENTS__' , _tag = 'script'; if(ns.preLoad){ return; } var _preLoad = ns.singlePreLoad = !!doc.getBoxObjectFor || win.mozInnerScreenX != null ? (_tag = '*') && function(sURL,isHTML){ //FF3.0+开始支持 object加载其他数据类型. 如果是html则会解析该html var el; if(isHTML){ el = doc.createElement('link'); el.type = 'text/css'; el.rel = 'stylesheet'; el.href = sURL; }else{ el = doc.createElement('object'); el.data = sURL; } el.className = _class; head.appendChild(el); }: function(sURL){//!FF var el = doc.createElement('script'); el.type = 'text/C'; el.className = _class; el.src = sURL; head.appendChild(el); }; ns.preLoad = function(aURL){ typeof aURL == 'string' && (aURL = [aURL]); for(var i = 0 , l = aURL.length ; i < l ; i++)_preLoad(aURL[i]); }; ns.preLoad.dispose = function (){ var list = ns.DOM.$C('__PRELOAD_CLEARTEMPELEMENTS__',_tag,head); for (var i = list.length ; i-- ;){ head.removeChild(list[i]); } list = null; }; }(window,document,document.getElementsByTagName('head')[0],Visit.Util);
//worker + script 方案 引入worker.js onmessage = function(e){ var a = e.data.toString().split(','); for(var i = 0 , l = a.length ;i++){ try{ //避免因加载失败导致脚本不在继续执行下去. 否则 一个imirtScripts.call(null,a); 就解决问题了. //现在,还导致失去了并行加载的优势.但是也是在是木有办法. importScripts(a[i]); }catch(e){} } postMessage('Loaded'); }; void function (win,doc,head,ns){ var _dom = ns.DOM, _workerURL = 'js/worker.js',//worker.js Path _emptyFn = function(){}; if(ns.preLoad){ return; } if(win.Worker){//FF3.5+ Opera10+ Safari4+ Chrome3+ (需要 worker.js支持) ns.preLoad = function(aURL){ var _w = new win.Worker(_workerURL); _w.onerror = _emptyFn; _w.onmessage = function(e){ e.data == 'loaded' && (_w.terminate(),1) && (_w = null);// }; _w.postMessage(aURL); }; }else if(!doc.getBoxObjectFor || !win.opera){//IE6+ Safari3- Chrome2- ns.preLoad = function(aURL){ for(var i = 0,l = aURL.length ; i < l ; i++){ _dom.loadJS(aURL[i],null,null,'text/C'); } }; }else{//FF3.0- Opera9.6- ns.preLoad = _emptyFn; } }(window,document,document.getElementsByTagName('head')[0],Visit.Util);
最后说说为什么说Woker 的沙箱不够好.
importScripts方法加载的资源如果是个脚本.那么实际上它是会执行的. 所以一旦资源拥有者了解这一切,那么可以给你一个执行 postMessage('loaded')语句的脚本.
显然主页面 : e.data == 'loaded' && (_w.terminate(),1) && (_w = null); 这里就会被执行.那么结果就不准确了. 绕过它很容易
每次先给inner worker 传一个随机的key . 主页面 闭包内缓存住这个 随机的key. onmessage中 用此key做验证.则可以防止此漏洞.
相关阅读 更多 +
排行榜 更多 +