Динамическая загрузка JavaScript синхронно - PullRequest
57 голосов
/ 21 мая 2010

Я использую шаблон модуля , одна из вещей, которую я хочу сделать, это динамически включить внешний файл JavaScript, выполнить файл, а затем использовать функции / переменные в файле в return { } моего модуля.

Я не могу понять, как это легко сделать.Существуют ли стандартные способы выполнения загрузки псевдосинхронного внешнего скрипта?

function myModule() {
    var tag = document.createElement("script");
    tag.type = "text/javascript";
    tag.src = "http://some/script.js";
    document.getElementsByTagName('head')[0].appendChild(tag);

    //something should go here to ensure file is loaded before return is executed

    return {
        external: externalVariable 
    }
}

Ответы [ 18 ]

0 голосов
/ 20 октября 2016

Я знаю, что это старый вопрос, но, может быть, кто-то еще прочитает это и посчитает его полезным! Только что созданные новые компоненты используют ES6 для динамической загрузки скриптов синхронно. Детали проекта и исходный код находятся на GitHub https://github.com/amgadfahmi/scripty

0 голосов
/ 26 января 2016

При использовании Angular вы можете воспользоваться тем фактом, что каждый провайдер создается перед созданием других сервисов.Вы можете объединить этот факт с использованием xhr и eval (), как упомянуто @Neil.Код должен быть следующим:

app.provider('SomeScriptSyncLoader', function() {

    var resourceUrl =  'http://some/script.js';
    var dummy = {};

    this.$get = function() {

        var q = jQuery.ajax({
            type: 'GET', url: resourceUrl, cache: false, async: false
        });

        if (q.status === 200) {
            eval(q.responseText); // execute some script synchronously as inline script - eval forces sync processing
        }
        return dummy;
    };
});

Чтобы принудительно инициализировать провайдера, вам необходимо ввести его хотя бы в одну другую директиву / услугу.Предпочтительно это будет служба, которая использует код, загруженный скриптом.

app.directive('myDirective', ['SomeScriptSyncLoader', function(someScriptSyncLoader) {

return {
    restrict: 'E',
    link: function(scope, element, attrs) {
        // some ode
    },
    template: "this is my template"
   };
}]);
0 голосов
/ 21 мая 2012

То же, что и ответ Шона, но вместо создания тега сценария просто оцените его. это гарантирует, что код действительно готов к использованию.

0 голосов
/ 21 мая 2010

Я использую метод jquery load , применяемый к элементу div. что-то вроде

<div id="js">
<!-- script will be inserted here --> 
</div>

...

$("#js").load("path", function() {  alert("callback!" });

Вы можете загружать сценарии несколько раз, и каждый раз один сценарий полностью заменяет загруженный ранее

0 голосов
/ 20 февраля 2019

вот мой код

var loaded_script = [];
function loadScript(urls, callback, sync) {
    var len = urls.length, count = 0;

    // check are all js loaded, then execute callback (if any)
    var check = function() {
        if (count == len) {
            callback && typeof callback=="function" && callback();
        }
    };

    for (var i = 0; i < len; i++) {
        var url = urls[i];

        // check if script not loaded (prevent load again)
        if (loaded_script.indexOf(url) == -1) {
            var script = document.createElement("script");
            script.type = "text/javascript";

            // set sync loading here (default is async)
            if (sync) {
                script.async = false;
            }

            // script onload event
            if (script.readyState) {    // IE
                script.onreadystatechange = function() {
                    if (script.readyState=="loaded" || script.readyState=="complete") {
                        script.onreadystatechange = null;
                        count++, check();
                    }
                };
            } else {    // Others
                script.onload = function() {
                    count++, check();
                };
            }

            // add script to head tag
            script.src = url;
            document.getElementsByTagName("head")[0].appendChild(script);

            // mark this script has loaded
            loaded_script.push(url);
        } else {
            count++, check();
        }
    }
}

Я использую это на сайте pjax.

loadScript(
    [
        "js/first.js",
        "js/second.js",
    ],
    function() {
        alert("Scripts loaded.");
    },
    true
);
0 голосов
/ 26 августа 2015

Моя стратегия, классический пример загрузки jQuery UI, надеюсь, это поможет вам

( function( tools, libs ){
	
    // Iterator
    var require = function( scripts, onEnd ){
        
        onEnd = onEnd || function(){};
        
        if( !scripts || scripts.length < 1 )return onEnd();
        
        var src    = scripts.splice( 0, 1),
            script = document.createElement( "script" );
        
        script.setAttribute( "src", src );
        
        tools.addEvent( "load", script, function(){
            
            require( scripts, onEnd );
            
        } );
        
        document.getElementsByTagName( "head" )[ 0 ].appendChild( script );
        
    };
    
    // Install all scripts with a copy of scripts
    require( libs.slice(), function(){
    
        alert( "Enjoy :)" );
    
    } );
    
    // Timeout information
    var ti = setTimeout( function(){
        
        if( !window.jQuery || !window.jQuery.ui )alert( "Timeout !" );
        
        clearTimeout( ti );
        
    }, 5000 );

} )(

    { // Tools
    
        addEvent : function( evnt, elem, func ){
        
            try{

                if( elem.addEventListener ){

                    elem.addEventListener( evnt, func, false );

                }else if( elem.attachEvent ){

                     var r = elem.attachEvent( "on" + evnt, func );

                }

                return true;

            }catch( e ){

                return false;

            }		    

        }
    
    },
    [ // Scripts
    
        "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.min.js",
        "https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"
        
    ]

);
0 голосов
/ 04 апреля 2019

У меня была похожая задача несколькими днями ранее, и вот как я это сделал.
Этот загрузчик работает как с префиксами file://, так и с http:// и https://, и является перекрестнымсовместимый с браузером.
Однако он не может загружать определенные классы или функции как модули из скриптов;он полностью загрузит весь скрипт и сделает его доступным для DOM.

// Loads a script or an array of scripts (including stylesheets)
// in their respective index order, synchronously.
// By Sayanjyoti Das @https://stackoverflow.com/users/7189950/sayanjyoti-das
var Loader={
    queue: [], // Scripts queued to be loaded synchronously
    loadJsCss: function(src, onl) {
        var ext=src.toLowerCase().substring(src.length-3, src.length);
        if(ext=='.js') {
            var scrNode=el('script', null, null, null);
            scrNode.type='text/javascript';
            scrNode.onload=function() {onl();};
            scrNode.src=src;
            document.body.appendChild(scrNode);
        }else if(ext=='css') {
            var cssNode=el('link', null, null, null);
            cssNode.rel='stylesheet';
            cssNode.type='text/css';
            cssNode.href=src;
            document.head.appendChild(cssNode);
            onl();
        }
    },
    add: function(data) {
        var ltype=(typeof data.src).toLowerCase();

        // Load a single script
        if(ltype=='string') {
            data.src=data.src;
            Loader.queue.splice(0, 1, data, Loader.queue[0]);
            Loader.next();
        }
        // Load an array of scripts
        else if(ltype=='object') {
            for(var i=data.src.length-1; i>=0; i--) {
                Loader.queue.splice(0, 1, {
                    src: data.src[i],
                    onload: function() {
                        if(Loader.next()==false) {
                            data.onload();
                            return;
                        }
                        Loader.next();
                    }
                }, Loader.queue[0]);
            }
            Loader.next();
        }
    },
    next: function() {
        if(Loader.queue.length!=0 && Loader.queue[0]) {
            var scr=Loader.queue[0];

            // Remove the script from the queue
            if(Loader.queue.length>1)
                Loader.queue.splice(0, 2, Loader.queue[1]);
            else
                Loader.queue=[];

            // Load the script
            Loader.loadJsCss(scr.src, scr.onload);
        }else return false;
    }
};

Вышеприведенная функция очень мощная и элегантная ;это позволяет вам загружать один скрипт или массив скриптов синхронно (т. е. следующий скрипт не загружается до завершения предыдущей загрузки скрипта).Кроме того, загруженный сценарий может загружать больше сценариев, что задерживает очередь в родительском сценарии.

Кстати, здесь сценарий означает файл JavaScript или таблицу стилей CSS .

Вот как это использовать: -

// Load a single script
Loader.add({
    src: 'test.js',
    onload: function() {
        alert('yay!');
    }
});

// Load multiple scripts
Loader.add({
    src: ['test1.js', 'test2.js', 'mystyles.css', 'test3.js'],
    onload: function() {
        alert('all loaded!');
    }
});

Обратите внимание, что функция onload в аргументах Loader вызывается при загрузке всех скриптов, а не когда один илизагружается один сценарий.

Вы также можете загрузить больше сценариев в загруженные сценарии, например, в test.js, test1.js и т. д. Таким образом, вы отложите загрузку следующего родительского сценария.и очередь в дочернем скрипте будет иметь приоритет.

Надеюсь, это поможет: -)

0 голосов
/ 21 мая 2010

Вы не можете и не должны выполнять серверные операции синхронно по очевидным причинам. Однако вы можете иметь обработчик событий, сообщающий, когда скрипт загружен:

tag.onreadystatechange = function() { if (this.readyState == 'complete' || this.readyState == 'loaded') this.onload({ target: this }); };

tag.onload = function(load) {/*init code here*/}

onreadystatechange делегирование из памяти - обходной путь для IE, который имеет пятнистую поддержку onload.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...