On my web site,我正在努力尽可能地完成最快的页面加载。
我注意到我的JavaScript似乎没有异步加载。图片链接如下。
alt text http://img249.imageshack.us/img249/2452/jsasynch2.png
我的网站如何运作是需要加载两个外部JavaScript文件:
然后,我在HTML中使用内联JavaScript,在下载上述两个文件之前无法执行。
一旦加载了这些外部javascript文件,它就会,然后才能动态呈现页面。在加载Google Maps和JQuery之前我的页面无法加载的原因是 - 我的页面基于用户的地理位置(使用Gmaps)将根据它们所在的位置显示页面(例如纽约,旧金山等)。这意味着,观看我网站的不同城市的两个人将看到不同的前页。
问题:如何让我的JavaScript文件异步下载,以便我的整个页面加载时间最快?
更新:
如果我以某种方式异步下载谷歌地图和JQuery,我将如何创建一个事件,一旦 Google-maps和JQuery已经下载,就会被解雇,因为我的页面很难依赖于那些要执行的文件。
更新2
即使下面有3个答案,但实际上没有人能回答我的问题。任何帮助将不胜感激。
答案 0 :(得分:7)
HTTP下载通常受到浏览器的限制,每个域每次同时下载。这就是某些网站在www.domain.tla
和the images and javascript on static.domain.tla
上拥有动态内容的原因。
标准解决方案是将脚本移动到页面底部,但有一种解决方法可能适用于您,也可能不适合您:Insert the script DOM element using Javascript。
答案 1 :(得分:4)
你可以使用这样的东西,它在大多数浏览器中运行得很好。它至少在IE6中存在一些问题,但我没有时间对它们进行调查。
var require = function (scripts, loadCallback) {
var length = scripts.length;
var first = document.getElementsByTagName("script")[0];
var parentNode = first.parentNode;
var loadedScripts = 0;
var script;
for (var i=0; i<length; i++) {
script = document.createElement("script");
script.async = true;
script.type = "text/javascript";
script.src = scripts[i];
script.onload = function () {
loadedScripts++;
if (loadedScripts === length) {
loadCallback();
}
};
script.onreadystatechange = function () {
if (script.readyState === "complete") {
loadedScripts++;
if (loadedScripts === length) {
loadCallback();
}
}
};
parentNode.insertBefore(script, first);
}
};
require([
"http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
"http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js",
"http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/yuiloader/yuiloader-min.js"
], function () {
console.log(jQuery);
console.log($);
console.log(YAHOO);
});
答案 2 :(得分:4)
有人让我对这个帖子发表评论,但那是在@lonut发布回复之前。 @ lonut的代码是一个非常好的解决方案,但我有一些评论(关键且不那么关键):
首先,@ lonut的代码假定脚本没有其他脚本的加载依赖性。这有点难以解释,所以让我们使用jquery.min.js和prototype.js的简单示例。假设我们有一个简单的页面,只需加载这两个脚本:
<script src="jquery.min.js"></script>
<script src="prototype.js"></script>
请记住 - 页面中没有其他内容 - 没有其他JavaScript代码。如果您加载该页面,则下载两个脚本,一切都很好。现在,如果删除jquery.min.js脚本会发生什么?如果你从prototype.js得到错误,因为它试图引用jquery.min.js中定义的符号,那么prototype.js对jquery.min.js有一个加载依赖 - 你不能加载prototype.js,除非jquery.min.js有已经装好了。但是,如果您没有收到任何错误,则可以按照您希望的任何顺序加载这两个脚本。假设您的外部脚本之间没有负载依赖关系,@ lonut的代码非常棒。如果你确实有负载依赖 - 它会非常困难,你应该阅读更快的网站中的第4章。
第二,@ lonut代码的一个问题是Opera的某些版本将调用loadCallback两次(一次来自onload处理程序,另一次来自onreadystatechange处理程序)。只需添加一个标志,以确保只调用一次loadCallback。
第三,今天大多数浏览器每个主机名打开2个以上的连接。请参阅Roundup on Parallel Connections。
答案 3 :(得分:2)
LABjs动态脚本加载器专门针对此类案例而设计。例如,你可以这样做:
$LAB
.script("googlemaps.js")
.script("jquery.js")
.wait(function(){
// yay, both googlemaps and jquery have been loaded, so do something!
});
如果情况稍微复杂一点,并且你有一些相互依赖的脚本,正如史蒂夫·索德斯所提到的那样,你可能会这样做:
$LAB
.script("jquery.js")
.wait() // make sure jquery is executed first
.script("plugin.jquery.js")
.script("googlemaps.js")
.wait(function(){
// all scripts are ready to go!
});
在任何一种情况下,LABjs将下载所有脚本(“jquery.js”,“googlemaps.js”和“plugin.jquery.js”)并行,至少到浏览器允许的程度。但是通过明智地使用链中的.wait(),LABjs将确保它们以正确的顺序执行。也就是说,如果链中的两个脚本之间没有.wait(),它们将分别执行ASAP(意味着tehm之间的不确定顺序)。如果链中的两个脚本之间存在.wait(),则第一个脚本将在第二个脚本之前执行,即使它们是并行加载的。
答案 4 :(得分:2)
以下是我如何设法在jquery mobile上异步加载gmaps: 首先,您可以加载jquery(即上面由IonuţG。Stan发布的 require 函数) 然后,您可以使用gmaps中的回调参数执行以下操作:
<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
var require = function (scripts, loadCallback) {
var length = scripts.length;
var first = document.getElementsByTagName("script")[0];
var parentNode = first.parentNode;
var loadedScripts = 0;
var script;
for (var i=0; i<length; i++) {
script = document.createElement("script");
script.async = true;
script.type = "text/javascript";
script.src = scripts[i];
script.onload = function () {
loadedScripts++;
if (loadedScripts === length) {
loadCallback();
}
};
script.onreadystatechange = function () {
if (script.readyState === "complete") {
loadedScripts++;
if (loadedScripts === length) {
loadCallback();
}
}
};
parentNode.insertBefore(script, first);
}
};
require([
"http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",], function () {
$.ajax({
type: "GET",
url: 'http://maps.googleapis.com/maps/api/js?v=3&sensor=false&callback=setMyMap',
dataType: "script"
});
});
function setMyMap() {
console.log('your actions here');
var coords = new google.maps.LatLng(40.5439532,-3.6441775);
var mOptions = {
zoom: 8,
center: coords,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("gmap"), mOptions);
}
</script>
<div id="gmap" style="width:299px; height:299px"></div>
</body>
重点是加载jquery async(你选择哪种方法),并在该回调上使用你的启动方法在gmaps脚本字符串的回调参数中对gmaps进行新的异步调用。
希望有所帮助
答案 5 :(得分:1)
无论他们下载的顺序是什么,脚本都应按照它们在页面上出现的顺序解析/执行(除非你使用DEFER)。
因此,您可以将两个Google地图放在首位,即THEN JQuery。然后,在你的页面正文的某个地方:
<script language="Javascript">
function InitPage() {
// Do stuff that relies on JQuery and Google, since this script should
// not execute until both have already loaded.
}
$(InitPage); // this won't execute until JQuery is ready
</script>
但这样做的好处是在加载页面开头时阻止其他连接,这对于页面性能来说并不是那么棒。
相反,您可以将JQuery保留在HEAD中,但使用JQuery的Javascript加载功能而不是Google JSAPI从 InitPage()函数加载Google脚本。然后在执行 回调函数时开始渲染。与上面相同,但改为使用此InitPage()函数:
function InitPage() {
$.getScript('Google Maps Javascript URL', function() {
// Safe to start rendering now
});
答案 6 :(得分:1)
将您的javascript包含(<script src="...
)从HEAD
元素移至BODY
元素的末尾。通常,HEAD中放置的任何内容都是同步加载的,BODY
中放置的任何内容都是异步加载的。对于脚本包含,这或多或少都是正确的,但是现在大多数浏览器会阻止脚本下面的所有内容,直到它被加载为止 - 因此,为什么在脚本底部包含脚本是最佳实践。
这是YUI guildline,它更详细地解释了它: http://developer.yahoo.net/blog/archives/2007/07/high_performanc_5.html
这也是为什么样式表应该在头部,并且javascript应该在正文中的原因。因为我们不希望看到我们的页面从意大利面转向漂亮,而样式是异步加载的,我们不希望在页面加载时等待我们的javascript。
答案 7 :(得分:0)
您想到的目标将通过使用requireJS来实现。 RequireJS异步下载js资源。它是一个非常简单实用的库。请在这里阅读更多。 http://requirejs.org/