借助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做验证.则可以防止此漏洞.
相关阅读 更多 +










