为了提高页面效果我需要预加载脚本,我需要在底部页面上运行。
我想控制何时解析,编译和执行脚本。
我必须避免脚本标记,因为它是blocker for common render engines(geeko等)。
我无法使用 defer 属性加载它,因为我需要控制脚本执行的时间。 此外, async 属性不可能。
示例:
<html><head>
//preload scripts ie: a.js without use the script
</head><body> ..... all my nice html here
//execute here a.js
</body></html>
这使我能够最大化我的页面的渲染性能,因为浏览器将开始下载脚本内容,并且它将同时呈现页面并行。最后,我可以添加脚本标记,以便浏览器解析,编译和执行代码。
我能做到的唯一方法就是使用隐藏的图片标签。 (这是简化的version of Stoyan)
即
<html><head>
<img src="a.js" style=display:none;>
</head><body> ..... all my nice html here
<script src="a.js">
</body></html>
我没有发现使用这种技术有任何问题,但有没有人知道更好的方法来做到这一点? 有没有meta预取?
我正在使用 requirejs ,因此我正在尝试预加载模块代码而不执行它,因为此代码依赖于DOM元素。
答案 0 :(得分:6)
使用类似的技术,您可以使用img
为Internet Explorer预加载脚本和样式表,并为每个其他浏览器使用object
标记。
var isMSIE = /*@cc_on!@*/false;
var resources = ['a.js', 'b.js', 'c.css'];
for (var i=0; i<resources.length; i++){
if (isMSIE){
new Image().src = resources[i];
} else {
var o = document.createElement('object');
o.data = resources[i];
document.body.appendChild(o);
}
}
有一篇博客文章介绍了这种技巧并概述了警告:Preload CSS/JavaScript without execution。
但是为什么你不想像其他答案中建议的那样只使用动态添加的脚本,这可能会带来更清晰的解决方案和更多的控制。
答案 1 :(得分:4)
您可以使用链接标记的prefetch
属性来预加载任何资源,包括javascript。在撰写本文时(2016年8月10日),它在Safari中不受支持,但在其他任何地方都是如此:
<link rel="prefetch" href="(url)">
有关支持的更多信息: http://caniuse.com/#search=prefetch
请注意,IE 9,10未列在caniuse
矩阵中,因为Microsoft已停止对它们的支持。
More info here和more options for preloading, like prerender and more
答案 2 :(得分:1)
对于您想要在不执行的情况下下载的每个脚本,创建一个包含名称和URL的对象,并将这些对象放入数组中。
循环遍历数组,使用jQuery.ajax
和dataType: "text"
将脚本下载为文本。在ajax调用的done
处理程序中,将文件的文本内容(在第一个参数中传递)存储在适当的对象中,递增计数器,并调用&#34; alldone&#34;当该计数器等于您以这种方式下载的文件数时起作用。
在&#34; alldone&#34;函数(或更高版本)执行以下操作:再次循环遍历数组,对于每个条目,使用document.createElement("script")
,document.createTextNode(...)
和(...scriptNode...).appendChild(...)
动态生成具有预期源内联的脚本,而不是而不是通过&#34; src&#34;属性。最后,执行document.head.appendChild(...scriptNode...)
,这是该脚本执行时的重点。
我在一个需要使用框架的项目中使用了这种技术,其中几个框架和/或框架集需要相同的JavaScript文件,以确保每个文件只从服务器请求一次。
代码(测试和工作)如下
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<script id="scriptData">
var scriptData = [
{ name: "foo" , url: "path/to/foo" },
{ name: "bar" , url: "path/to/bar" }
];
</script>
<script id="scriptLoader">
var LOADER = {
loadedCount: 0,
toBeLoadedCount: 0,
load_jQuery: function (){
var jqNode = document.createElement("script");
jqNode.setAttribute("src", "/path/to/jquery");
jqNode.setAttribute("onload", "LOADER.loadScripts();");
jqNode.setAttribute("id", "jquery");
document.head.appendChild(jqNode);
},
loadScripts: function (){
var scriptDataLookup = this.scriptDataLookup = {};
var scriptNodes = this.scriptNodes = {};
var scriptNodesArr = this.scriptNodesArr = [];
for (var j=0; j<scriptData.length; j++){
var theEntry = scriptData[j];
scriptDataLookup[theEntry.name] = theEntry;
}
//console.log(JSON.stringify(scriptDataLookup, null, 4));
for (var i=0; i<scriptData.length; i++){
var entry = scriptData[i];
var name = entry.name;
var theURL = entry.url;
this.toBeLoadedCount++;
var node = document.createElement("script");
node.setAttribute("id", name);
scriptNodes[name] = node;
scriptNodesArr.push(node);
jQuery.ajax({
method : "GET",
url : theURL,
dataType : "text"
}).done(this.makeHandler(name, node)).fail(this.makeFailHandler(name, node));
}
},
makeFailHandler: function(name, node){
var THIS = this;
return function(xhr, errorName, errorMessage){
console.log(name, "FAIL");
console.log(xhr);
console.log(errorName);
console.log(errorMessage);
debugger;
}
},
makeHandler: function(name, node){
var THIS = this;
return function (fileContents, status, xhr){
THIS.loadedCount++;
//console.log("loaded", name, "content length", fileContents.length, "status", status);
//console.log("loaded:", THIS.loadedCount, "/", THIS.toBeLoadedCount);
THIS.scriptDataLookup[name].fileContents = fileContents;
if (THIS.loadedCount >= THIS.toBeLoadedCount){
THIS.allScriptsLoaded();
}
}
},
allScriptsLoaded: function(){
for (var i=0; i<this.scriptNodesArr.length; i++){
var scriptNode = this.scriptNodesArr[i];
var name = scriptNode.id;
var data = this.scriptDataLookup[name];
var fileContents = data.fileContents;
var textNode = document.createTextNode(fileContents);
scriptNode.appendChild(textNode);
document.head.appendChild(scriptNode); // execution is here
//console.log(scriptNode);
}
// call code to make the frames here
}
};
</script>
</head>
<frameset rows="200pixels,*" onload="LOADER.load_jQuery();">
<frame src="about:blank"></frame>
<frame src="about:blank"></frame>
</frameset>
</html>
other question与上述方法密切相关 other related question
答案 3 :(得分:0)
为什么不尝试这个?
var script = document.createElement('script');
script.src = 'http://path/to/your/script.js';
script.onload = function() {
// do something here
}
document.head.appendChild(script);
您可以使用.onload事件来控制何时加载。需要注意的是.onload()在IE中不起作用,你可以使用它:
script.onreadystatechange = function() {
if (/^loaded|complete$/i.test(this.readyState)) {
// loaded
};
}
此外,通过DOM添加脚本是非阻塞的,我相信你可以用这种方法完美地实现你的目标。
答案 4 :(得分:0)
您应该查看以下链接:
http://calendar.perfplanet.com/2011/lazy-evaluation-of-commonjs-modules/
http://tomdale.net/2012/01/amd-is-not-the-answer/
以及ember.js如何使用一个名为minispade的工具并使用ruby进行预处理来快速加载,解析和运行javascript模块。
答案 5 :(得分:0)
我在那里回答了同样的问题:
https://stackoverflow.com/a/46121439/1951947
只需使用<link>
标记预加载您的脚本,然后您就可以将其与<script>
标记一起使用
例如:<link href="/js/script-to-preload.js" rel="preload" as="script">