Способы загрузки скриптов по AJAX с примерами

Раздел: Веб-разработка -> Динамическая загрузка

Динамическая загрузка скриптов через AJAX

Основной подход: создание DOM-элемента script.

Этот метод позволяет загрузить и выполнить внешний скрипт в любой момент, имитируя поведение тега <script> с атрибутом src. Подходит для скриптов, которые требуют полной загрузки перед выполнением.


// Функция для загрузки скрипта через динамическое создание script
function loadScript(url, callback) {
  var script = document.createElement('script');
  script.src = url;
  script.onload = function() {
    if (typeof callback === 'function') {
      callback();
    }
  };
  script.onerror = function() {
    console.error('Ошибка загрузки скрипта:', url);
  };
  document.head.appendChild(script);
}

// Пример использования
loadScript('https://example.com/script.js', function() {
  console.log('Скрипт загружен');
});
  

Js script script src ajax (подключение скрипта через ajax)

Пояснение шагов:

  • Создаётся новый элемент script.
  • Задаётся атрибут src с URL скрипта.
  • Обработчик onload вызывается после полной загрузки и выполнения скрипта.
  • Обработчик onerror уведомляет о неудаче (например, неверный URL или таймаут).
  • Элемент добавляется в <head> (можно и в <body>).

Возможные проблемы:

  • Порядок загрузки: Если несколько скриптов загружаются последовательно, каждый следующий должен стартовать после завершения предыдущего. Используйте цепочку колбэков или Promises.
  • Дублирование: Если один и тот же скрипт загружается дважды, возможно повторное выполнение. Проверяйте, не добавлен ли уже элемент с таким src.
  • Кэширование: Браузер может закэшировать скрипт, что полезно, но для обновления используйте уникальные параметры в URL (например, ?v=2).
  • CORS: Если скрипт расположен на другом домене, сервер должен разрешить кросс-доменные запросы (заголовок Access-Control-Allow-Origin).

Как загрузить скрипт, используя XMLHttpRequest и eval?

Этот вариант подходит, когда скрипт необходимо обработать перед выполнением (например, изменить содержимое). Однако он менее безопасен и не рекомендуется из-за риска XSS.


var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/script.js', true);
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    var code = xhr.responseText;
    // Выполнение кода через eval
    eval(code);
    // Или через new Function
    // var fn = new Function(code); fn();
  }
};
xhr.send();
  

Проблемы:

  • eval и безопасность: Выполнение произвольного кода может привести к уязвимостям, если источник не доверен.
  • Область видимости: eval выполняется в текущем контексте, может загрязнять глобальную область или нарушать локальные переменные.
  • Производительность: Некоторые движки не оптимизируют код внутри eval.
  • Кэширование: XMLHttpRequest может иметь отдельный кэш, отличающийся от кэша тега script.

Как загрузить скрипт с помощью fetch и Promise?

Современный способ, дающий полный контроль над загрузкой. Скрипт получается как текст, после чего выполняется через eval или Function. Подходит для случаев, когда требуется проверить или изменить код перед выполнением.


fetch('https://example.com/script.js')
  .then(response => {
    if (!response.ok) {
      throw new Error('Не удалось загрузить скрипт, статус: ' + response.status);
    }
    return response.text();
  })
  .then(code => {
    // Выполнение кода (например, через Function)
    new Function(code)();
  })
  .catch(err => console.error(err));
  

Проблемы:

  • Политика CORS: fetch также подчиняется кросс-доменной политике. Необходимы соответствующие заголовки на сервере.
  • Выполнение через eval/Function: те же риски безопасности и области видимости, что и у XMLHttpRequest.
  • Асинхронность: код выполняется после разрешения Promise, порядок может отличаться от традиционного script.
Как использовать динамический import() для загрузки модулей?

Этот метод предназначен для ES-модулей и возвращает Promise. Подходит, когда скрипт оформлен как модуль (с export).


// Импорт модуля по требованию
import('https://example.com/module.js')
  .then(module => {
    // Использование экспортированных функций
    module.doSomething();
  })
  .catch(err => console.error('Ошибка загрузки модуля:', err));
  

Проблемы:

  • Поддержка: import() работает только в современных браузерах, не поддерживается в IE.
  • CORS: Для внешних модулей требуется заголовок Access-Control-Allow-Origin.
  • Не модульные скрипты: import() не подходит для обычных скриптов, не имеющих export.
Как загрузить скрипт с помощью jQuery $.getScript?

jQuery предоставляет удобную обёртку над динамическим созданием script. Подходит для проектов, уже использующих jQuery.


// Загрузка скрипта
$.getScript('https://example.com/script.js', function(data, textStatus, jqxhr) {
  console.log('Скрипт загружен, статус:', textStatus);
});
  

Проблемы:

  • Зависимость от jQuery: необходимо подключение библиотеки.
  • Управление кэшем: по умолчанию jQuery добавляет параметр _=timestamp для предотвращения кэширования, что может быть нежелательно.
  • Обработка ошибок: колбэк ошибки вызывается не всегда надёжно, требуется дополнительная проверка.

Расширенные примеры динамической загрузки скриптов

1. Последовательная загрузка нескольких скриптов с помощью цепочек Promise

Пример

function loadScriptPromise(url) {
  return new Promise(function(resolve, reject) {
    var script = document.createElement('script');
    script.src = url;
    script.onload = resolve;
    script.onerror = function() {
      reject(new Error('Не удалось загрузить ' + url));
    };
    document.head.appendChild(script);
  });
}

// Загрузка скриптов по порядку
loadScriptPromise('first.js')
  .then(function() {
    console.log('first.js загружен');
    return loadScriptPromise('second.js');
  })
  .then(function() {
    console.log('second.js загружен');
  })
  .catch(function(err) {
    console.error(err);
  });

Результат:

first.js загружен
second.js загружен

2. Загрузка скрипта с проверкой на уже существующий элемент

Пример

function loadScriptOnce(url, callback) {
  // Проверяем, есть ли уже скрипт с таким src
  var existing = document.querySelector('script[src="' + url + '"]');
  if (existing) {
    // Если скрипт уже загружен, вызываем колбэк сразу
    if (typeof callback === 'function') callback();
    return;
  }
  var script = document.createElement('script');
  script.src = url;
  script.onload = callback;
  script.onerror = function() {
    console.error('Ошибка загрузки: ' + url);
  };
  document.head.appendChild(script);
}

loadScriptOnce('lib.js', function() {
  console.log('lib.js готов к использованию');
});

Результат:

lib.js готов к использованию

3. Загрузка скрипта с таймаутом

Пример

function loadScriptWithTimeout(url, timeout, callback) {
  var script = document.createElement('script');
  script.src = url;
  var timer = setTimeout(function() {
    script.onload = null; // отключаем колбэк
    console.error('Таймаут загрузки: ' + url);
  }, timeout);
  script.onload = function() {
    clearTimeout(timer);
    if (typeof callback === 'function') callback();
  };
  script.onerror = function() {
    clearTimeout(timer);
    console.error('Ошибка загрузки: ' + url);
  };
  document.head.appendChild(script);
}

loadScriptWithTimeout('slow.js', 5000, function() {
  console.log('slow.js загружен вовремя');
});

4. Динамическая загрузка модульных скриптов с использованием import() и условной логикой

Пример

// Загрузка модуля в зависимости от условия
if (window.innerWidth < 768) {
  import('./mobile-module.js')
    .then(m => m.initMobile())
    .catch(e => console.warn('Мобильный модуль не загружен', e));
} else {
  import('./desktop-module.js')
    .then(m => m.initDesktop())
    .catch(e => console.warn('Десктопный модуль не загружен', e));
}

5. Загрузка скрипта с добавлением параметров для обхода кэша

Пример

function loadScriptWithCacheBust(url, callback) {
  var separator = url.indexOf('?') > -1 ? '&' : '?';
  var bustedUrl = url + separator + '_=' + Date.now();
  var script = document.createElement('script');
  script.src = bustedUrl;
  script.onload = callback;
  document.head.appendChild(script);
}

loadScriptWithCacheBust('https://api.example.com/widget.js', function() {
  console.log('Виджет загружен');
});

Результат:

Виджет загружен

6. Загрузка скрипта и выполнение после загрузки всех скриптов (Promise.all)

Пример

var urls = ['lib1.js', 'lib2.js', 'lib3.js'];
var promises = urls.map(function(url) {
  return loadScriptPromise(url); // используем функцию из примера 1
});

Promise.all(promises)
  .then(function() {
    console.log('Все скрипты загружены и выполнены');
    // Здесь можно инициализировать приложение
  })
  .catch(function(err) {
    console.error('Ошибка при загрузке одного из скриптов', err);
  });

7. Работа с ошибками: повторная попытка загрузки

Пример

function loadScriptWithRetry(url, retries, callback) {
  var attempt = 0;
  function tryLoad() {
    attempt++;
    var script = document.createElement('script');
    script.src = url;
    script.onload = function() {
      if (typeof callback === 'function') callback();
    };
    script.onerror = function() {
      if (attempt < retries) {
        console.log('Попытка ' + attempt + ' не удалась, повтор через 2 секунды');
        setTimeout(tryLoad, 2000);
      } else {
        console.error('Не удалось загрузить скрипт после ' + retries + ' попыток');
      }
    };
    document.head.appendChild(script);
  }
  tryLoad();
}

loadScriptWithRetry('unstable.js', 3, function() {
  console.log('Скрипт загружен');
});

8. Загрузка inline-скрипта (код в виде строки) и его выполнение

Пример

var code = 'window.hello = function() { console.log("Hello from inline"); }';
// Выполнение через скрытый script элемент
var script = document.createElement('script');
script.textContent = code;
document.head.appendChild(script);
// Теперь доступна функция window.hello
window.hello();

Результат:

Hello from inline

Подключение скрипта через AJAX - comments

En
Js script script src ajax (javascript)