【问题标题】:error loading javascript files dynamically动态加载 javascript 文件时出错
【发布时间】:2015-07-15 09:25:54
【问题描述】:

我正在尝试在我的网页中延迟(动态)加载 javascript 文件。

我用过这个:

http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml

   function loadjscssfile(filename, filetype){
    if (filetype=="js"){ //if filename is a external JavaScript file
        var fileref=document.createElement('script')
        fileref.setAttribute("type","text/javascript")
        fileref.setAttribute("src", filename)
    }
    else if (filetype=="css"){ //if filename is an external CSS file
        var fileref=document.createElement("link")
        fileref.setAttribute("rel", "stylesheet")
        fileref.setAttribute("type", "text/css")
        fileref.setAttribute("href", filename)
    }
    if (typeof fileref!="undefined")
        document.getElementsByTagName("head")[0].appendChild(fileref)
}

loadjscssfile("myscript1.js", "js") //dynamically load and add this .js file
loadjscssfile("myscript2.js", "js") //dynamically load and add this .js file
loadjscssfile("myscript3.js", "js") //dynamically load and add this .js file

这 3 个脚本将按顺序加载。

myscript3 依赖于 myscript2
并且 myscript2 依赖于 myscript1。

尝试以这种方式加载它的行为很奇怪。

看起来这些未按预期顺序加载,因此 有时会抛出未定义的错误,而有时不会抛出错误

我是不是做错了什么。


更新:

我正在使用此代码以正确的顺序加载文件

 function loadjscssfile(filename, filetype) {
        if (filetype == "js") { //if filename is a external JavaScript file
            var fileref = document.createElement('script')
            fileref.setAttribute("type", "text/javascript")
            fileref.setAttribute("src", filename)
        }
        else if (filetype == "css") { //if filename is an external CSS file
            var fileref = document.createElement("link")
            fileref.setAttribute("rel", "stylesheet")
            fileref.setAttribute("type", "text/css")
            fileref.setAttribute("href", filename)
        }
        return fileref;
    }

    function loadSync(files, types) {
        if (typeof (files[0]) === 'undefined') return false;
        var script = loadjscssfile(files[0], types[0]);
        script.onload = function () {
            files.shift();
            types.shift();
            loadSync(files, types);
        }
        if (typeof script != "undefined")
            document.getElementsByTagName("head")[0].appendChild(script)
    }

    loadSync(['scripts/module3.base.js', 'scripts/module3.core.js', 'scripts/module3.init.js'], ['js', 'js', 'js']);
    console.log('this should get printed when all the three files are loaded');


但是控制台输出是:

this should get printed when all the three files are loaded
scripts/module3.base.js:9 base is initialised
scripts/module3.core.js:6 core is initialised
scripts/module3.init.js:3 app is initialsed

看起来第一次调用 loadSync 本身就是一个异步调用

【问题讨论】:

    标签: javascript dynamic load lazy-loading


    【解决方案1】:

    对于 HTML5 只需使用 async 属性(http://davidwalsh.name/html5-async 处的信息)

    使用可选回调选项加载脚本的函数示例

    function loadScript(url, callback) {
        if (!callback) callback = function () {};
        var script = document.createElement("script")
        script.type = "text/javascript";
        script.setAttribute("async", "true");
    
        if (script.readyState){  //IE
            script.onreadystatechange = function(){
                if (script.readyState == "loaded" ||
                        script.readyState == "complete"){
                    script.onreadystatechange = null;
                    callback();
                }
            };
        } else {  //Others
            script.onload = function(){
                callback();
            };
        }
    
        script.src = url;
        document.getElementsByTagName("head")[0].appendChild(script);
    }
    

    对于混合溶液:

    function loadjscssfile(url, type, callback) {
        if (!type) {
            type = "js";
        }
        var node;
        if (type === "js") {
            var node = document.createElement("script")
            node.setAttribute("async", "true");
            node.type = "text/javascript";
    
            if (node.readyState){  //IE
                node.onreadystatechange = function(){
                    if (node.readyState == "loaded" ||
                            node.readyState == "complete"){
                        node.onreadystatechange = null;
                        callback();
                    }
                };
            } else {  //Others
                node.onload = function(){
                    callback();
                };
            }
    
            node.src = url;
        } else if (type === "css") {
            var node = document.createElement("link")
            node.type = "text/css";
            node.setAttribute("rel", "stylesheet");
            node.setAttribute("async", "true");
    
            if (node.readyState){  //IE
                node.onreadystatechange = function(){
                    if (node.readyState == "loaded" ||
                            node.readyState == "complete"){
                        node.onreadystatechange = null;
                        callback();
                    }
                };
            } else {  //Others
                node.onload = function(){
                    callback();
                };
            }
            node.href = url;
        }
        if (node) {
            document.getElementsByTagName("head")[0].appendChild(node);
        } else {
            callback(new Error("File not found"));
        }
    }
    

    加载文件列表

    function loadjscssfileList(arr_original, callback) {
        var arr = [];
        arr_original.forEach(function (v) {
            arr.push(v);
        });
        var next = function () {
            if (arr.length) {
                var file    = arr.shift();
                loadjscssfile(file[0], file[1], function () {
                    next();
                });
            } else {
                callback();
            }
        };
        next();
    };
    
    
    // use without callback
    loadjscssfileList([
        ["myscript.js","js"],
        ["style.css","css"],
        ["myscript2.js","js"]
    ]);
    
    // use with callback
    loadjscssfileList([
        ["myscript.js","js"],
        ["style.css","css"],
        ["myscript2.js","js"]
    ], function () {
        alert("loaded")
    });
    

    【讨论】:

    • 我不能使用回调模型,因为这需要我更改现有的代码库(javascript 小部件、模块等)以适应回调地狱模型。
    • 我不打算异步加载它们。想法是加载它们,就好像它们是在 html 页面的 部分中以所需的顺序编写的一样
    • 从加载的文件中执行脚本,因为它是在 head write 中写入的:loadjscssfile("myscript.js","js"); myscript.js 的内容 (function (window) { // 代码在这里 }).apply(window);
    • 嗯,目的是按特定顺序(即同步)加载(比如说)10个javascript文件,这样依赖链就不会被破坏(不使用回调地狱)
    【解决方案2】:

    您可以同步加载它们。

    function get_contents(file){
       var xhr = new XMLHttpRequest();
       xhr.open('get', file, false);
       xhr.send();
       return xhr.responseText;
     }
    
    function loadjscssfile(filename, filetype){
    var content = get_contents(filename);
    if (filetype=="js"){ //if filename is a external JavaScript file
        var fileref=document.createElement('script');
        fileref.setAttribute("type","text/javascript");
        fileref.innerHTML = content;
    }
    else if (filetype=="css"){ //if filename is an external CSS file
        var fileref=document.createElement("link")
        fileref.setAttribute("rel", "stylesheet")
        fileref.setAttribute("type", "text/css")
        fileref.innerHTML = content;
    }
    if (typeof fileref!="undefined")
        document.getElementsByTagName("head")[0].appendChild(fileref)
    }
    

    外部加载:

    有两种方法:

    回调作为另一个答案。

    或者你可以这样做:

    function loadjscssfile(filename, filetype){
        if (filetype=="js"){ //if filename is a external JavaScript file
            var fileref=document.createElement('script')
            fileref.setAttribute("type","text/javascript")
            fileref.setAttribute("src", filename)
        }
        else if (filetype=="css"){ //if filename is an external CSS file
            var fileref=document.createElement("link")
            fileref.setAttribute("rel", "stylesheet")
            fileref.setAttribute("type", "text/css")
            fileref.setAttribute("href", filename)
        }
        return fileref;
    }
    
    function loadSync(files,types){
        if(files[0] === 'undefined')return false; //if there's no files to load
        var script = loadjscssfile(files[0],types[0]); //load first file
        script.onload = function(){//after the script is loaded delete them and continue;
            files.shift();
            types.shift();
            loadSync(files,types);
        }
        if (typeof script !="undefined")
            document.getElementsByTagName("head")[0].appendChild(script)
    }
    
    loadSync(["myscript1.js","myscript2.js","myscript3.js"],['js','js','js']);
    

    【讨论】:

    • 我能够以正确的顺序加载这样的文件。但现在我在 chrome 开发者工具的 Sources 部分看不到它们(很自然,现在我们正在附加标记)
    • 那是因为现在您将文件的源添加到内联脚本标记中,因此脚本不是外部的,因为它没有 src
    • 我不想要那个。我仍然希望能够在资源部分看到它们。以便它们保留在单独的文件中(应该如此)
    • 我能够以正确的顺序加载文件,但在 chrome 开发人员工具的资源选项卡中查看它们时遇到问题。 如果我尝试加载的 javascript 文件仅包含一个具有某些值的对象(即没有函数语句),并且如果 1)chrome 开发人员工具已打开或 2)我正在加载的 js 文件包含任何 function(){} 然后我可以在“资源”选项卡中查看该文件
    • 还有一个问题。 loadSync 调用本身就是一个异步调用。请查看我更新的问题
    【解决方案3】:

    有一些开源 js 可以缓解你的问题。 您可以使用LABJSRequreJS 插件。

    LABJSRequireJS 等脚本加载器将提高代码的速度和质量。

    您可以像这样在 LABJS 中保留加载脚本的顺序。

    $LAB.setOptions({AlwaysPreserveOrder:true})
    .script("script1.js")
    .script("script2.js")
    

    $LAB
    .script("script1.js").wait()
    .script("script2.js")
    

    script2.js 将等到 script1.js 加载完毕。

    我想这会解决你的问题。

    【讨论】:

    • 重要的是要注意 LABjs 的“等待”是指执行,而不是加载。所有脚本都尽可能并行加载以获得最佳性能,但如果需要,它们的执行顺序会按照正确的顺序进行。
    猜你喜欢
    • 2010-09-23
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多