Определение шаблона адаптера
Шаблон адаптера — это структурный шаблон проектирования, который позволяет несовместимым объектам взаимодействовать.
В нашем примере у нас в голове будет класс карусели и реализация событий с паттерном адаптера.
Почему таким образом?
Я перечислю несколько причин, и вы сможете узнать свою
- ответственность — Если вы посмотрите на код ниже, вы заметите, что каждый класс за что-то отвечает. В отличие от того, когда мы набираем код «спагетти»
- Разделение ответственности — как я уже упоминал, каждый класс за что-то отвечает и делится на слои.
— modules — папка, содержащая логику создания экземпляра класса (позже объясню, почему это важно)
— файлы констант — необходимые строки, заполнители которых должны быть неизменяемыми в коде.
— … - сотрудничество
— от BE к FE — например, что, если настройка должна быть выполнена на стороне BE редактором контента? Что он выбирает, как будет выглядеть карусель
— команда — если выбор будет внутри более широкой команды людей. В проекте несколько команд, обслуживающих одного клиента с несколькими проектами. Например, карусель Bootstrap используется на всех сайтах, но все используют ее по-разному. - логика — а что, если помимо видимой части карусель должна выполнять дополнительную логику?
— например, при воспроизведении видео на одном слайде оно останавливается, переключаясь на другой. Будем ли мы распространять логику и где она будет определена? Должен ли класс карусели напрямую заботиться о других компонентах?
— несколько каруселей с разными конфигурациями на одной странице. - читабельность — мне доводилось натыкаться на код, класс которого имеет почти такое же количество строк, как и сам плагин.
Примеры
Пример Swiper: https://codesandbox.io/s/carousel-swiper-x7owvm
Пример крошечного слайдера: https://codesandbox.io/s/carousel-tiny-slider-1gou1p
Примечание. Если некоторые примеры не запускаются, нажмите кнопку обновления на вкладке браузера или откройте в новом окне.
Руководство по внедрению
import carouselModule from "./modules/carousel-module"; carouselModule();
Мы вызываем модуль, отвечающий за логику инстанцирования нашего класса. файл: carousel-module.ts
import Carousel from "../carousel/carousel"; import "../styles.css"; export default () => { const carouseles = document.querySelectorAll(".js-carousel"); if (carouseles) { carouseles.forEach((carousel) => new Carousel(carousel as HTMLElement)); } };
- Наш класс карусели не должен интересоваться, готова ли DOM, есть ли js-карусель на странице или нет.
- Наш класс не должен использовать манипуляции с DOM и операторы if. Модули — это те, которые должны заботиться о том, как создается экземпляр класса.
- Это также место, где вы, например, будете выполнять динамический импорт в сборке веб-пакета. Когда вы хотите сделать кусочки
Например вот так
export default () => { const carouselElements = document.querySelectorAll('.js-carousel'); if (carouselElements.length) { carouselElements.forEach(async (element) => { const { default: Carousel } = await import(/* webpackChunkName: "carousel" */ '../components/carousel/carousel') new Carousel(element as HTMLElement); }); } return null; };
Карусель класс
Свайпер
constructor(element: HTMLElement) { this.carouselElement = element; this.desktopDevice = window.matchMedia("(min-width: 1024px)"); // Class for Plugin API this.carouselEvents = new CarouselEvents(); Swiper.use([Navigation, Pagination, Grid, Lazy, Autoplay]); this.init(); this.desktopDevice.addListener(this.breakPointHandler.bind(this)); }
Крошечный слайдер
constructor(element: HTMLElement) { this.carouselElement = element; this.desktopDevice = window.matchMedia("(min-width: 1024px)"); // Class for Plugin API this.carouselEvents = new CarouselEvents(); this.init(); this.desktopDevice.addListener(this.breakPointHandler.bind(this)); }
- конструктор устанавливает элемент, связанный с каруселью.
- прослушиватель настроен для отслеживания изменений для определенного медиа-запроса. (дополнительная логика для отслеживания медиа-запроса, если вы хотите показать или создать карусель только на рабочем столе или только на мобильном телефоне)
- адаптер карусели и сама карусель создаются с помощью метода init.
На первый взгляд разницы никакой, кроме строчки кода, куда включены карусельные модули Swiper.
Swiper.use([Navigation, Pagination, Grid, Lazy, Autoplay]);
В следующем разделе файла оба класса имеют одинаковую логику
init = () => this.createCarousel();
функция инициализации создать карусель
карусель
createCarousel = (carousel: HTMLElement) => { ... this.carousel = new Swiper(this.carouselElement, { ...defaultOptions, ...optionsFromElement }); this.carouselEvents.registerEvents(this.carousel, this.carouselElement); };
крошечный слайдер
createCarousel = (carousel: HTMLElement) => { ... this.carousel = tns({ container: this.carouselElement, ...defaultOptions, ...optionsFromElement }); this.carouselEvents.registerEvents(this.carousel, this.carouselElement); };
Единственное отличие заключается в объекте конфигурации. Для swiper элемент карусели является отдельным параметром, а для Tiny Slider — частью объекта конфигурации.
Наконец, мы подошли к классу CarouselEvents.
carousel.events.ts swiper
export default class CarouselEvents { private carousel: Swiper; private element: HTMLElement; registerEvents(carousel: Swiper, element: HTMLElement) { this.carousel = carousel; this.element = element; this.carousel.on(CAROUSEL_CHANGE, () => this.stopVideos()); } }
carousel.events.ts крошечный слайдер
export default class CarouselEvents { private carousel: TinySliderInstance; private element: HTMLElement; registerEvents(carousel: TinySliderInstance, element: HTMLElement) { this.carousel = carousel; this.element = element; this.carousel.events.on(CAROUSEL_CHANGE, () => this.stopVideos()); } }
Разница? Ну, только интерфейс карусели и то, как регистрируются события.
константы свайпера
export const CAROUSEL_CHANGE = "slideChange";
крошечные константы ползунка
export const CAROUSEL_CHANGE = "indexChanged";
В обоих случаях мы хотим отслеживать события при смене слайда. Имена разные, но событие то же самое CAROUSEL_CHANGE или слайд изменен.
Swiper регистрирует события с помощью
this.carousel.on(CAROUSEL_CHANGE, callback);
Крошечный ползунок регистрирует события с помощью
this.carousel.events.on(CAROUSEL_CHANGE, callback);
В обоих случаях мы хотим применить логику при смене слайда. В нашем случае мы хотим остановить некоторые видео.
Если мы изменим карусель на третий тип, например. карусель Bootstrap, код будет следующим
из документации
$('#myCarousel').on('slide.bs.carousel', function () {// do something…})
константы.ts
export const CAROUSEL_CHANGE = "slide.bs.carousel";
карусель.events.ts
this.element.on(CAROUSEL_CHANGE, callback)
Заключение
- Мы создали карусель, которая не зависит от типа используемого плагина.
- Мы можем создать много экземпляров с разными конфигурациями, не создавая разные типы карусели или экземпляры.
- Минимальное изменение разметки кода. Просто чтобы следовать структуре плагина. Конфигурация соответствует плагину.
- Логика реализации
- создание карусели только на мобильном устройстве (логика может быть расширена)
- мониторинг событий и реагирование на события (логика может быть расширена)