动态载入脚本和CSS

一般来说,脚本和CSS都是在页面中用<script>和<link>来进行引入,也就是说,正如你现在所看到的这张网页一样,是静态的写在html中的。

而在一些应用场合,你的某一个页面在不同场合下所需的脚本和CSS是不同的,而如果你同时把这些脚本和CSS全部载入在这张页面之中,则会显得非常庞大,而且非常笨重。

一个CSS的示例便是换肤,比如JQuery UI的ThemeRoller,通过后期载入CSS来达到覆盖原有效果的作用(对于同样的CSS效果而言,都是遵循最近原则,所以后加载的文件会覆盖之前CSS的效果)。而JavaScript的示例便是通过页面事件触发来形成一个不同的效果。比如在你按下按钮A时,你需要显示某个UI组件A并加载a.js来激活这个组件的UI表现功能,而在按下按钮B的时候,则对B有类似的操作。如果在A和B的脚本都很大的情况下,在页面加载之初就全部载入就会显得页面初始载入时间过长,给用户的体验就会显得不那么好。所谓的按需索取,就是这个意思(虽然这样并不是一个很好的设计,因为如果频繁使用会让JS请求增加很多,倒不如对脚本进行压缩合并做一个包来得合算,具体情况还是得具体分析)。

在很多JS的框架中都会有对JavaScript和CSS的动态加载的设定。比如在JQuery中有$.getScript(),Dojo有dojo.require(),YUI有YUILoader(YAHOO.util.YUILoader)。

一切都是xmlhttprequest

对xmlhttprequest的重新认识是AJAX技术的核心。所有的非页面加载期前后台的交互都是由它来完成的。

而对于动态载入来说,你可以选择同步加载或者异步加载。

由于JavaScript执行的时候在浏览器中是单线程操作的(至少它看上去是,特指在函数执行的过程中无法进行类似于其他语言的多线程操作),你无法进行类似于多线程阻塞或者悬停阻挡的效果。(注:alert函数其实在当前浏览器脚本的执行过程中起到了一个阻塞当前线程的效果,但它实在太丑了,而且功能过于有限,没办法起到更多的作用)所以说,如果你的需求是要在一个函数片段中执行远程载入,而且可能由于某种很恶劣的环境无法将后续代码以callback的形式延后执行,那么你就需要将这个操作同步化。如果只是一个很简单的应用,那么异步化是个很好的主意。

同步的JavaScript基本代码:

JavaScript
xmlhttp.open("GET",url,false/*asynchronous*/);

第三个参数指示这个请求是否是异步传输,如果是true便是异步,false便是同步。
如果你使用框架,那么请参阅相关API。
然后拿到这个请求回来的js代码之后便将之执行,也就是万恶的eval。

那么来看一下异步的处理方法吧。

JavaScript
var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = s.url;
head.appendChild(script);

这样的写法还是很干净的,就是在head上面加一个script结点,然后让浏览器来做这么些事。或者你可以在这个script结点的onreadystatechange和onload事件中把这个结点给删了,这样,当前的DOM模型就能保证干净如初。
诚然,你也可以使用同步的同样方法来进行异步加载,但那种用法显然不及这种来得优雅,特别是当你只需要一个lib的时候。

无论是同步抑或是异步,都是采用xmlhttprequest的方式。在同步模式下,应该使用GET方法向服务端获取一个JS或者CSS文件。

另外需要讲的一个问题便是AJAX的cache,显然每次载入的脚本和CSS都需要重新传输并不是一件明智的事情。

浏览器对于文件的cache的依据是浏览器的设定(是否每次都下载等),文件的expire头以及用户的操作(刷新)。浏览器cache的文件是根据请求的URL来作为一个key的,也就是说style.css?time=1234567890和style.css?time=1234567891是两个不同的文件。所以你的脚本和CSS如果没有频繁改动的话,这个时间戳其实并不需要。

总的来说,这是一个很小很小的技巧,对于众多的开发人员来说,甚至不用去加以关心它的实质,框架会帮你做掉这些事情。

Comments (12)

  1. 09:33, 2009-09-11任平生  / Reply

    看了两遍才看明白了,唉现在的理解能力是日趋下降。。。

  2. 09:38, 2009-09-11Chris  / Reply

    这个用jQuery不行吗?

  3. 10:34, 2009-09-11shen  / Reply

    技术贴一定要顶起来!!!

  4. 13:09, 2009-09-11Justice  / Reply

    @任平生
    说实话我也有同感 =.=

    @Chris
    发哥的层次是深入jQuery内部改源码的,和我们不是一个等级的好伐。

  5. 13:18, 2009-09-11Chris  / Reply

    @Justice
    我看不懂发哥写的东西= =

  6. 16:24, 2009-09-11Rui  / Reply

    学习了:)

  7. 02:54, 2009-09-12Sinnyn  / Reply

    @all
    趁着周末的时间边看Federer的比赛边把这个重新组织了下。抱歉给大家的阅读造成麻烦了:P

  8. 05:07, 2009-09-12Chris  / Reply

    @Sinnyn
    我最近在看Flex...其实网页上都用RIA多好...弄JS...只好头痛死一群程序员了...

  9. 12:16, 2009-09-12Sinnyn  / Reply

    @Chris
    Flex这个东西相对来说比较重,在带宽受限的地方Flex就是梦魇了,同样的还有Silverlight。这个东西我以前也弄过,网络优化是个太重要的事情了。

  10. 13:45, 2009-09-12Justice  / Reply

    @Sinnyn
    带宽够的话Flex用起来的确是很爽啊~

  11. 23:12, 2009-12-22Y.Jiajia  / Reply

    如果只是换肤的话,还有一种方法,更简单就是给link上加ID直接用$("link#ID")再用其他属性就可以动态换肤,更实用。:)

  12. 05:17, 2009-12-23Justice  / Reply

    @Y.Jiajia
    你这个已经是用了 jQuery 之后啦~这篇说的还是 raw JavaScript

Leave a Reply

Allowed Tags - You may use these HTML tags and attributes in your comment.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

Pingbacks (1)