1、浏览器在遇到<body>标签之前,不会渲染页面的任何部分。
如果将<script>标签放在<body>,这种方法把脚本放在页面的顶端,将导致一个可以察觉的延迟,通常表现为:页面打开时,首先显示为一幅空白的页面,而此时用户即不能阅读,也不能与页面进行交互操作。
推荐:将所有的<script>标签放在尽可能接近<body>标签底部的位置。
2、由于每个<script>标签下载时阻塞页面解析过程,所以限制页面的<script>总数也可以改善性能。
非阻塞脚本:
等页面加载完成后,再加载Javascript源码,意思是:在window.onload事件发生后才开始下载源码。
方法1:延期脚本
<script type="text/javascript" src="fileUpload.js"defer></script>
但是只在IE 4+ 和 FF 3.5+才支持。
方法2:动态脚本元素
文档对象模型(DOM)允许使用javascript动态创建HTML的几乎全部文档内容。因为<script>元素与页面中的其它元素没有什么不同。
所以一个<script>可以很容易通过标准DOM创建:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/
xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>动态脚本元素</title>
<style type="text/css">
*{margin:0;padding:0;font-family:微软雅黑}
span{position:absolute;left:0;top:0}
</style>
</head>
<body>
<span>测试</span>
</body>
<script type="text/javascript">
window.onload = function() {
var jQueryScript = document.createElement("script");
jQueryScript.type = "text/javascript";
jQueryScript.src = "jquery-1.9.1.min.js";
document.getElementsByTagName("head")[0].appendChild(jQueryScript);
//不能随即调用jQuery中的方法,我们不知道什么时候能加载完成
/**IE中可以调用onreadystatechange
*<script>有个readyState属性,它随着外部文件的加载过程而改变
*1、"uninitialized" :默认状态
*2、"loading" :下载开始
*3、"loaded" :下载完
*4、"interactive" :下载完但是不可用
*5、"complete" :所有数据已经准备好
*由于<script>加载的过程中,这些状态不一定会都出现,我们只需关系loaded和complete两种状态
*考虑到可能不会同时出现,用||即可,为了不让事件重复绑定,当出现其中一种状态时清空readystatechange事件
*************************************/
jQueryScript.onreadystatechange = function(){
if(jQueryScript.readyState == "loaded" || jQueryScript.readyState == "complete") {
jQueryScript.onreadystatechange = null;
callback();
}
}
//非IE
jQueryScript.onload = function(){
callback();
}
};
//当加载完成后就可以调用jQuery中的方法了
function callback() {
jQuery(document).ready(function(){
$("span").animate({"left":"300px","top":"500px"},5000);
});
}
</script>
</html>
考虑通用性,可以稍作整理:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/
xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>动态脚本元素</title>
<style type="text/css">
*{margin:0;padding:0;font-family:微软雅黑}
span{position:absolute;left:0;top:0}
</style>
</head>
<body>
<span>测试</span>
</body>
<script type="text/javascript">
LoadScript = (function(doc){
var position = doc.getElementsByTagName("body")[0],
script = doc.createElement("script"),
script.type = "text/javascript";
//加载js
function load(url,callback) {
if(typeof url != "string" || url == null || url == '') {
alert("加载失败...");
return ;
}
if(script.readyState) {
script.onreadystatechange = function(){
if(script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else {
script.onload = function(){
callback();
};
}
script.src = url;
position.appendChild(script);
}
return {
js:function(url,callback){
load(url,callback);
}
};
})(this.document);
LoadScript.js("jquery-1.9.1.min.js",function(){
$("span").animate({"left":"300px","top":"500px"},5000);
});
</script>
</html>
完整写法:参考loadScript.js
调用如:
<script type="text/javascript">
Require.js(['js/jquery.anchor-1.0.0.js'],function(){jQuery.anchor({layer:$(".yitian-layer")});});
Require.js(['js/yitian.footer-1.0.0.js'],function(){Yitian.footer.loader({start:2012});});
</script>
可以传入多个参数:
/**
* javascript 样式,js文件导入
* @author:xuzengqiang
* @since :2015-3-12 21:36:08
* 调用方式:
* Require.css('index.css',function(){
* //加载完成后的回调函数
* });
* Require.js(['js/jquery.anchor-1.0.0.js','js/common.js'],function(){});
**/
var Require = (function(window){
var document = window.document,
agent = window.navigator.userAgent.toLowerCase(),
queue = { //任务队列
css : [],
js : []
},
head = document.head || document.getElementsByTagName("head")[0],
//目前正在进行的请求
request = {},
//轮询总数
pollCount = 0;
//全局变量
window.DOMUtils = {
//描述:创建一个元素,name:元素名称,attrs:属性集合{id:'content',class:'content'};
createElement:function(name,attrs){
var element = document.createElement(name);
for(var attr in attrs) {
element.setAttribute(attr,attrs[attr]);
}
return element;
}
};
//浏览器信息
window.Browser = {
agent: agent,
isIE : /mise|trident/.test(agent),
//Gecko内核
isGecko : agent.indexOf("gecko") > 0 && agent.indexOf("like gecko") < 0,
//webkit内核
isWebkit : agent.indexOf("webkit") > 0
};
var Load = {
run : function(type,urls,callback) {
var isCSS = (type === "css"), //加载类型,只考虑js和css
node,current; //节点
if(urls) {
//concat()用于连接数组,但是不会改变现有数组,只是返回被连接数组的一个副本
urls = typeof urls === 'string' ? [urls] : urls.concat();
//除了Firefox和Opera浏览器能够保证并行加载且能保留顺序执行,所以统一按顺序将任务加入到任务队列中
for(var i=0,maxLen = urls.length;i<maxLen;i++) {
queue[type].push({
url : urls[i],
callback : (i === maxLen-1)? callback : null //当有多个等所有资源加载完成后才调用callback
});
}
}
//如果资源加载完成
if (request.type || !(current = request.type = queue[type].shift())) {
return false;
}
if(isCSS) {
//火狐下不支持link标签的onload事件.所以创建style标签,通过@import引入样式
node = Browser.isGecko?DOMUtils.createElement("style"):DOMUtils.createElement("link",{
type:'text/css',rel:'stylesheet',href:current.url});
} else {
node = DOMUtils.createElement("script",{type:'text/javascript',src:current.url});
//同步加载,防止顺序错乱
node.async = false;
}
node.className="require";
node.setAttribute("charset","utf-8");
//IE浏览器且不是CSS文件
if(Browser.isIE && !isCSS && node.onreadystatechange) {
node.onreadystatechange = function(){
if(/loaded|complete/.test(node.readyState)) {
node.onreadystatechange = null;
Load.complete(type);
}
};
//webkit内核和gecko内核不支持link节点的onload方法,document.getElementsByTagName("link")[0].onload
} else if(isCSS && (Browser.isWebkit || Browser.isGecko)) {
if (Browser.isWebkit) {
//current.url = node.href; // 解决相对url(或轮询不会工作)
Load.pollWebkit(type);
} else {
node.innerHTML = '@import "' + current.url + '";';
Load.pollGecko(node);
}
} else {
node.onload = node.onerror = function(){
Load.complete(type);
};
}
head.appendChild(node);
},
complete:function(type){
var req = request.type,callback;
//如果有请求
if(req) {
callback = req.callback;
pollCount = 0;
// 如果为最后一个待加载的URL 执行回调并且开始下一个队列中的请求(如果有)
if (req.url !== null) {
if(typeof callback !== 'undefined' && callback !== null) {
callback();
}
request.type = null;
queue[type].length && Load.run(type);// 队列中如还有当前类型的资源 加载该类型资源
}
}
},
//轮询判断样式是否已经加载完成,样式加载完成后或10s后停止轮询
pollWebkit:function(type){
var css = request.type, len, styleSheets = document.styleSheets;
if (css !== null) {
len = styleSheets.length;
//遍历styleSheets判断是否加载完毕
while (--len >= 0) {
if (styleSheets[len].href === css.url) {
Load.complete('css');
break;
}
}
pollCount += 1;
if (css !== null) {
if (pollCount < 200) {
setTimeout(function(){Load.pollWebkit(type);}, 50);
} else {
Load.complete('css');
}
}
}
},
//内核为Gecko的浏览器轮询
pollGecko:function(node){
var hasRules; //是否已经加载完成
try {
hasRules = !!node.sheet.cssRules;
} catch (ex) {
// 样式表仍然是在加载...那么轮询10秒,超过10s,退出
pollCount += 1;
if (pollCount < 200) {
setTimeout(function () { Load.pollGecko(node); }, 50);
} else {
hasRules && Load.complete('css');
}
return false;
}
Load.complete('css');
}
};
return {
css:function(urls,callback) {
return Load.run("css",urls,callback);
},
js:function(urls,callback) {
return Load.run("js",urls,callback);
}
}
})(window);