目前我们开发的产品涉及到大量的JavaScript,特别是前台的网页插件部分。因为这部分是一个网页中的第三方插件,是用户(站长或者blogger)通过在其网页上加入<script>标签来载入,所以和其他的JavaScript略有不同。
我总结一下,这部分功能有如下需求:
这部分的代码是我从去年下半年开始写的,到目前经过多次调整,目前结构已经基本稳定。对于上面提到的1、3、4已经很好的解决,而2和5则只是部分解决,至少我对目前的方案还不是很满意 。
(一)提高加载速度
在论坛里经常有站长提到使用了某个网页插件,功能很不错,但因为影响了网页加载,所以又删掉了。或者有人干脆就从来不用插件,怕影响加载速度。所以对我们来说,这是不得不考虑的头等大事。
最开始我们插件的JS代码是一次性全部加载到浏览器,然后所有的逻辑都在浏览器完成。这样的好处是,一旦代码全部加载完,客户端的响应非常快,除了特定情况不会再有更多的HTTP请求。缺点也非常明显,即一次性要加载大量代码,而大部分情况下,用户浏览网页时并不会去使用插件,造成很大浪费。另一个问题是,加载到第三方网页上的JavaScript无法直接操作Cookie,从而无法进行用户相关的操作(其实应该是可以的,不过比较麻烦)。不过那时候功能本来就不多,用户也很少,因此并没有什么影响。
但那时我们就意识到这样的结构是不行的,很快就进行了调整,使用了Iframe。这样,Iframe内的内容其实就相当于独立的网页了,和当前的网页完全没有关系,也不会影响加载。并且Iframe内是我们自己的domain,可以直接使用Cookie。当然用户使用时的响应速度就没有原来快了,但还在可接受范围内。
另外一个加快网页加载的方法是,在<script>标签处只加载一些基本的代码,其他的代码包括CSS式样则等到全部网页加载结束后再去加载。这样做的另外一个很好的副作用就是,只要处理得好,独立出来的代码还可以被重用。
这种方法里面要解决两个问题。第一个是动态加载JS代码。这个很好解决,动态生成一个<script>元素,设置好后放到head元素里就好了。只不过设置callback时必须兼容IE。代码如下
loadModule: function(url, callback) { var script = d.createElement('script'); script.setAttribute('src', url); script.setAttribute('charset', "utf-8"); script.setAttribute('type','text/javascript'); if (callback) { script.onload = callback; script.onreadystatechange = function() { if (this.readyState == 'complete' || this.readyState == 'loaded') { callback(); } }; } d.getElementsByTagName("head")[0].appendChild(script); }
另外一个问题是,如何等到页面全部加载完成再进行某个动作。如果有jQuery,用$(document).ready()就行了。但jQuery库即使压缩的也有50多k,我们是不可能用的。所以我从jQuery的源代码里参考实现了这个函数,可以替代$(document).ready()使用:
function onDocumentReady(callback){ // Mozilla, Opera and webkit if (document.addEventListener) { document.addEventListener("DOMContentLoaded", function(){ document.removeEventListener("DOMContentLoaded", arguments.callee, false); callback.call(this); }, false ); // If IE event model is used } else if (document.attachEvent) { // ensure firing before onload, // maybe late but safe also for iframes document.attachEvent("onreadystatechange", function(){ if ( document.readyState === "complete" ) { document.detachEvent( "onreadystatechange", arguments.callee ); callback.call(this); } }); // If IE and not an iframe // continually check to see if the document is ready if (document. documentElement.doScroll && window == window.top) (function(){ if (isReady) return; try { // http://javascript.nwbox.com/IEContentLoaded/ document. documentElement.doScroll("left"); } catch( error ) { setTimeout(arguments.callee, 0); return; } callback.call(this); })(); } window.onload = callback; };
目前我们的插件一开始只加载7k左右的代码,现在的网页动辄百来k,其影响基本可以忽略。网页加载结束后,再加载15k代码,另外根据参数还可能加载另外的模块。目前插件运行效果非常好,还没有用户抱怨过加载速度的问题。