Динамически загружать файл JavaScript - PullRequest
151 голосов
/ 22 августа 2008

Как вы можете надежно и динамически загрузить файл 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');
});

Какие события доступны для элемента скрипта?

Ответы [ 24 ]

3 голосов
/ 22 августа 2008

У кого-нибудь есть лучший способ?

Я думаю, что было бы проще добавить скрипт в тело, чем добавить его в последний узел на странице. Как насчет этого:

function include(url) {
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);
}
2 голосов
/ 17 июля 2018

Существует новый предложенный стандарт ECMA под названием динамический импорт , недавно включенный в Chrome и Safari.

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

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule
2 голосов
/ 08 февраля 2015

Делайте это красиво, коротко, просто и легко! :]

// 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]);
    }
}

Этот код является просто кратким функциональным примером того, что может потребовать дополнительные функциональные возможности для полной поддержки на любой (или данной) платформе.

2 голосов
/ 22 августа 2008

Техника, которую мы используем на работе, - это запрос файла javascript с использованием AJAX-запроса, а затем eval () возврата. Если вы используете библиотеку прототипов, они поддерживают эту функцию в своем вызове Ajax.Request.

2 голосов
/ 14 января 2011

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>');
    }

Для использования - в заголовке вашего html / php / etc после импорта jquery.js вы просто включите этот файл, чтобы загрузить всю библиотеку и добавить ее в заголовок ...

<script type="text/javascript" src="project.library.js"></script>
1 голос
/ 17 сентября 2018

Абсурдный однострочный текст для тех, кто считает, что загрузка библиотеки js не должна занимать более одной строки кода: P

await new Promise((resolve, reject) => {let js = document.createElement("script"); js.src="mylibrary.js"; js.onload=resolve; js.onerror=reject; document.body.appendChild(js)});

Очевидно, что если скрипт, который вы хотите импортировать, является модулем, вы можете использовать функцию import(...).

1 голос
/ 26 апреля 2018

Вот простой пример для функции загрузки файлов JS. Соответствующие баллы:

  • вам не нужен jQuery, поэтому вы можете использовать его изначально для загрузки также файла jQuery.js
  • это асинхронно с обратным вызовом
  • обеспечивает загрузку только один раз, поскольку хранит вложение с записью загруженных URL-адресов, что позволяет избежать использования сети
  • В отличие от jQuery $.ajax или $.getScript вы можете использовать одноразовые номера, решая, таким образом, проблемы с CSP unsafe-inline. Просто используйте собственность script.nonce
var getScriptOnce = function() {

    var scriptArray = []; //array of urls (closure)

    //function to defer loading of script
    return function (url, callback){
        //the array doesn't have such url
        if (scriptArray.indexOf(url) === -1){

            var script=document.createElement('script');
            script.src=url;
            var head=document.getElementsByTagName('head')[0],
                done=false;

            script.onload=script.onreadystatechange = function(){
                if ( !done && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') ) {
                    done=true;
                    if (typeof callback === 'function') {
                        callback();
                    }
                    script.onload = script.onreadystatechange = null;
                    head.removeChild(script);

                    scriptArray.push(url);
                }
            };

            head.appendChild(script);
        }
    };
}();

Теперь вы используете его просто

getScriptOnce("url_of_your_JS_file.js");
1 голос
/ 08 марта 2017

Здесь - простой с обратным вызовом и поддержкой IE:

function loadScript(url, callback) {

    var script = document.createElement("script")
    script.type = "text/javascript";

    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);
}

loadScript("https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {

     //jQuery loaded
     console.log('jquery loaded');

});
1 голос
/ 04 марта 2016

Я потерян во всех этих примерах, но сегодня мне нужно было загрузить внешний .js из моего основного .js, и я сделал это:

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");
1 голос
/ 12 апреля 2013

Я написал простой модуль, который автоматизирует работу по импорту / включению скриптов модуля в JavaScript. Попробуйте и, пожалуйста, оставьте отзыв! :) Для подробного объяснения кода обратитесь к этому сообщению в блоге: http://stamat.wordpress.com/2013/04/12/javascript-require-import-include-modules/

var _rmod = _rmod || {}; //require module namespace
_rmod.on_ready_fn_stack = [];
_rmod.libpath = '';
_rmod.imported = {};
_rmod.loading = {
    scripts: {},
    length: 0
};

_rmod.findScriptPath = function(script_name) {
    var script_elems = document.getElementsByTagName('script');
    for (var i = 0; i < script_elems.length; i++) {
        if (script_elems[i].src.endsWith(script_name)) {
            var href = window.location.href;
            href = href.substring(0, href.lastIndexOf('/'));
            var url = script_elems[i].src.substring(0, script_elems[i].length - script_name.length);
            return url.substring(href.length+1, url.length);
        }
    }
    return '';
};

_rmod.libpath = _rmod.findScriptPath('script.js'); //Path of your main script used to mark the root directory of your library, any library


_rmod.injectScript = function(script_name, uri, callback, prepare) {

    if(!prepare)
        prepare(script_name, uri);

    var script_elem = document.createElement('script');
    script_elem.type = 'text/javascript';
    script_elem.title = script_name;
    script_elem.src = uri;
    script_elem.async = true;
    script_elem.defer = false;

    if(!callback)
        script_elem.onload = function() {
            callback(script_name, uri);
        };

    document.getElementsByTagName('head')[0].appendChild(script_elem);
};

_rmod.requirePrepare = function(script_name, uri) {
    _rmod.loading.scripts[script_name] = uri;
    _rmod.loading.length++;
};

_rmod.requireCallback = function(script_name, uri) {
    _rmod.loading.length--;
    delete _rmod.loading.scripts[script_name];
    _rmod.imported[script_name] = uri;

    if(_rmod.loading.length == 0)
        _rmod.onReady();
};

_rmod.onReady = function() {
    if (!_rmod.LOADED) {
        for (var i = 0; i < _rmod.on_ready_fn_stack.length; i++){
            _rmod.on_ready_fn_stack[i]();
        });
        _rmod.LOADED = true;
    }
};

//you can rename based on your liking. I chose require, but it can be called include or anything else that is easy for you to remember or write, except import because it is reserved for future use.
var require = function(script_name) {
    var np = script_name.split('.');
    if (np[np.length-1] === '*') {
        np.pop();
        np.push('_all');
    }

    script_name = np.join('.');
    var uri = _rmod.libpath + np.join('/')+'.js';
    if (!_rmod.loading.scripts.hasOwnProperty(script_name) 
     && !_rmod.imported.hasOwnProperty(script_name)) {
        _rmod.injectScript(script_name, uri, 
            _rmod.requireCallback, 
                _rmod.requirePrepare);
    }
};

var ready = function(fn) {
    _rmod.on_ready_fn_stack.push(fn);
};

// ----- USAGE -----

require('ivar.util.array');
require('ivar.util.string');
require('ivar.net.*');

ready(function(){
    //do something when required scripts are loaded
});
...