Биздин тиркеме өскөн сайын, биз аны адатта "модулдар" деп аталган көптөгөн файлдарга бөлгүбүз келет. Модуль адатта функциялары бар классты же китепкананы камтыйт.
Узак убакыт бою JavaScript тил деңгээлинде модулдун синтаксиси жок болчу. Биринчи сценарийлер кичинекей жана жөнөкөй болгондуктан, бул көйгөй болгон жок. Модулдардын кереги жок болчу.
Бирок убакыттын өтүшү менен скрипттер барган сайын татаалдашып кетти, ошондуктан коомчулук кодду модулдарга уюштуруунун бир нече варианттарын ойлоп тапты. Модулдарды динамикалык жүктөө үчүн китепканалар бар.
Мисалы:
- AMD эң эски модулдук системалардын бири, башында require.js китепканасы менен ишке ашырылган .
- CommonJS Node.js сервери үчүн курулган модулдук система.
- UMD дагы бир модулдук система, ал AMD жана CommonJS менен шайкеш универсалдуу катары сунушталат.
Азыр алардын баары акырындык менен окуянын бир бөлүгү болуп баратат, бирок аларды эски сценарийлерде табууга болот.
Тил деңгээлиндеги модулдук система JavaScript стандартында 2015-жылы пайда болгон жана убакыттын өтүшү менен өнүгүп келген. Учурда аны көпчүлүк браузерлер жана Node.js колдойт. Кийинки, биз аны изилдейбиз.
Модуль деген эмне?
Модуль бул жөн гана файл. Бир скрипт бир модуль болуп саналат.
Модульдер бири-бирин жүктөй алышат жана директиваларды колдоно алышат export
жана import
функцияларды алмашуу үчүн бир модулдун функцияларын башкасынан чакыра алышат:
export
учурдагы модулдан тышкары жеткиликтүү болушу керек болгон өзгөрмөлөрдү жана функцияларды белгилейт.import
башка модулдардан функцияларды импорттоого мүмкүндүк берет.
Мисалы, бизде sayHi.js
функцияны экспорттоочу файл болсо:
@A@// 📁 sayHi.js
export function sayHi(user) {
alert(`Hello, ${user}!`);
}@A@
…Андан кийин башка файл аны импорттоп, колдоно алат:
@A@// 📁 main.js
import {sayHi} from './sayHi.js';
alert(sayHi); // function...
sayHi('John'); // Hello, John!@A@
Директива модулду учурдагы файлга салыштырмалуу import
жолдо жүктөйт жана экспорттолгон функцияны тиешелүү өзгөрмөгө жазат../sayHi.js
sayHi
Мисалды браузерде иштетели.
Модульдер бир катар атайын ачкыч сөздөрдү колдогондуктан жана алар бир катар өзгөчөлүктөргө ээ болгондуктан, браузерге скрипт бул <script type="module">
.
Бул сыяктуу:
@A@<!doctype html>
<script type="module">
import {sayHi} from './say.js';
document.body.innerHTML = sayHi('John');
</script>@A@
Браузер автоматтык түрдө импорттолгон модулду (жана ал импорттогондорду, керек болсо) жүктөп алып иштетет, андан кийин скриптти иштетет.
Протокол аркылуу веб-баракчаны локалдык түрдө ачууга аракет кылсаңыз , директивалар иштебей турганын file://
көрөсүз . Модуль тестирлөө үчүн статикалык серверimport/export
сыяктуу жергиликтүү веб серверди колдонуңуз же VS Code үчүн Live Server кеңейтүүсү сыяктуу редакторуңуздун "тирүү сервер" мүмкүнчүлүктөрүн колдонуңуз .
Модулдардын негизги өзгөчөлүктөрү
Модулдар "кадимки" скрипттерден эмнеси менен айырмаланат?
Браузерде да, сервер тарабында да JavaScriptте иштеген негизги мүмкүнчүлүктөр жана функциялар бар.
Ар дайым "катуу колдонуу"
Модулдар ар дайым режимди колдонушат use strict
. Мисалы, жарыяланбаган өзгөрмөгө дайындоо катага алып келет.
@A@<script type="module">
a = 5; // ошибка
</script>@A@
Өзүнүн өзгөрмө чөйрөсү
Ар бир модулдун өзүнүн масштабы бар. Башкача айтканда, модулда жарыяланган өзгөрмөлөр жана функциялар башка скрипттерде көрүнбөйт.
Төмөнкү мисал 2 скриптти импорттоду жана ичинде жарыяланган hello.js
өзгөрмөнү колдонууга аракет кылат . Натыйжада, ката:user
user.js
@A@<!doctype html>
<script type="module" src="user.js"></script>
<script type="module" src="hello.js"></script>@A@
Модулдар тышкы колдонууга арналган функцияларды экспорттоого тийиш. Жана башка модулдар аны импорттой алат.
Ошентип, биз глобалдык өзгөрмөлөргө таянгандын ордуна ага керектүү функцияларды импорттообуз user.js
керек .hello.js
Туура вариант:
@A@import {user} from './user.js';
document.body.innerHTML = user; // John@A@
Браузер ошондой эле ар бир скрипт үчүн көз карандысыз масштабга ээ <script type="module">
:
@A@<script type="module">
// Переменная доступна только в этом модуле
let user = "John";
</script>
<script type="module">
alert(user); // Error: user is not defined
</script>@A@
Эгерде биз жалпы беттеги глобалдык өзгөрмө жасашыбыз керек болсо, анда биз аны объектке ачык ыйгарсак болот window
, анда биз өзгөрмөнүн маанисин шилтеме менен ала алабыз window.user
. Бирок бул жүйөлүү себепти талап кылган өзгөчөлүк болушу керек.
Модулдагы код импортто бир гана жолу аткарылат
Эгерде бир эле модул бир нече жерде колдонулса, анда анын коду бир гана жолу аткарылат, андан кийин экспорттолгон функция бардык импорттоочуларга өткөрүлүп берилет.
Бул модулдар кантип иштээрин түшүнүү үчүн абдан маанилүү. Келгиле, кээ бир мисалдарды карап көрөлү.
Биринчиден, модулду иштетүүдө терс таасирлер болсо, мисалы билдирүү, модулду бир нече жерде импорттоо аны бир гана жолу көрсөтөт - биринчи импортто:
@A@// 📁 alert.js
alert("Модуль выполнен!");
// Импорт одного и того же модуля в разных файлах
// 📁 1.js
import `./alert.js`; // Модуль выполнен!
// 📁 2.js
import `./alert.js`; // (ничего не покажет)@A@
Иш жүзүндө, модулдук коддун милдети, адатта, инициализациялоо, ички маалымат структураларын түзүү жана бир нерсенин көп жолу колдонулушун кааласак, анда аны экспорттойбуз.
Эми өнүккөн мисал үчүн.
Келгиле, модуль объектти экспорттойт деп элестетип көрөлү:
@A@// 📁 admin.js
export let admin = {
name: "John"
};@A@
Эгерде модуль бир нече файлдарда импорттолсо, анда модулдун коду бир гана жолу аткарылат, объект admin
түзүлөт жана андан кийин бардык импорттоочуларга өткөрүлүп берилет.
Бардык импорттоочулар бир объектти алышат admin
:
@A@// 📁 1.js
import {admin} from './admin.js';
admin.name = "Pete";
// 📁 2.js
import {admin} from './admin.js';
alert(admin.name); // Pete
// Оба файла, 1.js и 2.js, импортируют один и тот же объект
// Изменения, сделанные в 1.js, будут видны в 2.js@A@
Модуль бир гана жолу аткарыларын дагы бир жолу белгилей кетүү керек. Экспорт түзүлүп, андан кийин бардык импорттоочуларга өткөрүлүп берилет, андыктан объектте бир нерсе өзгөрсө admin
, анда башка модулдар да бул өзгөрүүлөрдү көрөт.
Бул аракет модулдарды биринчи импорттоодо конфигурациялоого мүмкүндүк берет. Биз анын касиеттерин бир жолу орното алабыз жана келечекте импорттоодо ал конфигурацияланган болот.
Мисалы, модул admin.js
белгилүү бир функцияны камсыз кылат, бирок эсептик дайындардын объектке admin
сырттан берилишин күтөт:
@A@// 📁 admin.js
export let admin = { };
export function sayHi() {
alert(`Ready to serve, ${admin.name}!`);
}@A@
init.js
Колдонмобуздун биринчи скриптинде биз admin.name
. Андан кийин бардыгы, анын ичинде төмөнкүдөн жасалган чалууларды көрөт admin.js
:
@A@// 📁 init.js
import {admin} from './admin.js';
admin.name = "Pete";@A@
Башка модул да көрөт admin.name
:
@A@// 📁 other.js
import {admin, sayHi} from './admin.js';
alert(admin.name); // Pete
sayHi(); // Ready to serve, Pete!@A@
import.meta
Объект import.meta
учурдагы модул жөнүндө маалыматты камтыйт.
мазмуну айлана-чөйрөгө көз каранды. Браузерде ал скриптке шилтемени, же эгер модул HTMLге кыстарылган болсо, учурдагы веб-баракчага шилтемени камтыйт:
@A@<script type="module">
alert(import.meta.url); // ссылка на html страницу для встроенного скрипта
</script>@A@
Модулда "бул" аныкталган эмес
Бул анча-мынча өзгөчөлүк, бирок толук болушу үчүн, биз бул жөнүндө сөз кылышыбыз керек.
Жогорку деңгээлдеги модулда this
аныкталбаган .
Модулдук эмес скрипттер менен салыштырыңыз, this
глобалдык объект бар:
@A@<script>
alert(this); // window
</script>
<script type="module">
alert(this); // undefined
</script>@A@
Браузердеги өзгөчөлүктөр
type="module"
Кадимки скрипттерге салыштырмалуу скрипттердин бир нече башка серепчи спецификалык өзгөчөлүктөрү бар .
Эгер сиз муну биринчи жолу окуп жатсаңыз, же браузерлерде плагиндерди колдонбосоңуз, бул бөлүмдү азыр өткөрүп жиберсеңиз болот.
Модулдар кийинкиге калтырылды
Модульдер ар дайым кийинкиге калтырылган режимде аткарылат, атрибуту бар скрипттер сыяктуу ( Скрипттер: асинхрондоштуруу, кийинкиге калтырууdefer
бөлүмүндө сүрөттөлгөн ). Бул тышкы жана орнотулган скрипт модулдарына да тиешелүү.
Башкача айтканда:
- сыяктуу тышкы модулдарды жүктөө
<script type="module" src="...">
, HTML иштетүүгө бөгөт койбойт. - модулдар тез жүктөлсө дагы, HTML документинин толук жүктөлүшүн күтүшөт жана андан кийин гана алар аткарылат.
- скрипттердин салыштырмалуу тартиби сакталат: документте биринчи келген сценарийлер биринчи аткарылат.
Кошумча эффект катары, модулдар ар дайым толук жүктөлгөн HTML баракчасын, анын ичинде астындагы элементтерди көрүшөт.
Мисалы:
@A@<script type="module">
alert(typeof button); // object: скрипт может 'видеть' кнопку под ним
// так как модули являются отложенными, то скрипт начнёт выполнятся только после полной загрузки страницы
</script>@A@
Сравните с обычным скриптом ниже:
@A@<script>
alert(typeof button); // Ошибка: кнопка не определена, скрипт не видит элементы под ним
// обычные скрипты запускаются сразу, не дожидаясь полной загрузки страницы
</script>@A@
@A@<button id="button">Кнопка</button>@A@
Көңүл буруңуз: экинчи скрипт биринчисине чейин аткарылат! Ошондуктан, биз биринчи көрөбүз undefined
, анан object
.
Себеби, модулдар барак толук жүктөлгөндөн кийин иштей баштайт. Кадимки скрипттер дароо иштейт, ошондуктан биз биринчи кезекте кадимки скрипттен кабарды көрөбүз.
Модулдарды колдонууда, модулдар аткарылганга чейин жана JavaScript тиркемеси иштөөгө даяр болгонго чейин HTML барагы браузер тарабынан көрсөтүлөрүн эстен чыгарбашыбыз керек. Кээ бир функциялар иштебей калышы мүмкүн. Келген адамды чаташтырбоо үчүн "жүктөө индикаторун" же башка нерсени коюшубуз керек.
асинхрондук атрибуту ички скрипттерде иштейт
Модулдук эмес скрипттер үчүн атрибут async
тышкы скрипттерде гана иштейт. Аны менен скрипттер даяр болоор замат иштейт, алар башка скрипттерди же HTML документин күтпөйт.
Модулдар үчүн атрибут async
каалаган скрипттерде иштейт.
Мисалы, төмөндөгү скриптте бар async
, ошондуктан ал башка скрипттерди күтпөстөн, жүктөлгөндөн кийин дароо аткарылат.
./analytics.js
HTML документи жүктөлө элек болсо же башка скрипттер дагы эле жүктөлүп жатса да, скрипт импорттолот ( жүктөйт) жана ал даяр болгондо дароо иштейт.
Бул модуль эч нерсеге тиешеси жок болгондо абдан пайдалуу, мисалы, эсептегичтер, жарнамалар, окуяны иштетүүчүлөр үчүн.
@A@<!-- загружаются зависимости (analytics.js) и скрипт запускается -->
<!-- модуль не ожидает загрузки документа или других тэгов <script> -->
<script async type="module">
import {counter} from './analytics.js';
counter.count();
</script>@A@
Тышкы скрипттер
Атрибуту бар тышкы скрипттердин type="module"
эки айырмасы бар:
-
Бир эле атрибутка ээ тышкы скрипттер
src
бир гана жолу иштетилет:@A@<!-- скрипт my.js загрузится и будет выполнен только один раз --> <script type="module" src="my.js"></script> <script type="module" src="my.js"></script>@A@
-
Башка доменден жүктөлгөн тышкы скрипт CORS аталыштарын көрсөтүүнү талап кылат . Башкача айтканда, эгерде модулдук скрипт башка доменден жүктөлсө, анда алыскы сервер
Access-Control-Allow-Origin
скрипт жүктөөгө уруксат берилгенин көрсөтүүчү башты коюшу керек.@A@<!-- another-site.com должен указать заголовок Access-Control-Allow-Origin --> <!-- иначе, скрипт не выполнится --> <script type="module" src="http://another-site.com/their.js"></script>@A@
Бул жакшыраак демейки коопсуздукту камсыз кылат.
Жылаңач модулдарга жол берилбейт
Браузерде ал import
модулга салыштырмалуу же абсолюттук жолду камтышы керек. Жолу жок модулдар "жылаңач" модулдар деп аталат. Аларга уруксат берилбейт import
.
Мисалы, бул import
туура эмес:
@A@import {sayHi} from 'sayHi'; // Ошибка, "голый" модуль
// путь должен быть, например './sayHi.js' или абсолютный@A@
Башка чөйрөлөр, мисалы, Node.js, жылаңач модулдарга жолдору жок жол берет, анткени алардын мындай модулдар менен кантип иштөө жана аларды кайдан табуу боюнча өз эрежелери бар. Бирок браузерлер азырынча жылаңач модулдарды колдобойт.
Шайкештик, "nomodule"
Эски браузерлер түшүнүшпөйт type="module"
. Белгисиз атрибуту бар скрипттерге type
жөн гана көңүл бурулбайт. Биз алар үчүн атрибутун колдонуп "резервдик" скрипт түзө алабыз nomodule
:
@A@<script type="module">
alert("Работает в современных браузерах");
</script>
<script nomodule>
alert("Современные браузеры понимают оба атрибута - и type=module, и nomodule, поэтому пропускают этот тег script")
alert("Старые браузеры игнорируют скрипты с неизвестным атрибутом type=module, но выполняют этот.");
</script>@A@
Куруу куралдары
Чыныгы жашоодо браузерлердеги модулдар "чийки" түрүндө сейрек колдонулат. Адатта, биз модулдарды Webpack сыяктуу атайын куралдын жардамы менен бириктирип , анан кодду өндүрүш серверине түртөбүз.
Кранды колдонуунун артыкчылыктарынын бири, ал сизге модулдарды кантип издөөнү көбүрөөк көзөмөлдөөгө мүмкүндүк берет, жылаңач модулдарды жана CSS/HTML модулдары сыяктуу башка көптөгөн "ыңгайлаштырылган" нерселерди колдонууга мүмкүндүк берет.
Ассемблер төмөнкүлөрдү аткарат:
<script type="module">
Биз HTMLге киргизе турган "негизги" модулду алат .- Көз карандылыкты талдайт (импорт, импорт жана башкалар)
import
Бардык модулдар менен бир файлды курат (же бир нече файлдар, муну конфигурациялоого болот), бардыгы иштеши үчүн ассемблерден орнотулган импорт функциясын кайра жазат . HTML/CSS сыяктуу "атайын" модулдун түрлөрү да колдоого алынат.- Процессте коддун башка трансформациялары жана оптималдаштыруулары болушу мүмкүн:
- Жеткиликсиз код алынып салынды.
- Пайдаланылбаган экспорт алынып салынат («дарактар титиреп»).
console
жана сыяктуу дизайнга тиешелүү операторлорdebugger
алынып салынат.- Заманбап JavaScript синтаксисин мурунку стандартка айландырса болот, мисалы, Babel аркылуу окшош функционалдуулук .
- Натыйжадагы файлды кичирейтсе болот (боштуктарды алып салуу, өзгөрмө аттарын кыскараактары менен алмаштыруу ж.б.).
Эгерде биз куруу куралдарын колдонсок, анда алар модулдарды бир же бир нече файлдарга бириктирип, аларды import/export
өздөрүнүн чалуулары менен алмаштырышат. type="module"
Демек, акыркы жыйынды кадимки скрипт катары атрибутсуз туташтырууга болот :
<!-- Предположим, что мы собрали bundle.js, используя например утилиту Webpack -->
<script src="bundle.js"></script>
"Кандай болсо да" модулдары да колдонулушу мүмкүн, ал эми керек болсо коллекторду кийинчерээк конфигурациялоого болот.
Бардыгы
Кыскача айтканда, негизги түшүнүктөр:
- Модуль бул файл. Иштөө үчүн
import/export
сиз браузерлер үчүн атрибутту көрсөтүшүңүз керек<script type="module">
. Модулдар бир катар өзгөчөлүктөргө ээ:- Демейки боюнча кийинкиге калтырылган (кийинкиге калтырылган) аткаруу.
- Async атрибуту саптык скрипттерде иштейт.
- Тышкы модулдарды башка булактан жүктөө үчүн, ал CORS башын коюшу керек.
- Кайталануучу тышкы скрипттерге көңүл бурулбайт.
- Модулдардын өз чөйрөсү бар, функциялар аркылуу алмашууга болот
import/export
. - Модулдар ар дайым камтыйт
use strict
. - Модулдардагы код бир гана жолу аткарылат. Экспорттолуучу функция бир жолу түзүлүп, бардык импорттоочуларга берилет.
Биз модулдарды колдонгондо, ар бир модуль өзүнүн функцияларын ишке ашырат жана аны экспорттойт. Андан кийин биз import
аны керектүү жерде түздөн-түз импорттоо үчүн колдонобуз. Браузер скрипттерди автоматтык түрдө жүктөйт жана талдайт.
Чыныгы жашоодо Webpack көбүнчө иштөө жана башка жакшы нерселер үчүн модулдарды бириктирүү үчүн колдонулат.
Кийинки бөлүмдө биз көбүрөөк мисалдарды жана импорт/экспорт параметрлерин көрөбүз.