动态加载JavaScript文件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态加载JavaScript文件相关的知识,希望对你有一定的参考价值。

如何可靠地动态加载javascript文件?这将用于实现一个模块或组件,当'初始化'时,组件将按需动态加载所有需要的JavaScript库脚本。

使用该组件的客户端不需要加载实现此组件的所有库脚本文件(并手动将<script>标记插入其网页) - 只需要“主”组件脚本文件。

主流JavaScript库如何实现这一点(Prototype,jQuery等)?这些工具是否将多个JavaScript文件合并为一个可再发行的“构建”版本的脚本文件?或者他们是否动态加载辅助“库”脚本?

这个问题的补充:有没有办法在加载动态包含的JavaScript文件后处理事件?原型有document.observe用于文档范围的事件。例:

document.observe("dom:loaded", function() {
  // initially hide all containers for tab content
  $$('div.tabcontent').invoke('hide');
});

脚本元素有哪些可用事件?

答案

您可以编写动态脚本标记(使用Prototype):

new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});

这里的问题是我们不知道外部脚本文件何时完全加载。

我们经常希望我们的依赖代码在下一行,并喜欢写下这样的东西:

if (iNeedSomeMore) {
    Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod();
    myFancyMethod(); // cool, no need for callbacks!
}

有一种智能方法可以在不需要回调的情况下注入脚本依赖项。您只需通过同步AJAX请求拉出脚本并在全局级别上评估脚本。

如果使用Prototype,则Script.load方法如下所示:

var Script = {
    _loadedScripts: [],
    include: function(script) {
        // include script only once
        if (this._loadedScripts.include(script)) {
            return false;
        }
        // request file synchronous
        var code = new Ajax.Request(script, {
            asynchronous: false,
            method: "GET",
            evalJS: false,
            evalJSON: false
        }).transport.responseText;
        // eval code on global level
        if (Prototype.Browser.IE) {
            window.execScript(code);
        } else if (Prototype.Browser.WebKit) {
            $$("head").first().insert(Object.extend(
                new Element("script", {
                    type: "text/javascript"
                }), {
                    text: code
                }
            ));
        } else {
            window.eval(code);
        }
        // remember included script
        this._loadedScripts.push(script);
    }
};
另一答案

刚刚发现了YUI 3的一个很棒的功能(在预览版中可用时)。您可以轻松地将依赖项插入YUI库和“外部”模块(您正在寻找的),而无需太多代码:YUI Loader

它还会在加载外部模块后立即回答有关正在调用的函数的第二个问题。

例:

YUI({
    modules: {
        'simple': {
            fullpath: "http://example.com/public/js/simple.js"
        },
        'complicated': {
            fullpath: "http://example.com/public/js/complicated.js"
            requires: ['simple']  // <-- dependency to 'simple' module
        }
    },
    timeout: 10000
}).use('complicated', function(Y, result) {
    // called as soon as 'complicated' is loaded
    if (!result.success) {
        // loading failed, or timeout
        handleError(result.msg);
    } else {
        // call a function that needs 'complicated'
        doSomethingComplicated(...);
    }
});

完美地为我工作,并具有管理依赖关系的优势。有关example with YUI 2 calendar的信息,请参阅YUI文档。

另一答案

如果要加载SYNC脚本,则需要将脚本文本直接添加到html HEAD标记。添加它将触发ASYNC加载。要同步从外部文件加载脚本文本,请使用XHR。下面是一个快速示例(它正在使用此帖和其他帖子中的其他部分答案):

/*sample requires an additional method for array prototype:*/

if (Array.prototype.contains === undefined) {
Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) { if (this[i] === obj) return true; }
    return false;
};
};

/*define object that will wrap our logic*/
var ScriptLoader = {
LoadedFiles: [],

LoadFile: function (url) {
    var self = this;
    if (this.LoadedFiles.contains(url)) return;

    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                self.LoadedFiles.push(url);
                self.AddScript(xhr.responseText);
            } else {
                if (console) console.error(xhr.statusText);
            }
        }
    };
    xhr.open("GET", url, false);/*last parameter defines if call is async or not*/
    xhr.send(null);
},

AddScript: function (code) {
    var oNew = document.createElement("script");
    oNew.type = "text/javascript";
    oNew.textContent = code;
    document.getElementsByTagName("head")[0].appendChild(oNew);
}
};

/*Load script file. ScriptLoader will check if you try to load a file that has already been loaded (this check might be better, but I'm lazy).*/

ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
ScriptLoader.LoadFile("Scripts/jquery-2.0.1.min.js");
/*this will be executed right after upper lines. It requires jquery to execute. It requires a HTML input with id "tb1"*/
$(function () { alert($('#tb1').val()); });
另一答案

我们在工作中使用的技术是使用AJAX请求请求javascript文件,然后使用eval()返回。如果您正在使用原型库,则它们在Ajax.Request调用中支持此功能。

另一答案

jquery用它的.append()函数解决了这个问题 - 用它来加载完整的jquery ui包

/*
 * FILENAME : project.library.js
 * USAGE    : loads any javascript library
 */
    var dirPath = "../js/";
    var library = ["functions.js","swfobject.js","jquery.jeditable.mini.js","jquery-ui-1.8.8.custom.min.js","ui/jquery.ui.core.min.js","ui/jquery.ui.widget.min.js","ui/jquery.ui.position.min.js","ui/jquery.ui.button.min.js","ui/jquery.ui.mouse.min.js","ui/jquery.ui.dialog.min.js","ui/jquery.effects.core.min.js","ui/jquery.effects.blind.min.js","ui/jquery.effects.fade.min.js","ui/jquery.effects.slide.min.js","ui/jquery.effects.transfer.min.js"];

    for(var script in library){
        $('head').append('<script type="text/javascript" src="' + dirPath + library[script] + '"></script>');
    }

要使用 - 在导入jquery.js之后在你的html / php / etc的头部,你只需要包含这样一个文件就可以加载到你的库的整个库中...

<script type="text/javascript" src="project.library.js"></script>
另一答案

保持简洁,简洁,可维护! :]

// 3rd party plugins / script (don't forget the full path is necessary)
var FULL_PATH = '', s =
[
    FULL_PATH + 'plugins/script.js'      // Script example
    FULL_PATH + 'plugins/jquery.1.2.js', // jQuery Library 
    FULL_PATH + 'plugins/crypto-js/hmac-sha1.js',      // CryptoJS
    FULL_PATH + 'plugins/crypto-js/enc-base64-min.js'  // CryptoJS
];

function load(url)
{
    var ajax = new XMLHttpRequest();
    ajax.open('GET', url, false);
    ajax.onreadystatechange = function ()
    {
        var script = ajax.response || ajax.responseText;
        if (ajax.readyState === 4)
        {
            switch(ajax.status)
            {
                case 200:
                    eval.apply( window, [script] );
                    console.log("library loaded: ", url);
                    break;
                default:
                    console.log("ERROR: library not loaded: ", url);
            }
        }
    };
    ajax.send(null);
}

 // initialize a single load 
load('plugins/script.js');

// initialize a full load of scripts
if (s.length > 0)
{
    for (i = 0; i < s.length; i++)
    {
        load(s[i]);
    }
}

此代码只是一个简短的功能示例,可能需要额外的功能,以便在任何(或给定)平台上提供全面支持。

另一答案

有一个新的ECMA标准叫做dynamic import,最近被整合到Chrome和Safari中。

const moduleSpecifier = './dir/someModule.js';

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule
另一答案

有专门为此目的而设计的脚本。

yepnope.js内置于Modernizr,lab.js是一个更优化(但不太用户友好的版本)。

我不会建议通过像jquery或prototype这样的大型库来实现这一点 - 因为脚本加载器的一个主要好处是能够尽早加载脚本 - 你不必等到jquery和你所有的dom元素加载之前运行检查以查看是否要动态加载脚本。

另一答案

我编写了一个简单的模块,可以自动执行在JavaScript中导入/包含模块脚本的工作。试一试,请多给一些反馈! :)有关代码的详细说明,请参阅此博客文章:http://stamat.wordpress.c

以上是关于动态加载JavaScript文件的主要内容,如果未能解决你的问题,请参考以下文章

thymeleaf 片段渲染后重新加载 javascript

两种动态加载JavaScript文件的方法

在android中动态创建选项卡并使用传入的参数加载片段

如何延迟或异步此 WordPress javascript 片段以最后加载以加快页面加载时间?

动态加载 JavaScript 文件

动态加载 JavaScript 文件