May 2nd, 2021

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

Начал читать тринадцатый раздел («Модули») первой части («Язык программирования JavaScript») учебника по JavaScript, а именно подраздел 13.1 «Модули, введение».

Цитата оттуда:

Модуль — это просто файл. Один скрипт — это один модуль.

Модули могут загружать друг друга и использовать директивы export и import, чтобы обмениваться функциональностью, вызывать функции одного модуля из другого:

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


Тут у меня сразу возник вопрос. Зачем нужны ключевые слова export и import, если ранее в учебнике уже неоднократно приводились примеры написания и использования небольших программ, состоящих из нескольких файлов, которые обходятся без использования этих ключевых слов?

Например, еще в подразделе 2.1 «Привет, мир!» учебника рассказано о том, как в HTML-страницу можно вставить несколько скриптов из разных файлов с помощью тега <script></script>:

<script src="script1.js"></script>
<script src="script2.js"></script>

При этом в одном из файлов можно объявить функции (будем считать этот файл библиотекой функций), а в другом вызвать эти функции на исполнение без всяких дополнительных ключевых слов export и import. Например:

Текст файла script1.js:
function sayHi(user) {
    alert(`Привет, ${user}!`);
}

Текст файла script2.js:
sayHi("Илья");

Как я понимаю, ключевые слова export и import, во-первых, могут пригодиться в таком окружении, в котором HTML-теги недоступны. Например, программная платформа Node.js превращает JavaScript из узкоспециализированного языка (в браузерном окружении) в язык общего назначения, на котором можно писать даже программы, работающие как приложения операционной системы. (Это в качестве предположения, так как я не в курсе, как пишут многофайловые программы в Node.js. Я пока еще не работал с этой платформой.)

Во-вторых, ключевые слова export и import становятся нужны, если на HTML-страницу мы вставляем только один скрипт, а уже этот скрипт вызывает функции из другого скрипта, который не был вставлен на эту HTML-страницу с помощью тега <script></script>. Например, что-то вроде следующего:

Текст файла test.html:
...
<script src="script2.js" type="module"></script>
...
Тут, кстати, следует отметить атрибут type="module", без него будет выдана ошибка: «Uncaught SyntaxError: Cannot use import statement outside a module».

Текст файла script1.js:
export function sayHi(user) {
    alert(`Привет, ${user}!`);
}

Текст файла script2.js:
import {sayHi} from "./script1.js";

sayHi("Илья");

Продолжение: «JavaScript: Политика одинакового источника и CORS».

JavaScript: Политика одинакового источника и CORS

Начало в предыдущем посте: «JavaScript: модули, зачем нужен экспорт и импорт».

Примеры и задачи из первых двенадцати разделов учебника по JavaScript, до начала изучения модулей, я набирал в текстовом редакторе «Notepad++» и сохранял в текстовых файлах с расширениями .html (HTML-документ) и .js (отдельные скрипты) на рабочем столе (на моем компьютере установлена операционная система «Windows 10 Pro»). После этого я открывал HTML-документ в браузере и тестировал написанные мною скрипты.

В предыдущем посте я, чтобы посмотреть, как можно в языке JavaScript работать с модулями, написал такой HTML-документ и пару скриптов:

Текст файла test.html:
...
<script src="script2.js" type="module"></script>
...

Текст файла script1.js:
export function sayHi(user) {
    alert(`Привет, ${user}!`);
}

Текст файла script2.js:
import {sayHi} from "./script1.js";

sayHi("Илья");

Открыв, как обычно, файл test.html, находящийся на рабочем столе, в браузере, я получил следующую ошибку (ее можно увидеть в консоли разработчика): «Access to script at 'file:///C:/Users/%D0%98%D0%BB%D1%8C%D1%8F/Desktop/script2.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome-extension, edge, https, chrome-untrusted.» (я тестирую скрипты в браузере «Microsoft Edge», который создан на движке «Chromium»).

Оказалось, что существует так называемая «Политика (принцип) одинакового источника» (по-английски «Same-origin policy» или сокращенно «SOP»):

https://ru.wikipedia.org/wiki/Правило_ограничения_домена
https://developer.mozilla.org/ru/docs/Web/Security/Same-origin_policy

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

Упомянутый в тексте ошибки CORS (эта аббревиатура расшифровывается как «Cross-origin resource sharing», что по-русски означает «совместное использование ресурсов между разными источниками») — это механизм (технология современных браузеров), который, как я понял, позволяет владельцу каждого источника (сервера) настроить политику, регулирующую доступ к скриптам данного источника (можно разрешить доступ всем клиентам, либо только некоторым клиентам, либо всем запретить).

https://ru.wikipedia.org/wiki/Cross-origin_resource_sharing
https://developer.mozilla.org/ru/docs/Web/HTTP/CORS

Чтобы источники двух разных скриптов считались одним и тем же источником, у них должны совпадать одновременно протокол, домен и порт (по-английски соответственно «scheme», «host» и «port»).

Как видно по тексту вышеупомянутой ошибки, в моем случае механизм CORS может сработать и открыть доступ к скрипту script1.js из скрипта script2.js только через перечисленные протоколы, в том числе HTTP. Несмотря на то, что оба файла находятся в одном и том же месте, из-за протокола file:/// считается, что они находятся в разных местах (это всё ради обеспечения безопасности). Об этом казусе можно почитать тут:

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSRequestNotHttp

В интернетах пишут, что якобы имеются некие способы обхода (или отключения) механизма CORS. Я считаю, что раз такой механизм придумали, значит это «ж-ж-ж-ж» неспроста. Всё ради безопасности. Следовательно, все эти обходы и отключения — не наш метод. Как говорится, одно дело делаешь, другого не порть.

Поэтому я решил обеспечить браузеру (его механизму CORS) протокол HTTP, раз уж это требуется. Тут есть два варианта.

Во-первых, для этого можно загрузить мою страничку test.html, скрипты script1.js и script2.js на какой-нибудь веб-хостинг и запустить их оттуда.

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

Я выбрал второй вариант. Продолжение: «JavaScript: локальный веб-сервер из набора IIS».

Дополнение от 02.10.2021 г.: подробнее про работу с механизмом CORS (про разные уровни кросс-доменного доступа) можно прочесть в подразделе 5.3 «Загрузка ресурсов: onload и onerror» второй части обсуждаемого учебника.