不执行预载脚本(Preload script without execute)

2019-06-25 15:02发布

问题

为了提高页面的表现,我需要预装 ,我需要在底部网页上运行 的脚本

我想借此当脚本解析,编译和执行的控制。

我必须避免script标签 ,因为它是阻断共同渲染引擎 (geeko等)。

它使用延迟属性,因为我需要的是执行脚本时要控制,我可以不加载。 此外, 异步属性的可能性也不大。

样品:

<html><head>
//preload scripts ie: a.js  without use the script
</head><body> ..... all my nice html here
//execute here a.js
</body></html>

这使我能够最大限度地呈现在我的网页的性能,因为浏览器将开始donwload脚本内容,它会在同时并行渲染页面。 最后,我可以添加脚本标记,因此浏览器将解析,编译和执行代码。

我能做到这一点的唯一方法是使用一个隐藏的图像标记。 (这是一个简化斯托的版本 )

 <html><head>
 <img src="a.js" style=display:none;>
</head><body> ..... all my nice html here
 <script src="a.js">  
</body></html>

我没有发现使用这种技术的任何问题,但没有人知道一个更好的方式来做到这一点? 是否有任何元预取?

附加信息

我使用requirejs,所以我想预加载模块的代码,而不执行它,因为这段代码取决于DOM元素。

Answer 1:

你应该看看下面的链接:

http://calendar.perfplanet.com/2011/lazy-evaluation-of-commonjs-modules/

http://tomdale.net/2012/01/amd-is-not-the-answer/

而在ember.js如何使用一个工具,称为minispade与红宝石预处理,使加载过程,解析和运行的JavaScript模块快。



Answer 2:

随着类似的技术可能会使用预加载脚本和样式表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);
  }
}

有一个博客帖子描述了这样的技术,并概述警告: 不执行预加载CSS / JavaScript的 。

但是,为什么你不希望只使用动态添加脚本仿如建议在其他的答案,这或许将导致更多的控制一个清晰的解决方案。



Answer 3:

您可以使用prefetch的链接标签的属性来预加载任何资源,包括JavaScript的。 在撰写本文时(2016年8月10日),它不是在Safari浏览器的支持,但几乎其他任何地方:

<link rel="prefetch" href="(url)">

上支持更多资讯: http://caniuse.com/#search=prefetch

需要注意的是IE 9,10未在上市caniuse矩阵,因为微软已经停止对他们的支持。

更多资讯 ,并为预加载更多的选择,如预渲染和更



Answer 4:

对于每一个脚本,你想下载而不执行,使包含名称和URL的对象,并把这些对象到一个数组。

循环遍历数组,使用jQuery.ajaxdataType: "text"下载脚本为文本。 在done Ajax调用的处理程序,将文件存储在适当的对象(在第一个参数传递)的文本内容,增加一个计数器,并称之为一个“alldone”功能,当该计数器等于文件数您正在下载这种方式。

在“alldone”功能(或更高版本)执行以下操作:依次通过您的阵列再次,对于每个条目,使用document.createElement("script") document.createTextNode(...)(...scriptNode...).appendChild(...)来动态产生具有,而不是通过“src”属性的预期源内嵌脚本。 最后,做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>

其他问题密切相关,上述方法等相关问题



Answer 5:

为什么不试试呢?

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另外添加脚本是非阻塞的,我相信你可以完美实现这种方法你的目标。



Answer 6:

我已经回答有同样的问题:

https://stackoverflow.com/a/46121439/1951947

只是使用<link>标签来预加载您的脚本,然后你可以用用它<script>标记

例如: <link href="/js/script-to-preload.js" rel="preload" as="script">



文章来源: Preload script without execute