文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>借助HTML5 Web Worker 解决资源预加载 的一些顽疾.

借助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 以及之前的那些老古董们.. 好.最后给出两组伪代码. 当然其实只是代码依托的一些基础对象不存在,而导致无法执行: 

//反面教材. 
//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做验证.则可以防止此漏洞.


相关阅读 更多 +
排行榜 更多 +
吹风机射击 v1.0 安卓版

吹风机射击 v1.0 安卓版

飞行射击 下载
吹风机射击 v1.0 安卓版

吹风机射击 v1.0 安卓版

飞行射击 下载
吹风机射击 v1.0 安卓版

吹风机射击 v1.0 安卓版

飞行射击 下载