使用“动态内联”的方式加载javascript、CSS
“动态内联”这个词是出自“高性能网站建设指南”中第八章“规则8—使用外部Javascript和CSS”,它的原理是基于权衡页面中内联Javascript、CSS和外联Javascript、CSS时的比较。“纯粹而言,内联快一些”,因为外联的时候需要向服务器发送HTTP请求,这需要更多的加载时间,而相对于内联的方式,页面的总大小(包括HTML内容、js、css等等)的一定的,直接下载的方式当然会更快。但是,从“可缓存”、“组件重用”等方面来考虑,外联的方式更占有优势,可以更快的加载页面。
所以,这里就存在一个权衡点:到底是内联,还是外联?但是,有没有可能,让内联的优势和外联的优势结合起来呢?
第一种方案就是书中提出的“加载后加载”,意思就是当可以预测到用户将会接下来访问什么页面的时候,可以通过当前页面的onload事件,来加载后续的组件,这样当用户在访问接下来的页面的时候,可以从缓存中获取组件,使得加载速度提升。这种方式是比较普遍的了。它的主要应用在于首页中,比如(Google,百度,Yahoo等等)。
第二种方案就是标题说的“动态内联”。如果服务器知道一个组件是否在浏览器的缓存中,它就可以在内联或者使用外联外部文件之间作出最佳的选择。服务器当然不可能直接查看浏览器的缓存,不过可以通过cookie的方式来检测是否缓存了组件。如果指定的cookie不存在,说明浏览器没有缓存相应的组件,使用内联的方式加载组件;当cookie存在,则使用外联的方式加载组件,从缓存中读取组件。
理论说起来容易,实践起来还是需要技巧的,而基本原理就是:使用Javascript设置cookie,服务器端的语言(php,jsp,asp.net等等)来检测cookie并读取文件,例如PHP中使用file_get_contents来读取文件。下面提供一个简单的例子(测试页面):
<?php
//检查cookie是否存在,存在则采用外联的方式,否则采用内联的方式
if($_COOKIE["CSS"]){
echo '<link rel="stylesheet" href="test.css" />';
}else{
echo "<style>".file_get_contents("test.css")."</style>";
}
?>
<script>
"use strict"; //Don't cancel
//首先检测是否设置了cookie,如果没有则设置,并通过iframe的方式先缓存组件
//在test.html中加载了test.css文件,先在浏览器缓存起来。
if(!(/CSS/.test(document.cookie))){
document.cookie="CSS=1";
var iframe=document.createElement("iframe");
iframe.src="test.html";
iframe.width="0";
iframe.height="0";
document.body.appendChild(iframe);
}
</script>
如上代码所示,在页面第一次访问的时候,此时cookie还没有设置,采用内联的方式来加载组件,当刷新页面或者浏览其他页面的时候,会检测cookie并从缓存中读取test.css组件。这样就结合使用了内联和外联的优势,对页面加载速度起到了一定的优化效果。对于“动态内联”更多的描述,书中提到:
这种方式的美好之处在于它的宽容,即便cookie的状态和缓存不匹配,页面也能够工作,只是没有本应该的那么优化而已。基于会话的cookie技术在内联时会发生错误,即便组件已经被放到浏览器缓存中了——如果用户重新打开浏览器,基于会话的cookie会消失,但组件依然存在于缓存中。将cookie从基于会话的改为短期的(数小时或数天)可以解决这个问题,但当它们并不在浏览器缓存中时,使用外部文件又会出错。不管出现哪种情况,页面都能够正常工作。
对于上面的描述,我存在一个问题:当cookie存在,而缓存不存在的时候,就over了。除非在每一个页面的js中都加载一个iframe来缓存组件,比如修改上面的js代码如下:
document.cookie="CSS=1";
var iframe=document.createElement("iframe");
iframe.src="test.html";
iframe.width="0";
iframe.height="0";
document.body.appendChild(iframe);
上面的方式就是不检测cookie是否存在,而是每个页面每次浏览的时候都动态生成一个iframe来缓存文件。这确实没有必要的。
