Skip to content

Latest commit

 

History

History
342 lines (209 loc) · 14.3 KB

JS_17.md

File metadata and controls

342 lines (209 loc) · 14.3 KB

Еще чуть чуть про кастомные элементы

:defined

CSS псевдо-класс :defined находит любой элемент, который был определён, включая любой стандартный встроенные в браузер элемент и пользовательские элементы (то есть определённые с помощью метода CustomElementRegistry.define()).

/* Находит любой элемент, который был определён */
:defined {
  font-style: italic;
}

/* Выбирает все элементы simple-custom, если пользовательский элемент simple-custom был определён */
simple-custom:defined {
  display: block;
}

ES6 модули

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

Модуль обычно содержит класс или библиотеку с функциями.

Имеется страница index.html:

<!DOCTYPE html>
<html>
  <body>
    <div id="app"></div>
  
    <script type="module" src="index.mjs"></script>
    <!--обратите внимание на тип скрипта "module" -->
  </body>
</html>

Есть файл index.mjs, представляющий собой модуль, подключённый к странице:

import { doSomething } from './utils.mjs';

doSomething('Make me a sandwich');

Этот модуль, в свою очередь, импортирует функцию doSomething из модуля utils.mjs

export const doSomething = message => {
  console.log('No.');
};

Каковы плюсы использования модулей в браузерах?

Это — чистый JavaScript! Никакого конвейера сборки проекта, никакого 400-строчного конфигурационного файла Webpack, никакого Babel, никаких плагинов и пресетов, и никаких дополнительных 45 npm-модулей. Только вы и ваш код.

Итак. Плюсы ES6-модулей — это чистый JavaScript, это отсутствие сборки, конфигурирования проекта, это отказ от ставших ненужными зависимостей.

А что ещё?

Есть же что-то ещё?

Нет, больше ничего.

Что такое модуль?

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

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

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

  • import позволяет импортировать функциональность из других модулей.

Например, если у нас есть файл sayHi.js, который экспортирует функцию

// sayHi.js
export function sayHi(user) {
  alert(`Hello, ${user}!`);
}

…Тогда другой файл может импортировать её и использовать:

// main.js
import {sayHi} from './sayHi.js';

alert(sayHi); // function...
sayHi('John'); // Hello, John!

Директива import загружает модуль по пути ./sayHi.js относительно текущего файла и записывает экспортированную функцию sayHi в соответствующую переменную.

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

  <script type="module">.

Основные возможности модулей

Чем отличаются модули от «обычных» скриптов?

Есть основные возможности и особенности, работающие как в браузере, так и в серверном JavaScript.

  • Всегда «use strict»

В модулях всегда используется режим use strict. Например, присваивание к необъявленной переменной вызовет ошибку.

<script type="module">
  a = 5; // ошибка
</script>

  • Своя область видимости переменных

Каждый модуль имеет свою собственную область видимости. Другими словами, переменные и функции, объявленные в модуле, не видны в других скриптах.

Код в модуле выполняется только один раз при импорте

Если один и тот же модуль используется в нескольких местах, то его код выполнится только один раз, после чего экспортируемая функциональность передаётся всем импортёрам.

Во-первых, если при запуске модуля возникают побочные эффекты, например выдаётся сообщение, то импорт модуля в нескольких местах покажет его только один раз – при первом импорте:

// alert.js
alert("Модуль выполнен!");

// Импорт одного и того же модуля в разных файлах

// 1.js
import `./alert.js`; // Модуль выполнен!

// 2.js
import `./alert.js`; // (ничего не покажет)

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

Теперь более продвинутый пример.

Давайте представим, что модуль экспортирует объект:

// admin.js
export let admin = {
  name: "John"
};

Если модуль импортируется в нескольких файлах, то код модуля будет выполнен только один раз, объект admin будет создан и в дальнейшем будет передан всем импортёрам.

Все импортёры получат один-единственный объект admin:

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

Ещё раз заметим – модуль выполняется только один раз. Генерируется экспорт и после передаётся всем импортёрам, поэтому, если что-то изменится в объекте admin, то другие модули тоже увидят эти изменения.

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

Например, модуль admin.js предоставляет определённую функциональность, но ожидает передачи учётных данных в объект admin извне:

// admin.js
export let admin = { };

export function sayHi() {
  alert(`Ready to serve, ${admin.name}!`);
}

В init.js, первом скрипте нашего приложения, мы установим admin.name. Тогда все это увидят, включая вызовы, сделанные из самого admin.js:

// init.js
import {admin} from './admin.js';
admin.name = "Pete";

Другой модуль тоже увидит admin.name:

// other.js
import {admin, sayHi} from './admin.js';

alert(admin.name); // Pete

sayHi(); // Ready to serve, Pete!

import.meta

Объект import.meta содержит информацию о текущем модуле.

Содержимое зависит от окружения. В браузере он содержит ссылку на скрипт или ссылку на текущую веб-страницу, если модуль встроен в HTML:

<script type="module">
  alert(import.meta.url); // ссылка на html страницу для встроенного скрипта
</script>

В модуле «this» не определён

В модуле на верхнем уровне this не определён (undefined).

Сравним с не-модульными скриптами, там this – глобальный объект:

<script>
  alert(this); // window
</script>

<script type="module">
  alert(this); // undefined
</script>

Модули являются отложенными (deferred)

Модули всегда выполняются в отложенном (deferred) режиме, точно так же, как скрипты с атрибутом defer. Это верно и для внешних и встроенных скриптов-модулей.

Другими словами:

  • загрузка внешних модулей, таких как <script type="module" src="...">, не блокирует обработку HTML.

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

  • сохраняется относительный порядок скриптов: скрипты, которые идут раньше в документе, выполняются раньше.

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

Например:

<script type="module">
  alert(typeof button); // object: скрипт может 'видеть' кнопку под ним
  // так как модули являются отложенными, то скрипт начнёт выполнятся только после полной загрузки страницы
</script>

Сравните с обычным скриптом ниже:

<script>
  alert(typeof button); // Ошибка: кнопка не определена, скрипт не видит элементы под ним
  // обычные скрипты запускаются сразу, не дожидаясь полной загрузки страницы
</script>

<button id="button">Кнопка</button>

Внешние скрипты

Внешние скрипты с атрибутом type="module" имеют два отличия:

  • Внешние скрипты с одинаковым атрибутом src запускаются только один раз:
<!-- скрипт my.js загрузится и будет выполнен только один раз -->
<script type="module" src="my.js"></script>
<script type="module" src="my.js"></script>

  • Внешний скрипт, который загружается с другого домена, требует указания заголовков CORS.

Другими словами, если модульный скрипт загружается с другого домена, то удалённый сервер должен установить заголовок Access-Control-Allow-Origin означающий, что загрузка скрипта разрешена.

<!-- another-site.com должен указать заголовок Access-Control-Allow-Origin -->
<!-- иначе, скрипт не выполнится -->
<script type="module" src="http://another-site.com/their.js"></script>

(Запускайте свой Live Сервер через VSCode и все будет работать)

HomeWork:

Нужно создать красный круг. По нажатию он (круг) едет вправо на 100 пикселей. Как это будем делать?

У нас должно быть 2 файла

  • DOM.js
  • script.js
  1. В DOM.js

В этом файле у нас будет класс DOM, который в свой конструктор принимает сам document Этот класс мы экспортируем

  1. В script.js

Мы импортируем наш класс DOM Создаем класс Circle, который наследуется от DOM

В классе Circle у нас должно быть:

  • конструктор, который принимает ширину и высоту нашего круга (можете в конструктор передавать еще что-то)
  • метод spawnCircle, который будет устанавливать для круга ширину, высоту, цвет и спавнить его на страницу
  • метод setClickAction. Тут описываем логику нажатия на круг и его перемещение. При вызове этот метод устанавливем обработчик события на круг

В этом же файле делаем фабрику, которая будет создавать круг