Как включить файл JavaScript в другой файл JavaScript? - PullRequest
4674 голосов
/ 04 июня 2009

Есть ли в JavaScript что-то похожее на @import в CSS, которое позволяет включать файл JavaScript в другой файл JavaScript?

Ответы [ 55 ]

4048 голосов
/ 04 июня 2009

В старых версиях JavaScript не было импорта, включения или требования, поэтому было разработано много разных подходов к этой проблеме.

Но с 2015 года (ES6) в JavaScript появился стандарт ES6 modules для импорта модулей в Node.js, который также поддерживается большинством современных браузеров .

Для совместимости со старыми браузерами можно использовать инструменты build и / или * .

Модули ES6

Модули ECMAScript (ES6) поддерживаются в Node.js начиная с версии 8.5 с флагом --experimental-modules. Все задействованные файлы должны иметь расширение .mjs.

// module.mjs
export function hello() {
  return "Hello";
}
// main.mjs
import { hello } from 'module'; // or './module'
let val = hello();  // val is "Hello";

ECMAScript модули в браузерах

Браузеры поддерживают загрузку модулей ECMAScript напрямую (не требуются такие инструменты, как Webpack) с Safari 10.1, Chrome 61, Firefox 60 и Edge 16. Проверьте текущую поддержку на caniuse .

<script type="module">
  import { hello } from './hello.mjs';
  hello('world');
</script>
// hello.mjs
export function hello(text) {
  const div = document.createElement('div');
  div.textContent = `Hello ${text}`;
  document.body.appendChild(div);
}

Подробнее на https://jakearchibald.com/2017/es-modules-in-browsers/

Динамический импорт в браузерах

Динамический импорт позволяет скрипту загружать другие скрипты по мере необходимости:

<script type="module">
  import('hello.mjs').then(module => {
      module.hello('world');
    });
</script>

Подробнее на https://developers.google.com/web/updates/2017/11/dynamic-import

Node.js требуется

Старый стиль импорта модулей, все еще широко используемый в Node.js, это система module.exports / require .

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}
// server.js
const myModule = require('./mymodule');
let val = myModule.hello(); // val is "Hello"   

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

AJAX Загрузка

Вы можете загрузить дополнительный скрипт с помощью вызова AJAX, а затем использовать eval для его запуска. Это самый простой способ, но он ограничен вашим доменом из-за модели безопасности песочницы JavaScript. Использование eval также открывает путь к ошибкам, взломам и проблемам безопасности.

Загрузка данных

Как и Dynamic Imports, вы можете загрузить один или несколько сценариев с помощью вызова fetch, используя обещания для контроля порядка выполнения зависимостей сценариев, используя библиотеку Fetch Inject :

fetchInject([
  'https://cdn.jsdelivr.net/momentjs/2.17.1/moment.min.js'
]).then(() => {
  console.log(`Finish in less than ${moment().endOf('year').fromNow(true)}`)
})

JQuery Загрузка

Библиотека jQuery обеспечивает загрузку в одну строку :

$.getScript("my_lovely_script.js", function() {
   alert("Script loaded but not necessarily executed.");
});

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

Вы можете добавить тег HTML с URL-адресом скрипта в HTML. Чтобы избежать накладных расходов в jQuery, это идеальное решение.

Скрипт может даже находиться на другом сервере. Кроме того, браузер оценивает код. Тег <script> может быть вставлен в веб-страницу <head> или вставлен непосредственно перед закрывающим тегом </body>.

Вот пример того, как это может работать:

function dynamicallyLoadScript(url) {
    var script = document.createElement("script");  // create a script DOM node
    script.src = url;  // set its src to the provided URL

    document.head.appendChild(script);  // add it to the end of the head section of the page (could change 'head' to 'body' to add it to the end of the body section instead)
}

Эта функция добавит новый тег <script> в конец раздела заголовка страницы, где атрибуту src задается URL-адрес, который передается функции в качестве первого параметра.

Оба эти решения обсуждаются и иллюстрируются в JavaScript Madness: динамическая загрузка скриптов .

Обнаружение, когда скрипт был выполнен

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

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

Например: 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 . И это работает! Смешение ...

Так что с этим делать?

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

function loadScript(url, callback)
{
    // Adding the script tag to the head as suggested before
    var head = document.head;
    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 whatever you want
};

Затем вы запускаете все это:

loadScript("my_lovely_script.js", myPrettyCode);

Обратите внимание, что сценарий может выполняться после загрузки DOM или раньше, в зависимости от браузера и от того, включена ли строка script.async = false;. Есть отличная статья о загрузке Javascript в целом , в которой обсуждается это.

Слияние исходного кода / предварительная обработка

Как уже упоминалось в верхней части этого ответа, многие разработчики используют в своих проектах такие инструменты сборки, как Parcel, Webpack или Babel, что позволяет им использовать предстоящий синтаксис JavaScript, обеспечивать обратную совместимость для старых браузеров, объединять файлы , минимизировать, выполнять разбиение кода и т. д.

541 голосов
/ 08 июня 2012

Если кто-то ищет что-то более продвинутое, попробуйте RequireJS . Вы получите дополнительные преимущества, такие как управление зависимостями, улучшенный параллелизм и избежание дублирования (то есть получение сценария более одного раза).

Вы можете записать свои файлы JavaScript в «модули», а затем ссылаться на них как на зависимости в других скриптах. Или вы можете использовать RequireJS как простое решение «иди и получи этот скрипт».

Пример:

Определить зависимости как модули:

некоторые-dependency.js

define(['lib/dependency1', 'lib/dependency2'], function (d1, d2) {

     //Your actual script goes here.   
     //The dependent scripts will be fetched if necessary.

     return libraryObject;  //For example, jQuery object
});

creation.js - это ваш "основной" файл JavaScript, который зависит от some-dependency.js

require(['some-dependency'], function(dependency) {

    //Your script goes here
    //some-dependency.js is fetched.   
    //Then your script is executed
});

Отрывок из GitHub README:

RequireJS загружает как простые файлы JavaScript, так и более определенные модули. Он оптимизирован для использования в браузере, в том числе в Интернете. Рабочий, но его можно использовать в других средах JavaScript, таких как Носорог и Узел Он реализует API асинхронного модуля.

RequireJS использует простые скриптовые теги для загрузки модулей / файлов, поэтому он должен Позвольте для легкой отладки. Может использоваться просто для загрузки существующих Файлы JavaScript, поэтому вы можете добавить его в существующий проект без необходимость переписать ваши файлы JavaScript.

...

177 голосов
/ 28 апреля 2011

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

Вам необходимо использовать jQuery.append() в элементе <head> вашей страницы, то есть:

$("head").append('<script type="text/javascript" src="' + script + '"></script>');

Однако этот метод также имеет проблему: если в импортированном файле JavaScript возникает ошибка, Firebug (а также консоль ошибок Firefox и Chrome Developer Tools также сообщат) его место неправильно, что является большой проблемой, если вы используете Firebug для отслеживания ошибок JavaScript (я так и делаю). Firebug по какой-то причине просто не знает о недавно загруженном файле, поэтому, если в этом файле происходит ошибка, он сообщает, что она произошла в вашем основном файле HTML , и у вас будут проблемы с поиском действительного причина ошибки.

Но если это не проблема для вас, тогда этот метод должен работать.

На самом деле я написал плагин jQuery с именем $. Import_js () , который использует этот метод:

(function($)
{
    /*
     * $.import_js() helper (for JavaScript importing within JavaScript code).
     */
    var import_js_imported = [];

    $.extend(true,
    {
        import_js : function(script)
        {
            var found = false;
            for (var i = 0; i < import_js_imported.length; i++)
                if (import_js_imported[i] == script) {
                    found = true;
                    break;
                }

            if (found == false) {
                $("head").append('<script type="text/javascript" src="' + script + '"></script>');
                import_js_imported.push(script);
            }
        }
    });

})(jQuery);

Итак, все, что вам нужно сделать для импорта JavaScript, это:

$.import_js('/path_to_project/scripts/somefunctions.js');

Я также сделал простой тест для этого в Пример .

Включает файл main.js в основном HTML, а затем сценарий в main.js использует $.import_js() для импорта дополнительного файла с именем included.js, который определяет эту функцию:

function hello()
{
    alert("Hello world!");
}

И сразу после включения included.js вызывается функция hello(), и вы получаете предупреждение.

(Этот ответ является ответом на комментарий e-sat).

141 голосов
/ 08 сентября 2011

Другой способ, который, на мой взгляд, намного чище, - сделать синхронный Ajax-запрос вместо использования тега <script>. Что также относится к Node.js .

Вот пример использования jQuery:

function require(script) {
    $.ajax({
        url: script,
        dataType: "script",
        async: false,           // <-- This is the key
        success: function () {
            // all good...
        },
        error: function () {
            throw new Error("Could not load script " + script);
        }
    });
}

Затем вы можете использовать его в своем коде, как обычно, используя include:

require("/scripts/subscript.js");

И сможете вызвать функцию из необходимого скрипта в следующей строке:

subscript.doSomethingCool(); 
91 голосов
/ 03 июля 2012

Есть хорошие новости для вас. Очень скоро вы сможете легко загрузить код JavaScript. Это станет стандартным способом импорта модулей кода JavaScript и станет частью самого ядра JavaScript.

Вам просто нужно написать import cond from 'cond.js';, чтобы загрузить макрос с именем cond из файла cond.js.

Таким образом, вам не нужно полагаться ни на какую инфраструктуру JavaScript, и при этом вам не нужно явно делать Ajax вызовы.

См .:

82 голосов
/ 04 июня 2009

Можно динамически генерировать тег JavaScript и добавлять его в документ HTML из другого кода JavaScript. Это загрузит целевой файл JavaScript.

function includeJs(jsFilePath) {
    var js = document.createElement("script");

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

    document.body.appendChild(js);
}

includeJs("/path/to/some/file.js");
63 голосов
/ 17 апреля 2015

Заявление import в ECMAScript 6.

Синтаксис

import name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import name , { member [ , [...] ] } from "module-name";
import "module-name" as name;
53 голосов
/ 04 июня 2009

Может быть, вы можете использовать эту функцию, которую я нашел на этой странице Как включить файл JavaScript в файл JavaScript? :

function include(filename)
{
    var head = document.getElementsByTagName('head')[0];

    var script = document.createElement('script');
    script.src = filename;
    script.type = 'text/javascript';

    head.appendChild(script)
}
47 голосов
/ 11 декабря 2013

Вот синхронная версия без jQuery :

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

Обратите внимание, что для получения этого рабочего междомена сервер должен будет установить заголовок allow-origin в своем ответе.

43 голосов
/ 23 января 2010

Я только что написал этот код JavaScript (используя Прототип для DOM манипуляции):

var require = (function() {
    var _required = {};
    return (function(url, callback) {
        if (typeof url == 'object') {
            // We've (hopefully) got an array: time to chain!
            if (url.length > 1) {
                // Load the nth file as soon as everything up to the
                // n-1th one is done.
                require(url.slice(0, url.length - 1), function() {
                    require(url[url.length - 1], callback);
                });
            } else if (url.length == 1) {
                require(url[0], callback);
            }
            return;
        }
        if (typeof _required[url] == 'undefined') {
            // Haven't loaded this URL yet; gogogo!
            _required[url] = [];

            var script = new Element('script', {
                src: url,
                type: 'text/javascript'
            });
            script.observe('load', function() {
                console.log("script " + url + " loaded.");
                _required[url].each(function(cb) {
                    cb.call(); // TODO: does this execute in the right context?
                });
                _required[url] = true;
            });

            $$('head')[0].insert(script);
        } else if (typeof _required[url] == 'boolean') {
            // We already loaded the thing, so go ahead.
            if (callback) {
                callback.call();
            }
            return;
        }

        if (callback) {
            _required[url].push(callback);
        }
    });
})();

Использование:

<script src="prototype.js"></script>
<script src="require.js"></script>
<script>
    require(['foo.js','bar.js'], function () {
        /* Use foo.js and bar.js here */
    });
</script>

Суть: http://gist.github.com/284442.

...