Разрабатываем переиспользуемый web-компонент для лайков
В рамках цикла статей о веб-компонентах мы рассмотрим пример реализации компонента лайков wc-likes
, пройдемся по шагам реализации и интегрируем лайки прямо на webislife 😄 поехали!)
wc-like
будет автономным пользовательским элементом – «полностью новый» элемент, расширяющий абстрактный класс HTMLElement
Функциональная часть
Что такое компонент лайка? это некий HTMLElement который обычно нам показывает число лайков и иконку с сердечком. Если бы мы его верстали, получалась бы +- такая emmet конструкция wc-likes>button+span Начнем с описания типов и конструктора нашего компонента
public Liked:boolean = false public LikesRendered:boolean = false public LikesCount:number = 0 public LikesFetch:boolean = false public LikesCountNode:Node public LikesIcon:HTMLElement public LikesPhrases:WCLikesLang public LikesPhrasesLang:WCLikesPhrasesKey|string constructor() { super(); const defaultLang = 'ru'; this.LikesCount = Number(this.getAttribute('value')); this.LikesPhrasesLang = defaultLang; this.LikesPhrases = WCLikesLangs[this.LikesPhrasesLang]; this.classList.add('wc-likes'); }
Вроде все понятно, добавляем себе возможность для поддержки мультиязычности и навешиваем класс wc-likes на элемент чтобы каждый раз его не писать в HTML
/** * Создаем основные node элементы для нашего компонента лайков */ buildElement() { //Кнопка с иконкой this.LikesIcon = document.createElement('button'); //Like SVG icon - иконку можно поменять на свою, я вставил прямо в код this.LikesIcon.innerHTML = ''; //Добавим слушателя события нажатия на кнопку this.LikesIcon.onpointerup = ev => this.onLikesSubmit(ev); //Создадим textNode с количеством лайков this.LikesCountNode = document.createTextNode(`${this.LikesCount}`); //Добавим элементы в DOM нашего компонента this.append(this.LikesIcon); this.append(this.LikesCountNode); }
Рассмотрим метод обработки отправки нашего лайка
/** * Логика обработки отправки лайка * @param ev */ async onLikesSubmit(ev:Event) {{ //Если уже лайкнули if(this.Liked) { this.setAttribute('data-hint', this.LikesPhrases.alreadyLike); return; } this.setAttribute('fetch', '1'); const fetchLikesStatus = await this.fetchSubmitLikes().catch(err => { this.setAttribute('data-hint', err.toString()); }); this.removeAttribute('fetch'); switch (fetchLikesStatus) { case 200: const newCount = this.LikesCount + 1; this.setAttribute('value', String(newCount)); this.setAttribute('liked', String(1)); break; default: this.setAttribute('data-hint', this.LikesPhrases.likeError); break; } }};
Ничего сложного, мы проверяем лайкал ли уже пользователь и если нет, отправляем запрос в API и навешиваем -fetch класс CSS
Но на самом деле нам осталось реализовать еще 2 метода отправки и получения количества лайков, эта реализация зависит от сайта и его системы и не входит в философию веб-компонента, я расскажу про интеграцию лайков к постам на webislife.ru в следующей статье)
/** * ABSTRACT METHOD NEEDS YOUR IMPLEMENTATION SEE wc-likes-post.ts example * submitLike to backend */ async fetchSubmitLikes():Promise { const PromiseAPIStatus:number = await new Promise((resolve, reject) => { setTimeout(() => resolve(200), 1000); }); return PromiseAPIStatus; } /** * ABSTRACT METHOD NEEDS YOUR IMPLEMENTATION SEE wc-likes-post.ts example * fetch likes count from backend */ async fetchAsyncLikes(params:any):Promise { const PromiseAPIStatus:number = await new Promise((resolve, reject) => { setTimeout(() => resolve(200), 1000); }); return PromiseAPIStatus; }
Два метода, которые осталось реализовать)
А ну и конечно же добавим немного SCSS
стилей и CSS анимаций) куда же без этого
SCSS стили для компонента
@keyframes wcLikesSubmit { 0% { visibility: visible; top: 0; opacity: 1; } 100% { opacity: 0; top: -100px; font-size: 4em; } } @keyframes wcLikesFetch { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .wc-likes { display: inline-flex; align-items: center; padding: 0px 1em 0px 0px; border:1px solid var(--color-blue-light-100); border-radius: 20px; transition: all 0.2s ease; user-select:none; &:hover { background-color: var(--color-blue-light-50); border-color: var(--color-blue-light-50); } &:after { min-width: 100px; text-align: center; white-space: nowrap; background-color: var(--color-blue-light-50); color: var(--color-blue-500); } & > button { user-select: contain; display: inline-flex; align-items: center; justify-content: center; position: relative; width: 2em; height: 2em; transition: all 0.2s ease; margin-right: 10px; color: var(--color-red-500); background-color: #fff; border: none; border-radius: 50%; outline: none; cursor: pointer; &:hover { background-color: var(--color-red-100); border: none; & > svg { fill: var(--color-red-500); } } &:after { visibility: hidden; color: var(--color-red-500); content: '♥'; position: absolute; } & > svg { fill: var(--color-red-100); min-width: 16px; } } //fetch like state &.-fetch { opacity: 0.8; pointer-events: none; & > button:before { position:absolute; border: 2px solid var(--color-red-100); border-top: 2px solid var(--color-red-500); border-radius: 50%; display: block; width: 2em; height: 2em; content: ''; animation: wcLikesFetch 1s linear infinite; } } //liked state &.-liked { background-color: var(--color-blue-light-100); border-color: var(--color-blue-light-100); & > button { background-color: var(--color-red-500); color: #fff; &:after { animation-fill-mode: forwards; animation-duration: 0.4s; animation-name: wcLikesSubmit; } & > svg { fill: #fff; stroke: none; } } } }
Веб компоненты — это замена современных фреймворков?
Однозначно нет — есть конечно представители фронтенда с перегибами в сторону нативных компонентов, как и есть армии фанатов react/vue/angular но у всех этих фреймворков уже есть поддержка web компонентов
- Vue и web компоненты
- web компоненты вместе React
- angular guide elements руководство на 🇺🇸
Для тех кому хочется побыстрее попробовать, уже готовый репозиторий на github и готовый npm пакет npm-wc-likes также пакет доступен на github.
Последняя редакция 31 января, 2023 в 03:01