Динамічно завантажуйте файл JavaScript


167

Як можна надійно та динамічно завантажувати файл JavaScript? Це можна використовувати для реалізації модуля або компонента, який при "ініціалізації" компонент буде динамічно завантажувати всі необхідні сценарії бібліотеки JavaScript на вимогу.

Клієнту, який використовує компонент, не потрібно завантажувати всі файли скриптів бібліотеки (і вручну вставляти <script>теги на їх веб-сторінку), які реалізують цей компонент - лише "основний" файл сценарію компонента.

Як основні бібліотеки JavaScript виконують це (прототип, jQuery тощо)? Чи об'єднують ці інструменти кілька файлів JavaScript в єдину перерозподілену версію файлу сценарію? Або вони роблять будь-яке динамічне завантаження допоміжних сценаріїв "бібліотеки"?

На додаток до цього питання: чи існує спосіб події після події динамічно включеного файлу JavaScript? Прототип document.observeпризначений для загальнодокументальних подій. Приклад:

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

Які доступні події для елемента сценарію?


Відповіді:


84

Ви можете писати теги динамічного сценарію (використовуючи прототип ):

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 та оцінити сценарій на глобальному рівні.

Якщо ви використовуєте прототип, метод 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);
    }
};

Що я можу зробити, щоб він працював для крос-домену? (сценарій завантаження з http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
користувач2284570


@Ciastopiekarz: Я не контролюю web.archive.org.
користувач2284570

Тоді вам потрібно скребти дані, до яких ви хочете отримати доступ, використовуючи якусь іншу програму, і надати їх собі
Девід Шуман

що таке Ajax.Request ??
bluejayke

72

У javascript немає імпорту / включення / необхідності, але є два основні способи досягти того, що ви хочете:

1 - Ви можете завантажити його за допомогою AJAX-дзвінка, а потім використовувати eval.

Це найпростіший спосіб, але він обмежений вашим доменом через налаштування безпеки Javascript, а використання eval відкриває двері для помилок і хак.

2 - Додайте тег сценарію з URL-адресою сценарію в HTML.

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

Обидва ці рішення обговорені та проілюстровані тут.

Тепер є велике питання, про яке потрібно знати. Це означає, що ви віддалено завантажуєте код. Сучасні веб-браузери завантажуватимуть файл і продовжуватимуть виконувати ваш поточний сценарій, оскільки вони завантажують все асинхронно для покращення продуктивності.

Це означає, що якщо ви скористаєтесь цими трюками безпосередньо, ви не зможете використати ваш щойно завантажений код наступного рядка після того, як ви попросили його завантажити, оскільки він все ще завантажується.

EG: my_lovely_script.js містить MySuperObject

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

Потім ви перезавантажуєте сторінку, натиснувши F5. І це працює! Конфуз ...

То що робити з цим?

Що ж, ви можете використовувати хак, який пропонує автор, за посиланням, яке я вам дав. Підсумовуючи це, для людей, які поспішають, він використовує en event для запуску функції зворотного дзвінка під час завантаження сценарію. Таким чином, ви можете помістити весь код за допомогою віддаленої бібліотеки у функцію зворотного виклику. EG:

function loadScript(url, callback)
{
    // adding the script tag to the head as suggested before
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = url;

   // then bind the event to the callback function 
   // there are several events for cross browser compatibility
   script.onreadystatechange = callback;
   script.onload = callback;

   // fire the loading
   head.appendChild(script);
}

Потім ви пишете код, який хочете використовувати ПІСЛЯ сценарій завантажений у лямбда-функції:

var myPrettyCode = function() {
    // here, do what ever you want
};

Потім ви запускаєте все це:

loadScript("my_lovely_script.js", myPrettyCode);

Добре. Я зрозумів. Але боляче писати всі ці речі.

Ну, і в цьому випадку ви можете використовувати як завжди фантастичний фреймворк jQuery, який дозволяє вам робити те саме в одному рядку:

$.getScript("my_lovely_script.js", function() {
    alert("Script loaded and executed.");
    // here you can use anything you defined in the loaded script
});

11
не можу повірити, наскільки це занижена відповідь. Дякую.
нафталіміч

2
Надіюсь, це тому, що люди не люблять читати повз першого рядка, якщо це не обіцяє їм, що вони будуть багатими, якщо вони "просто дотримуються цих трьох таємних кроків до успіху".
Коста

Це працює. Я сподіваюся, що обмін моїм досвідом тут економить ваш час, тому що я витратив на це багато годин. Я використовую Angular 6 і застосований шаблон (html, css, jquery) Проблема полягає в тому, що в шаблоні є файл js, який завантажується після завантаження елементів html для приєднання подій слухачів. Цей файл js було важко виконати після завантаження кутового додатка. Додавши його до тега сценаріїв angular app (angular.json), вони згрупують їх, але не виконають цей файл js після завантаження. Це занадто багато коду для перезапису в машинописному тексті, щоб це було чудовою підмогою. Наступний коментар Я поставлю в приклад через тривалість коментарів
Nour Lababidi

Я просто використав цей код так: ngAfterViewInit () {відладчик; $ .getScript ("/ properties / js / jquery.app.js", function () {alert ("Сценарій завантажений і виконаний."); // тут ви можете використовувати все, що було визначено у завантаженому скрипті}); }
Nour Lababidi

Для «$» в кутовому я дотримувався цього: stackoverflow.com/questions/32050645 / ...
Нура Lababidi

28

Нещодавно я використовував набагато менш складну версію з jQuery :

<script src="scripts/jquery.js"></script>
<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  var $head = $("head");
  for (var i = 0; i < js.length; i++) {
    $head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>

Він чудово працював у кожному браузері, в якому я його тестував: IE6 / 7, Firefox, Safari, Opera.

Оновлення: версія jQuery:

<script>
  var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"];
  for (var i = 0, l = js.length; i < l; i++) {
    document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>");
  }
</script>

25
Це чудово ... якщо ви не намагаєтеся завантажити jquery.

1
Схоже, jQuery збирається запустити плагін "Require" в jQuery Core для майбутнього випуску: plugins.jquery.com/project/require
Адам

1
Ще кращим способом використання jQuery є використання $.getScript. Дивіться мою відповідь.
Muhd

1
Modernizr (yepnope.js) або lab.js - відповідні рішення для цього. використання важкої бібліотеки сценаріїв (яку потрібно завантажити спочатку) не є найбільш підходящою відповіддю для мобільних пристроїв чи багатьох інших ситуацій.
1nfiniti

2
@MaulikGangani старші браузери та валідатори html інтерпретують це як маркер для завершення сценарію.
travis

20

Я в основному зробив те саме, що ви зробили і Адам, але з невеликою модифікацією, щоб переконатися, що я додав тег заголовка, щоб виконати роботу. Я просто створив функцію включення (код нижче) для обробки файлів скриптів та css.

Ця функція також перевіряє, чи сценарій або файл CSS ще не завантажувались динамічно. Він не перевіряє наявність кодованих вручну значень і, можливо, був кращий спосіб зробити це, але це відповідало меті.

function include( url, type ){
    // First make sure it hasn't been loaded by something else.
    if( Array.contains( includedFile, url ) )
        return;

    // Determine the MIME-type
    var jsExpr = new RegExp( "js$", "i" );
    var cssExpr = new RegExp( "css$", "i" );
    if( type == null )
        if( jsExpr.test( url ) )
            type = 'text/javascript';
        else if( cssExpr.test( url ) )
            type = 'text/css';

    // Create the appropriate element.
    var tag = null;
    switch( type ){
        case 'text/javascript' :
            tag = document.createElement( 'script' );
            tag.type = type;
            tag.src = url;
            break;
        case 'text/css' :
            tag = document.createElement( 'link' );
            tag.rel = 'stylesheet';
            tag.type = type;
            tag.href = url;
            break;
    }

    // Insert it to the <head> and the array to ensure it is not
    // loaded again.
    document.getElementsByTagName("head")[0].appendChild( tag );
    Array.add( includedFile, url );
}

Без більшого контексту, ніж цей Пікл, я боюся, що я не маю жодних пропозицій. Код, що знаходиться вище, працює як такий, його було витягнуто безпосередньо з функціонуючого проекту.
блідий кінь

5
@palehorse, Майк і Муд, мають рацію, це може спрацювати у вашому проекті, але це тому, що змінні "включений файл" і "масив" повинні бути визначені в інших місцях вашого проекту, цей код самостійно не запускається, можливо, це буде краще відредагувати його, щоб він міг працювати поза контекстом вашого проекту, або принаймні додати коментар із поясненням, що це за невизначені змінні (тип тощо)
user280109

14

ще одна дивовижна відповідь

$.getScript("my_lovely_script.js", function(){


   alert("Script loaded and executed.");
  // here you can use anything you defined in the loaded script

 });

https://stackoverflow.com/a/950146/671046


2
Що я можу зробити, щоб він працював для крос-домену? (завантаження сценарію від http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
користувач2284570

9

Ось декілька прикладів коду, який я знайшов ... хтось має кращий спосіб?

  function include(url)
  {
    var s = document.createElement("script");
    s.setAttribute("type", "text/javascript");
    s.setAttribute("src", url);
    var nodes = document.getElementsByTagName("*");
    var node = nodes[nodes.length -1].parentNode;
    node.appendChild(s);
  }

Що я можу зробити, щоб він працював для крос-домену? (завантаження сценарію з http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
користувач2284570

6

Якщо у вас вже завантажений jQuery, слід використовувати $ .getScript .

Це має перевагу перед іншими відповідями тут у тому, що у вас є вбудована функція зворотного виклику (щоб гарантувати завантаження сценарію до запуску залежного коду), і ви можете керувати кешуванням.


4

Якщо ви хочете завантажити сценарій 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()); });

3

хтось має кращий спосіб?

Я думаю, що просто додати скрипт до тіла було б простіше, ніж додати його до останнього вузла на сторінці. Як щодо цього:

function include(url) {
  var s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", url);
  document.body.appendChild(s);
}

Що я можу зробити, щоб він працював для крос-домену? (завантаження сценарію з http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
користувач2284570

3

Я використав ще одне рішення, яке я знайшов у мережі ... це знаходиться під Creativecommons, і він перевіряє, чи було включено джерело до виклику функції ...

Ви можете знайти файл тут: include.js

/** include - including .js files from JS - bfults@gmail.com - 2005-02-09
 ** Code licensed under Creative Commons Attribution-ShareAlike License 
 ** http://creativecommons.org/licenses/by-sa/2.0/
 **/              
var hIncludes = null;
function include(sURI)
{   
  if (document.getElementsByTagName)
  {   
    if (!hIncludes)
    {
      hIncludes = {}; 
      var cScripts = document.getElementsByTagName("script");
      for (var i=0,len=cScripts.length; i < len; i++)
        if (cScripts[i].src) hIncludes[cScripts[i].src] = true;
    }
    if (!hIncludes[sURI])
    {
      var oNew = document.createElement("script");
      oNew.type = "text/javascript";
      oNew.src = sURI;
      hIncludes[sURI]=true;
      document.getElementsByTagName("head")[0].appendChild(oNew);
    }
  }   
} 

Що я можу зробити, щоб він працював для крос-домену? (сценарій завантаження з http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStu‌​ff/userjs/aagmfunctions.js)
користувач2284570

3

Щойно дізнався про чудову особливість у 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(...);
    }
});

Працював ідеально для мене і має перевагу в управлінні залежностями. Зверніться до документації YUI для прикладу календаря YUI 2 .


Це, мабуть, ідеально, за винятком того, що YUI нецензурно великий лише для цієї функції.
Muhd

3

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

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

import(moduleSpecifier)
   .then(someModule => someModule.foo()); // executes foo method in someModule

2

Метод, який ми використовуємо в роботі, - це запитувати файл JavaScript за допомогою запиту AJAX, а потім повертати eval (). Якщо ви використовуєте бібліотеку прототипів, вони підтримують цю функціональність під час виклику Ajax.Request.


2

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 / тощо після імпорту jquery.js ви просто включите цей один файл, як-от так, щоб завантажити його у всій бібліотеці, додавши його до голови ...

<script type="text/javascript" src="project.library.js"></script>

2

Будьте приємними, короткими, простими та ретельними! :]

// 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

Динамічний імпорт модуля приземлився у Firefox 67+ .

(async () => {
   await import('./synth/BubbleSynth.js')
})()

З поводженням з помилками:

(async () => {
    await import('./synth/BubbleSynth.js').catch((error) => console.log('Loading failed' + error))
})()

Він також працює для будь-яких бібліотек без модулів, в цьому випадку lib доступний на об'єкті вікна, старим способом, але тільки на вимогу, що приємно.

Наприклад, використовуючи suncalc.js , для цього сервера повинен бути увімкнено CORS !

(async () => {
 await import('https://cdnjs.cloudflare.com/ajax/libs/suncalc/1.8.0/suncalc.min.js')
 .then(function(){
   let times = SunCalc.getTimes(new Date(), 51.5,-0.1);
   console.log("Golden Hour today in London: " + times.goldenHour.getHours() + ':' + times.sunrise.getMinutes() + ". Take your pics!")
  })
})()

https://caniuse.com/#feat=es6-module-dynamic-import


1

Є сценарії, розроблені спеціально для цієї мети.

yepnope.js вбудований в Modernizr та lab.js - більш оптимізована (але менш зручна для користувачів версія).

Я б не рекомендував робити це через велику бібліотеку, наприклад jquery або прототип - тому що однією з головних переваг завантажувача скриптів є можливість раннього завантаження скриптів - вам не доведеться чекати, поки jquery та всі ваші елементи дому завантажуватимуться раніше запустіть чек, щоб побачити, чи хочете динамічно завантажувати сценарій.


1

Я написав простий модуль, який автоматизує завдання імпорту / включення скриптів модулів у 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
});

1

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

document.write("<script src='https://www.google.com/recaptcha/api.js'></script>");

ви можете подивитися за посиланням: відповідь
asmmahmud

1

Ось простий варіант із зворотним зв'язком та підтримкою 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

Я знаю, що моя відповідь трохи запізнюється на це запитання, але ось чудова стаття на www.html5rocks.com - Глибоке занурення у мутні води завантаження сценарію .

У цій статті зроблено висновок, що щодо підтримки браузера найкращим способом динамічного завантаження файлу JavaScript без блокування візуалізації вмісту є наступний спосіб:

Зважаючи на те, що ви назвали чотири сценарії, script1.js, script2.js, script3.js, script4.jsви можете це зробити, застосувавши async = false :

[
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
].forEach(function(src) {
  var script = document.createElement('script');
  script.src = src;
  script.async = false;
  document.head.appendChild(script);
});

Тепер Spec каже : Скачайте разом, виконайте порядок, як тільки всі завантажують.

Firefox <3.6, Opera говорить: Я поняття не маю, що це за "асинхронність", але так буває, я виконую сценарії, додані через JS, у тому порядку, який вони додали.

Safari 5.0 говорить: я розумію "асинхронізацію", але не розумію, як встановити її на "false" за допомогою JS. Я виконаю ваші сценарії, як тільки вони приземляться, в будь-якому порядку.

IE <10 говорить: Поняття про "асинхронізацію" не має, але існує рішення із використанням "onreadystatechange".

Все інше говорить: я твій друг, ми будемо робити це за книгою.

Тепер повний код з IE <10 вирішення:

var scripts = [
  'script1.js',
  'script2.js',
  'script3.js',
  'script4.js'
];
var src;
var script;
var pendingScripts = [];
var firstScript = document.scripts[0];

// Watch scripts load in IE
function stateChange() {
  // Execute as many scripts in order as we can
  var pendingScript;
  while (pendingScripts[0] && pendingScripts[0].readyState == 'loaded') {
    pendingScript = pendingScripts.shift();
    // avoid future loading events from this script (eg, if src changes)
    pendingScript.onreadystatechange = null;
    // can't just appendChild, old IE bug if element isn't closed
    firstScript.parentNode.insertBefore(pendingScript, firstScript);
  }
}

// loop through our script urls
while (src = scripts.shift()) {
  if ('async' in firstScript) { // modern browsers
    script = document.createElement('script');
    script.async = false;
    script.src = src;
    document.head.appendChild(script);
  }
  else if (firstScript.readyState) { // IE<10
    // create a script and add it to our todo pile
    script = document.createElement('script');
    pendingScripts.push(script);
    // listen for state changes
    script.onreadystatechange = stateChange;
    // must set src AFTER adding onreadystatechange listener
    // else we’ll miss the loaded event for cached scripts
    script.src = src;
  }
  else { // fall back to defer
    document.write('<script src="' + src + '" defer></'+'script>');
  }
}

Кілька хитрощів і мінімізація пізніше, це 362 байти

!function(e,t,r){function n(){for(;d[0]&&"loaded"==d[0][f];)c=d.shift(),c[o]=!i.parentNode.insertBefore(c,i)}for(var s,a,c,d=[],i=e.scripts[0],o="onreadystatechange",f="readyState";s=r.shift();)a=e.createElement(t),"async"in i?(a.async=!1,e.head.appendChild(a)):i[f]?(d.push(a),a[o]=n):e.write("<"+t+' src="'+s+'" defer></'+t+">"),a.src=s}(document,"script",[
  "//other-domain.com/1.js",
  "2.js"
])

Я використовую підхід вище, він працює добре в хроміровані та firefox, але стикається з проблемами в браузері IE
Містер Рошан,

1

Щось на зразок цього...

<script>
     $(document).ready(function() {
          $('body').append('<script src="https://maps.googleapis.com/maps/api/js?key=KEY&libraries=places&callback=getCurrentPickupLocation" async defer><\/script>');
     });
</script>

1

Ось простий приклад функції для завантаження файлів 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

Абсурдна однолінійка для тих, хто думає, що завантаження 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

За допомогою Обіцянь ви можете спростити це так. Функція навантажувача:

  const loadCDN = src =>
    new Promise((resolve, reject) => {
      if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
      const script = document.createElement("script")
      script.src = src
      script.async = true
      document.head.appendChild(script)
      script.onload = resolve
      script.onerror = reject
    })

Використання (асинхронізація / очікування):

await loadCDN("https://.../script.js")

Використання (Обіцянка):

loadCDN("https://.../script.js").then(res => {}).catch(err => {})

ПРИМІТКА. Було одне подібне рішення, але воно не перевіряє, чи сценарій уже завантажений і щоразу завантажує сценарій. Цей перевіряє властивість src.


0

всі основні бібліотеки javascript, такі як jscript, прототип, YUI, мають підтримку для завантаження файлів скриптів. Наприклад, в YUI після завантаження ядра ви можете виконати наступне, щоб завантажити календар управління

var loader = new YAHOO.util.YUILoader({

    require: ['calendar'], // what components?

    base: '../../build/',//where do they live?

    //filter: "DEBUG",  //use debug versions (or apply some
                        //some other filter?

    //loadOptional: true, //load all optional dependencies?

    //onSuccess is the function that YUI Loader
    //should call when all components are successfully loaded.
    onSuccess: function() {
        //Once the YUI Calendar Control and dependencies are on
        //the page, we'll verify that our target container is 
        //available in the DOM and then instantiate a default
        //calendar into it:
        YAHOO.util.Event.onAvailable("calendar_container", function() {
            var myCal = new YAHOO.widget.Calendar("mycal_id", "calendar_container");
            myCal.render();
        })
     },

    // should a failure occur, the onFailure function will be executed
    onFailure: function(o) {
        alert("error: " + YAHOO.lang.dump(o));
    }

 });

// Calculate the dependency and insert the required scripts and css resources
// into the document
loader.insert();

ви можете подивитися за посиланням: відповідь
asmmahmud

0

Деякі з вищезгаданих постів я переробив робочим прикладом. Тут ми також можемо дати css і js в одному масиві.

$(document).ready(function(){

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 jsScriptCssLoader = {

jsExpr : new RegExp( "js$", "i" ),
cssExpr : new RegExp( "css$", "i" ),
loadedFiles: [],

loadFile: function (cssJsFileArray) {
    var self = this;
    // remove duplicates with in array
    cssJsFileArray.filter((item,index)=>cssJsFileArray.indexOf(item)==index)
    var loadedFileArray = this.loadedFiles;
    $.each(cssJsFileArray, function( index, url ) {
            // if multiple arrays are loaded the check the uniqueness
            if (loadedFileArray.contains(url)) return;
            if( self.jsExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addScript(data);
                });

            }else if( self.cssExpr.test( url ) ){
                $.get(url, function(data) {
                    self.addCss(data);
                });
            }

            self.loadedFiles.push(url);
    });

    // don't load twice accross different arrays

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

};


//jsScriptCssLoader.loadFile(["css/1.css","css/2.css","css/3.css"]);
jsScriptCssLoader.loadFile(["js/common/1.js","js/2.js","js/common/file/fileReader.js"]);
});

0

Для тих із вас, хто любить одноосібники:

import('./myscript.js');

Ймовірно, ви можете отримати помилку, наприклад:

Доступ до скрипту за адресою " http: //..../myscript.js " від походження " http://127.0.0.1 " був заблокований політикою CORS: у заголовку "Access-Control-Allow-Origin" немає заголовка запитуваний ресурс.

У такому випадку ви можете повернутися до:

fetch('myscript.js').then(r => r.text()).then(t => new Function(t)());
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.