📝 Настраиваем автокомплит правильно
#frontend #браузеры #автозаполнение
Интересная статья от яндекса про правильную настройку автокомплита на фронтенде. Довольно глубоко рассматривается внутренне устройство автокомплита и какие действия предпринимает браузер, чтобы сопоставить поля (по атрибуту id, name, placeholder и label) и сохранённые значения пользователя.
Опираясь на знание о том, как определяется тип поля браузером, мы можем улучшить пользовательский опыт, лишив его необходимости повторно вбивать уже сохранённые данные.
Читать статью: https://habr.com/ru/company/yandex/blog/686668/
#frontend #браузеры #автозаполнение
Интересная статья от яндекса про правильную настройку автокомплита на фронтенде. Довольно глубоко рассматривается внутренне устройство автокомплита и какие действия предпринимает браузер, чтобы сопоставить поля (по атрибуту id, name, placeholder и label) и сохранённые значения пользователя.
Опираясь на знание о том, как определяется тип поля браузером, мы можем улучшить пользовательский опыт, лишив его необходимости повторно вбивать уже сохранённые данные.
Читать статью: https://habr.com/ru/company/yandex/blog/686668/
🔥3👍1
⌨️ Учимся правильно отображать клавиатуру
#frontend #браузеры #mobile
Ни для кого не секрет, что подход mobile-first или даже mobile-only сегодня важен, как никогда. Но к сожалению, довольно большое количество разработчиков забывает про удобство пользователя при вводе каких-либо данных с мобильного устройства.
Как улучшить пользовательский опыт? Продолжаем небольшой цикл статей про браузеры и удобства (вчерашний пост был про автокомплит).
Сегодня рекомендую вам ознакомиться со статьёй на medium, в которой рассказывается про влияние атрибута type у элемента input на то, какая клавиатура будет показываться пользователю с мобильного устройства.
К примеру, если вы назначите атрибуту type значение tel, то у пользователя появится клавиатура с цифрами, что позволит комфортно заполнить номер телефона.
Используйте атрибут type правильно, чтобы сделать чью-то жизнь немного удобнее и будет вам счастье!
Читать статью: https://medium.com/front-end-chummies/control-types-including-virtual-keyboards-associated-with-14-html-input-types-4a0b216d68c6
#frontend #браузеры #mobile
Ни для кого не секрет, что подход mobile-first или даже mobile-only сегодня важен, как никогда. Но к сожалению, довольно большое количество разработчиков забывает про удобство пользователя при вводе каких-либо данных с мобильного устройства.
Как улучшить пользовательский опыт? Продолжаем небольшой цикл статей про браузеры и удобства (вчерашний пост был про автокомплит).
Сегодня рекомендую вам ознакомиться со статьёй на medium, в которой рассказывается про влияние атрибута type у элемента input на то, какая клавиатура будет показываться пользователю с мобильного устройства.
К примеру, если вы назначите атрибуту type значение tel, то у пользователя появится клавиатура с цифрами, что позволит комфортно заполнить номер телефона.
Используйте атрибут type правильно, чтобы сделать чью-то жизнь немного удобнее и будет вам счастье!
Читать статью: https://medium.com/front-end-chummies/control-types-including-virtual-keyboards-associated-with-14-html-input-types-4a0b216d68c6
👍4🤔1
🖱 Атрибут cursor
#браузеры #frontend #css
Продолжаю небольшой цикл про повышения удобства пользовательского опыта. Сегодня поговорим про CSS-свойство cursor.
Самое популярное значение для этого свойства — pointer. Это значение показывает пользователю, что на элемент можно кликнуть, но и есть и другие его значения, про которые довольно часто забывают разработчики.
Предлагаю вспомнить про важные виды курсоров, на мой взгляд это:
- wait/progress (контент загружается);
- not allowed (действие совершить нельзя);
- help (выводит знак вопроса);
- grab и grabbing (в основном, нужны чтобы показать draggable элементы);
- zoom in / zoom out (приближение / отдаление, соответственно).
Про остальные типы вы можете почитать в доке: https://doka.guide/css/cursor/
#браузеры #frontend #css
Продолжаю небольшой цикл про повышения удобства пользовательского опыта. Сегодня поговорим про CSS-свойство cursor.
Самое популярное значение для этого свойства — pointer. Это значение показывает пользователю, что на элемент можно кликнуть, но и есть и другие его значения, про которые довольно часто забывают разработчики.
Предлагаю вспомнить про важные виды курсоров, на мой взгляд это:
- wait/progress (контент загружается);
- not allowed (действие совершить нельзя);
- help (выводит знак вопроса);
- grab и grabbing (в основном, нужны чтобы показать draggable элементы);
- zoom in / zoom out (приближение / отдаление, соответственно).
Про остальные типы вы можете почитать в доке: https://doka.guide/css/cursor/
👍4🤔1
#ux #frontend #дизайн
В прошлом году мы с вами уже говорили про автозаполнение, корректный тип клавиатуры и смену курсора, в зависимости от ситуации. Продолжаем тему подборкой раздражающих косяков в современных интерфейсах от одного из пользователей хабра:
1) приложения, в которых нельзя отключить рекламные пуши;
2) сброс выбранного города или неверное его отображение;
3) выбор дат без возможности ввода с клавиатуры;
4) буквенная клавиатура для телефонных номеров и неуместная автокоррекция ввода;
5) наш сайт использует куки, разрешите нам использовать куки, куки-куки-куки...
Радует, что появляются такие разработчики, которые начинают задумываться об удобстве конечного пользователя. Мне кажется, это то, что действительно важно в современном мире и, к сожалению, далеко не все и даже не большая часть программистов и дизайнеров задумываются об удобстве.
📖 Источник:
Это ужасно бесит — подборка косяков, постоянно встречающихся от сайта к сайту, от приложения к приложению — https://habr.com/ru/post/709494/
А вас бесит что-то из этих пунктов? Голосуйте в опросе👇
Please open Telegram to view this post
VIEW IN TELEGRAM
Хабр
Это ужасно бесит — подборка косяков, постоянно встречающихся от сайта к сайту, от приложения к приложению
Каждый день мы пользуемся десятками различных мобильных приложений и посещаем десятки, если не сотни, всевозможных сайтов. Часто при этом мы сталкиваемся с какими-то их неприятными особенностями —...
👍2🤔2
#react #frontend #ui
Интересный рассказ о том, как в альфа-банке решали проблему использования общих компонентов между несколькими микросервисами. Проблема заключалась в необходимости вывода общего меню между всеми доступными сервисами в веб-приложении банка.
Эта ситуация для меня интересна, потому что мы и сами недавно столкнулись с похожей ситуацией, хотя и масштаб совершенно несравним. Так получилось, что мы пилили один проект двумя разными командами, поделив между ними куски интерфейса, произошло это потому, что мы банально не успевали сделать всё своими силами и отдали часть приложения на аутсорс. В силу сжатых сроков — мы решили просто сверстать одинаковые компоненты на разных технологиях и оставить решение проблемы на начало текущего года, вместо того, чтобы лихорадочно пытаться решить не самую простую задачу. А вот ребята из команды альфа-банка пошли дальше, — запилили отдельный сервис для отдачи готовых пунктов меню по API + необходимый CSS- и JS-код для обеспечения интерактивности некоторых пунктов (например, уведомления).
А как бы вы решили такую проблему? Пишите в комментариях👇
📖 Источник:
Что такое Shared UI, как он нам помог и причём тут микросервисы — https://habr.com/ru/company/alfa/blog/701720/
Please open Telegram to view this post
VIEW IN TELEGRAM
Хабр
Что такое Shared UI, как он нам помог и причём тут микросервисы
Всем привет! Меня зовут Дима, я frontend-разработчик в Альфа-Банке. Сегодня я расскажу про Shared UI, что это такое и как мы пришли к созданию такого сервиса в интернет-банке, что это такое, как мы...
👍3🤔2
🕹 Подборка игр для изучения фронтенд-разработки
#подборка #frontend
В моём университетском курсе по фронтенду есть домашняя работа, целью которой является прохождение трёх игр (это уже почти классика):
1) Flexbox Froggy — https://flexboxfroggy.com/#ru
Игра про лягушек и кувшинки, которая учит основам вёрстки с помощью flexbox, формирует начальное понимание об использовании flex-контейнеров и позволяет запомнить и закрепить основные свойства, необходимые для работы с flexbox.
2) Grid Garden — https://cssgridgarden.com/#ru
Морковки, грядки, сорняки... прекрасная возможность научиться работать с CSS Grid и прощупать каждый основной момент этой технологии.
3) Learn git branching — https://learngitbranching.js.org/?locale=ru_RU
Тренажёр по git, в котором довольно много теории, подающейся в игровой форме. Но учит эта игра не только базовым вещам, но и более продвинутому уровню (rebase, remotes, etc.). Проверено на личном опыте: все мои подчинённые джуны обязаны проходить эту игру, когда учатся правильной работе с git.
Недавно я задумался о том, какие ещё есть способы для изучения тех же технологий, прочитал несколько подборок и выделил ещё 3 наиболее интересных, на мой взгляд, игры:
1) Grid Attack — https://codingfantasy.com/games/css-grid-attack
Игра в стиле мультяшного фэнтези на целых 80 уровней, в которых нужно защищать своего героя от атаки монстров путём определения корректных значений для различных свойств CSS Grid.
2) FlexBox Adventure — https://codingfantasy.com/games/flexboxadventure
Восьмибитная графика, приятные анимации и обучение CSS, больше тут добавить нечего. Лично мне — понравилось.
3) JSRobot — https://lab.reaal.me/jsrobot/
Ещё одна восьмибитная игра, но в этот раз уже по JS, здесь вы учитесь управляться с экземплярами классов, вызывать их методы, чтобы всячески управлять роботом на экране, ведя его к цели каждого из представленных уровней.
💬 Есть что добавить? Поделись в комментариях :)
#подборка #frontend
В моём университетском курсе по фронтенду есть домашняя работа, целью которой является прохождение трёх игр (это уже почти классика):
1) Flexbox Froggy — https://flexboxfroggy.com/#ru
Игра про лягушек и кувшинки, которая учит основам вёрстки с помощью flexbox, формирует начальное понимание об использовании flex-контейнеров и позволяет запомнить и закрепить основные свойства, необходимые для работы с flexbox.
2) Grid Garden — https://cssgridgarden.com/#ru
Морковки, грядки, сорняки... прекрасная возможность научиться работать с CSS Grid и прощупать каждый основной момент этой технологии.
3) Learn git branching — https://learngitbranching.js.org/?locale=ru_RU
Тренажёр по git, в котором довольно много теории, подающейся в игровой форме. Но учит эта игра не только базовым вещам, но и более продвинутому уровню (rebase, remotes, etc.). Проверено на личном опыте: все мои подчинённые джуны обязаны проходить эту игру, когда учатся правильной работе с git.
Недавно я задумался о том, какие ещё есть способы для изучения тех же технологий, прочитал несколько подборок и выделил ещё 3 наиболее интересных, на мой взгляд, игры:
1) Grid Attack — https://codingfantasy.com/games/css-grid-attack
Игра в стиле мультяшного фэнтези на целых 80 уровней, в которых нужно защищать своего героя от атаки монстров путём определения корректных значений для различных свойств CSS Grid.
2) FlexBox Adventure — https://codingfantasy.com/games/flexboxadventure
Восьмибитная графика, приятные анимации и обучение CSS, больше тут добавить нечего. Лично мне — понравилось.
3) JSRobot — https://lab.reaal.me/jsrobot/
Ещё одна восьмибитная игра, но в этот раз уже по JS, здесь вы учитесь управляться с экземплярами классов, вызывать их методы, чтобы всячески управлять роботом на экране, ведя его к цели каждого из представленных уровней.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Новое свойство
#frontend #css
Во вчерашней подборке от reddit, увидел интересный пост про новое CSS-свойство
Автор треда говорит о том, что совсем скоро это завезут в Chrome Canary. Я покопался и на самом деле это свойство туда уже успели завести, по крайней мере, так было написано на chromestatus. Немного вчитался и обнаружил, как можно запустить Chrome Canary с включённым свойством, поскольку в
В данный момент Pull request с этим свойством уже принят в черновиках CSSWG на github.
Пример кода, который я накидал для демки:
field-sizing
или autoresize для textarea без костылей#frontend #css
Во вчерашней подборке от reddit, увидел интересный пост про новое CSS-свойство
field-sizing
, которое позволяет <textarea>
и <input type="text">
автоматически расширяться в зависимости от контента.Автор треда говорит о том, что совсем скоро это завезут в Chrome Canary. Я покопался и на самом деле это свойство туда уже успели завести, по крайней мере, так было написано на chromestatus. Немного вчитался и обнаружил, как можно запустить Chrome Canary с включённым свойством, поскольку в
chrome://flags
оно пока недоступно (команда, работающая у меня на MacOS, вам надо заменить путь до Chrome Canary, самый простой способ узнать это — открыть в своей Chrome Canary chrome://version
):$ /Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary --flag-switches-begin --enable-features=CssFieldSizing --flag-switches-end --origin-trial-disabled-features=WebGPU
В данный момент Pull request с этим свойством уже принят в черновиках CSSWG на github.
Пример кода, который я накидал для демки:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
textarea, input {
field-sizing: content;
min-height: 10px;
min-width: 100px;
}
</style>
</head>
<body>
<div>
<textarea></textarea>
</div>
<div>
<input type="text">
</div>
</body>
</html>
🔥8😍2❤🔥1
"был в сети 15 минут назад" — учимся форматировать даты на фронтенде
#frontend #intl
Идея этого поста пришла ко мне во время кодревью, в процессе которого я вспомнил про такую штуку, как dayjs. Библиотека, которая на мой взгляд, в принципе не нужна, потому что существует Intl API (которым, к сожалению, мало кто пользуется). Причём про это даже в саркастичном ключе написал автор канала ExtremeCode. И ещё тогда я пересылал друзьям и писал, что смешно, что такое решение существует. Но пошутили и забыли, а тут я увидел его в MR и сразу забраковал. И дело вовсе не в саркастичных постах.
Если говорить кратко, dayjs это более современная замена moment.js. Только весит moment в распакованном виде 4.35 Мб, а dayjs всего 664 Кб. Впрочем, это не значит, что это решение в чём-то действительно лучше, поскольку, ни то, ни другое не использует браузерные API, а предоставляет вам свой собственный велосипед. И он действительно может быть неплох в каких-то моментах, но вряд ли вы будете спорить с тем, что решение, интегрированное в браузер будет менее оптимальным выбором.
В нашем случае, dayjs был втянут в проект только чтобы отрисовывать текст типа: "30 минут назад", вместо конкретной даты и времени. Именно это мне и не понравилось, что ради одного красивого вывода мы тянем целую отдельную библиотеку. В качестве альтернативы, я накидал небольшой пример для решения задачи, которую мой сотрудник пытался решить с помощью dayjs:
Константные переменные можно вынести в общий конфиг решения, но в остальном оно не использует никаких велосипедов и костылей, но предоставляет возможность опираясь на силы браузера реализовать решение простой задачи, не устанавливая лишних зависимостей.
#frontend #intl
Идея этого поста пришла ко мне во время кодревью, в процессе которого я вспомнил про такую штуку, как dayjs. Библиотека, которая на мой взгляд, в принципе не нужна, потому что существует Intl API (которым, к сожалению, мало кто пользуется). Причём про это даже в саркастичном ключе написал автор канала ExtremeCode. И ещё тогда я пересылал друзьям и писал, что смешно, что такое решение существует. Но пошутили и забыли, а тут я увидел его в MR и сразу забраковал. И дело вовсе не в саркастичных постах.
Если говорить кратко, dayjs это более современная замена moment.js. Только весит moment в распакованном виде 4.35 Мб, а dayjs всего 664 Кб. Впрочем, это не значит, что это решение в чём-то действительно лучше, поскольку, ни то, ни другое не использует браузерные API, а предоставляет вам свой собственный велосипед. И он действительно может быть неплох в каких-то моментах, но вряд ли вы будете спорить с тем, что решение, интегрированное в браузер будет менее оптимальным выбором.
В нашем случае, dayjs был втянут в проект только чтобы отрисовывать текст типа: "30 минут назад", вместо конкретной даты и времени. Именно это мне и не понравилось, что ради одного красивого вывода мы тянем целую отдельную библиотеку. В качестве альтернативы, я накидал небольшой пример для решения задачи, которую мой сотрудник пытался решить с помощью dayjs:
function countTimedeltaFromToday(date: Date) {
const currDate = new Date();
const delta = currDate.getTime() - date.getTime();
const ONE_MILLISECOND = 1000;
const HOUR_IN_SECONDS = 60 * 60;
const DAY_IN_HOURS = 24;
const MONTH_IN_DAYS = 30;
const YEAR_IN_MONTH = 12;
const daysDelta = delta / ONE_MILLISECOND / HOUR_IN_SECONDS / DAY_IN_HOURS;
const monthsDelta = daysDelta / MONTH_IN_DAYS;
const yearsDelta = monthsDelta / YEAR_IN_MONTH;
return {
delta,
daysDelta,
monthsDelta,
yearsDelta,
};
}
function formatDate(intl: Intl.RelativeTimeFormat, date: Date) {
const MAX_DAYS_DELTA = 15;
const MAX_MONTHS_DELTA = 11;
const delta = countTimedeltaFromToday(date);
const { daysDelta, monthsDelta, yearsDelta } = delta;
if (daysDelta < MAX_DAYS_DELTA) {
return intl.format(Math.floor(-daysDelta), 'day');
}
if (monthsDelta < MAX_MONTHS_DELTA) {
return intl.format(Math.floor(-monthsDelta), 'month');
}
return intl.format(Math.floor(-yearsDelta), 'year');
}
const intl = new Intl.RelativeTimeFormat('ru', { style: 'long', numeric: 'auto' });
Константные переменные можно вынести в общий конфиг решения, но в остальном оно не использует никаких велосипедов и костылей, но предоставляет возможность опираясь на силы браузера реализовать решение простой задачи, не устанавливая лишних зависимостей.
👍8⚡3❤🔥1
#frontend #браузеры
Пару лет назад, я писал про предложение о добавлении элемента, который стал бы нативным попапом. Судя по комментарию в топике этого предложения, идея элемента popup была перенесена в новую концепцию — Popover API. К моему удивлению, в данный момент это API поддерживается большинством современных браузеров, кроме Firefox. Подробности с примерами можно почитать, как в переводе на хабре, так и в оригинале. К своему стыду, я упустил новость о внедрении этого API в браузеры, хотя это произошло уже почти год назад. Постараюсь найти время и собрать под это дело интерактивную демку.
Внедрение различных новых фич в браузеры -- это всегда повод для радости. В декабре я писал про свойство
field-sizing
(на момент публикации, в интернете об этом почти не было постов, я в числе первых, кто написал об этом на русском языке), сегодня хочу поделиться с вами новостью про добавление нового элемента <dialog>
.Как этим пользоваться?
Я собрал небольшую демку на codepen, где используется элемент
<dialog>
для отрисовки модального окна. Есть минимальная стилизация. Давайте разберём эту демку и попробуем как-то её усложнить.<style>
::backdrop {
background: #000;
opacity: 0.75;
}
dialog {
border: unset;
border-radius: 10px;
}
dialog form {
display: flex;
justify-content: end;
}
</style>
<button onclick="dialog.showModal()">Открыть модалку</button>
<dialog id="dialog">
<p>Это нативное модальное окно</p>
<form method="dialog">
<button>ОК</button>
</form>
</dialog>
Начнём со стилизации, псевдокласс
::backdrop
позволяет отрисовать затемнение общего фона страницы, чтобы отделить модальное окно от всего остального интерфейса. Стилизация самого элемента и формы не так интересна, её можно пропустить.Дальше по коду идёт кнопка, с обработчиком onclick, в котором обозначен вызов метода
showModal()
на элементе с id dialog
. Внутри тела самого модального окна есть форма, которая отвечает за различные действия, которые можно совершить. По умолчанию, нажатие на кнопку будет закрывать модальное окно.Логично, что модальные окна могут быть не только информационными, одного закрытия по кнопке маловато. Изменить это можно следующим образом:
<button onclick="dialog.showModal()">Открыть модалку</button>
<dialog id="dialog">
<p>Хотите продолжить?</p>
<form method="dialog">
<button type="submit" value="no">Нет</button>
<button type="submit" value="yes">Да</button>
</form>
</dialog>
<script>
dialog.addEventListener('close', (event) => {
if (dialog.returnValue === 'yes') { /* ... */ }
});
</script>
Таким образом, мы получаем полнофункциональное модальное окно, которое можно и нужно использовать вместо самописных велосипедов. Доступность у такого элемента точно будет выше, чем у написанного руками. Бывают случаи, когда фремйворки соблюдают рекомендации WAI ARIA, как например, Bootstrap. Но если есть возможность заменить ненативный элемент нативным, это всегда большой плюс.
Расширенная версия поста доступна у меня в блоге.
Please open Telegram to view this post
VIEW IN TELEGRAM
Teletype
Нативные модалки
Пару лет назад, я писал про предложение о добавлении элемента, который стал бы нативным попапом. Судя по комментарию в топике этого...
👍5🔥2❤🔥1
#frontend
Проблема, с которой мы недавно столкнулись — это redux в зависимостях у react-beautiful-dnd. Есть какое-то внутреннее сопротивление тому факту, что кто-то втягивает целый стейт-менеджер в библиотеку, которая должна давать возможность перетащить карточку из одной колонки в другую. Разумеется, я несколько утрирую ситуацию, но в данном случае решение кажется необоснованным.
В моём понимании, библиотека для drag-n-drop, в целом, не нуждается в том, чтобы иметь свой собственный стетйт-менеджер, возможностей локального стейта компоненты для такого решения более, чем достаточно.
В случае, если стор всё-таки требуется, я предлагаю следующее решение: "стор должен поступать внешней зависимостью, согласно типизированного библиотекой интерфейса". То есть, сделать возможность передать стор внутрь библиотеки, создав кастомную обёртку, подходящую для конкретной библиотеки. Понимаю, что от этого сложность может возрасти, но почти наверняка такой подход позволит уменьшить итоговый бандл, хотя и увеличит время и сложность разработки. С другой стороны, это решение разработанное инженерами Atlassian, вряд ли они недостаточно компетентны, хотя по работе в Jira этого иногда и не скажешь.
Возможно, если у меня дойдут руки, то мы соберём отдельное собственное решение для drag-n-drop. В случае, если это произойдёт — я обязательно поделюсь ссылкой на решение.
В целом, ситуация, является лишним доказательством слов про то, что решение, разработанное большой компанией призвано решать проблемы этой самой большой компании, а не ваши.
Please open Telegram to view this post
VIEW IN TELEGRAM
npm
npm: react-beautiful-dnd
Beautiful and accessible drag and drop for lists with React. Latest version: 13.1.1, last published: 3 years ago. Start using react-beautiful-dnd in your project by running `npm i react-beautiful-dnd`. There are 2007 other projects in the npm registry using…
🤔3👍1
📆 Правильное форматирование дат в JS
#frontend
Буквально месяц назад писал свои мысли по поводу таких решений, как
И вот, моя мечта сбылась. На неделе я сидел и листал тематические каналы в поисках чего-то нового и интересного и наткнулся на пост про библиотеку tempo. В основе это решение использует браузерный объект
#frontend
Буквально месяц назад писал свои мысли по поводу таких решений, как
moment
/day.js
и ругал их за то, что они не используют нативные браузерные API. И вот, моя мечта сбылась. На неделе я сидел и листал тематические каналы в поисках чего-то нового и интересного и наткнулся на пост про библиотеку tempo. В основе это решение использует браузерный объект
Date
и Intl API
. При этом, API самой библиотеки достаточно простое и имеет довольно подробную документацию.Formkit
Tempo • Dates by FormKit
An open-source library for handling complex date operations across timezones. The easiest way to work with dates in JavaScript.
👍5❤2
#vue #frontend
Несколько месяцев назад, в подборке от reddit мне попался довольно странный вопрос в сабреддите vue.js: "Почему я должен использовать Pinia вместо глобальных реактивных переменных?".
Странным он показался мне, в первую очередь, из-за того, что я никогда об этом всерьёз не задумывался и выбирал использовать какой-либо стейт-менеджер, вместо переменных, потому что так заведено. Как будто, это тема, в которой и так всё ясно, дополнительных исследований не требуется: хочешь реактивность и надёжность — бери стейт-менеджер, он это предоставляет.
В тексте своего сообщения автор опирается на раздел документации Vue, в котором рассказывается о том, как управление состоянием устроено в компонентах. По мнению пользователя reddit, у использования глобальных реактивных переменных есть такие плюсы, как:
1) простота;
2) нативная поддержка фреймворка;
3) нет зависимостей, что означает, не появится ситуации, как с Vuex (который прекратили развивать), в которой придётся переписывать половину кодовой базы;
4) Composition API выглядит более зрело и стабильно и вряд ли изменится в ближайшем будущем (в сравнении с переходом с Vue 2 на Vue 3);
5) возможность использования всей мощи реактивных API, вместо урезанной реактивной обёртки Pinia;
6) разница в эффективности при миллионе операций записи довольна внушительна:
Ref time: 36ms
Pinia ref time: 628ms
Reactive time: 501ms
Pinia reactive time: 809ms
В комментариях в качестве основных плюсов использования Pinia вместо глобальных реактивных переменных, приводили аргументы только про возможность использования SSR и DevTools.
Эта дискуссия действительно заставила меня задуматься, в следующем своём проекте на Vue, хочу попытаться применить подобную концепцию на практике.
P. S.
Оказалось, автор является частью русскоязычного сообщества @vuejs_ru и поддерживает и развивает ресурс Vue FAQ. Я бегло по нему прошёлся и не могу не выразить искреннее восхищение проделанной работой. Прошу обратить своё внимание на этот ресурс, в нём собрано довольно много информации по фреймворку.
Please open Telegram to view this post
VIEW IN TELEGRAM
Reddit
From the vuejs community on Reddit
Explore this post and more from the vuejs community
👍7👏3
Маска для номера телефона
#frontend #браузеры #стандартизация
Проблема стандартизации форматирования и валидации поля ввода номера телефона абсолютно не новая. Даже в MDN говорят о том, что
Решение, которое мы можем без труда соорудить стандартными средствами браузера, не используя дополнительных библиотек выглядит примерно так:
С таким решением мы будем строго принимать один обозначенный формат (пример сильно упрощён, более комплексный вариант регулярного выражения можно подглядеть тут) с общей подсказкой для пользователя, без маски для ввода. Как давно вы в последний раз видели поле ввода для номера телефона без маски?
Можно долго дискутировать о том, нужна ли маска для ввода в данном случае, но пользователи уже к этому привыкли и вы явно их не отучите, только добавите неудобств.
Вернёмся к основной теме. Без добавления JS-кода маску мы не получим. Можно поискать готовое решение, но с этим тоже есть проблема. Решения, которые сразу будут хорошо работать из коробки найти сложно. Довольно часто, многие из этих решений уже содержат в себе стилизацию, которую не всегда легко переделать под нужный дизайн.
На мой взгляд, всё, что должно быть в хорошем решении подобной задачи:
1) возможность управления паттерном форматирования, валидации;
2) отсутствие лишних зависимостей и любой стилизации, она должна поступать извне, либо легко кастомизироваться;
3) интернационализация.
Почти идеальным решением, на мой взгляд, является imask. Он предоставляет только функциональную часть с очень простыми API. К примеру, для российского номера телефона код будет выглядеть так:
Кроме прочего, решение имеет адаптацию под популярные веб-фреймворки (react, vue, angular, svelte) и даже неплохой задел на интернационализацию за счёт использования динамических масок. Пожалуй, единственным минусом этого решения является необходимость написания большого количества кода для более тонкой настройки. С другой стороны, это даёт возможность широкой кастомизации и позволяет менять паттерны и маски так, как вам требуется, не отнимая возможности стилизовать свои поля ввода так, как вы считаете нужным.
#frontend #браузеры #стандартизация
Проблема стандартизации форматирования и валидации поля ввода номера телефона абсолютно не новая. Даже в MDN говорят о том, что
<input type="tel">
в отличие от <input type="email">
не имеет никакой автоматической валидации, потому что "форматы телефонных номеров сильно раличаются по всему миру".Решение, которое мы можем без труда соорудить стандартными средствами браузера, не используя дополнительных библиотек выглядит примерно так:
<div>
<input
type="tel"
pattern="(^8|7|\+7)([0-9]{10})"
required
>
</div>
<div>
<small>Формат: +79999999999</small>
</div>
С таким решением мы будем строго принимать один обозначенный формат (пример сильно упрощён, более комплексный вариант регулярного выражения можно подглядеть тут) с общей подсказкой для пользователя, без маски для ввода. Как давно вы в последний раз видели поле ввода для номера телефона без маски?
Можно долго дискутировать о том, нужна ли маска для ввода в данном случае, но пользователи уже к этому привыкли и вы явно их не отучите, только добавите неудобств.
Вернёмся к основной теме. Без добавления JS-кода маску мы не получим. Можно поискать готовое решение, но с этим тоже есть проблема. Решения, которые сразу будут хорошо работать из коробки найти сложно. Довольно часто, многие из этих решений уже содержат в себе стилизацию, которую не всегда легко переделать под нужный дизайн.
На мой взгляд, всё, что должно быть в хорошем решении подобной задачи:
1) возможность управления паттерном форматирования, валидации;
2) отсутствие лишних зависимостей и любой стилизации, она должна поступать извне, либо легко кастомизироваться;
3) интернационализация.
Почти идеальным решением, на мой взгляд, является imask. Он предоставляет только функциональную часть с очень простыми API. К примеру, для российского номера телефона код будет выглядеть так:
IMask(
document.getElementById('phone-mask'),
{
mask: '+{7}(000)000-00-00'
}
)
Кроме прочего, решение имеет адаптацию под популярные веб-фреймворки (react, vue, angular, svelte) и даже неплохой задел на интернационализацию за счёт использования динамических масок. Пожалуй, единственным минусом этого решения является необходимость написания большого количества кода для более тонкой настройки. С другой стороны, это даёт возможность широкой кастомизации и позволяет менять паттерны и маски так, как вам требуется, не отнимая возможности стилизовать свои поля ввода так, как вы считаете нужным.
👍9
#frontend #vue #производительность
На reddit наткнулся на пост в комьюнити Vue.JS с описанием проблемы с производительностью у вычисляемых свойств (
computed
). Заключается проблема в том, что судя по тестам использованием watch
+ ref
на 50% более эффективно, чем использованием computed
.Добавим немного контекста. Тест проводился на дропдауне с 1 000 000 элементов. Задача теста состояла в том, чтобы написать функцию, которая будет конвертировать объекты (или строки) с типом
selectOption
в объект selectOptionsObject
.Типы выглядят так:
export type selectOption = selectOptionObject | string;
export type selectOptionObject = {
id: string | number;
render: string;
raw?: any;
};
Код функции
normaliseOptions
для конвертации:const normaliseOptions = (
options?: selectOption[]
): normalisedOptionObject[] => {
if (!options) return [];
// We will use a straight for loop for performance
const normalisedOptions = [];
for (let i = 0; i < options.length; i++) {
const option = options[i];
if (typeof option === "string") {
normalisedOptions.push({
id: option,
render: option,
});
continue;
}
normalisedOptions.push({
id: option.id.toString(),
render: option.render,
disabled: option.disabled || false,
raw: option.raw,
});
}
return normalisedOptions;
};
Код, который использовался в тесте:
1) С использованием computed:
const normalisedOptions = computed(() => {
return normaliseOptions(props.options);
});
2) С использованием watch + ref:
// Using the pattern below rather than a computed value gives us a 2x performance improvement
const normalisedOptions = ref(normaliseOptions(props.options));
const recomputeOptions = () => {
normalisedOptions.value = normaliseOptions(props.options);
};
watch(
() => props.options,
() => {
recomputeOptions();
}
);
Самые умные и опытные уже догадались, в чём может заключаться проблема конкретно в данном случае (а я не успел самостоятельно подумать и наткнулся на ответ в комментариях). Проблема заключается в том, что
computed
отслеживает каждую вложенную зависимость. Поэтому он будет работать медленнее, если props.options
не является плоской структурой, состоящей из примитивов, потому что требуется провести кратно больше вычислений. Таким образом, корректный код для данного случая выглядит так:const recomputedOptions = computed(() => normaliseOptions(toRaw(props.options)));
Метод
toRaw
извлекает необработанный объект из proxy-объекта, созданного Vue, давая нам возможность поработать с оригинальным объектом. Подробнее про него можно почитать в документации.Please open Telegram to view this post
VIEW IN TELEGRAM
Reddit
From the vuejs community on Reddit
Explore this post and more from the vuejs community
👍8🤔1
#frontend #react
Около месяца назад я писал пост про библиотеку imask, чтобы стандартизировать ввод номера телефона, с помощью использования маски. Тогда же я упомянул, что основным минусом, на мой взгляд, является то, что нужно написать много дополнительного кода, чтобы достичь желаемого результата.
Пару недель назад я как раз столкнулся с подобной задачей в рамках проекта на react и захотел попробовать реализовать это с помощью imask, но столкнулся с проблемой: нужно отображать список стран, с привязанным к ним кодом. В imask есть возможность решить это, но нет готового набора кодов номеров телефонов. Я вспомнил о решении, которое мои сотрудники находили. Попробовал его адаптировать и понял, что я особо не могу его кастомизировать (по дизайну требовалось выводить двухбуквенное название страны, а не флаг), несмотря на его описание, — "Advanced, highly customizable phone input component for Ant Design."
Поняв, что нахожусь в сложной ситуации, я пошёл искать библиотеки, которые предоставляют просто список телефонных кодов по странам, чтобы реализовать это с помощью imask и нашёл такое решение. По сути, это простая библиотечка, которая возвращает нам объект из пар ключ-значение в том формате, в котором нам необходимо. Казалось бы, всё замечательно. Можно брать динамически маски в imask, генерировать их с помощью этих самых кодов и идти спокойно пить чай. Но в этот момент я осознал, что у разных стран, буквально, могут быть разные маски для ввода номера телефона и это окончательно закопало мою прекрасную идею об использовании imask для решения этой задачи.
Пример с самого сайта imask, где наглядно видно, что маски вообще не подчиняются единым правилам и могут выглядеть абсолютно как угодно:
[
{
mask: '+00 {21} 0 000 0000',
startsWith: '30',
lazy: false,
country: 'Greece'
},
{
mask: '+0 000 000-00-00',
startsWith: '7',
lazy: false,
country: 'Russia'
},
{
mask: '+00-0000-000000',
startsWith: '91',
lazy: false,
country: 'India'
},
{
mask: '0000000000000',
startsWith: '',
country: 'unknown'
}
]
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6
#frontend #react
Ресёрч пришлось продолжать. Я нашёл библиотеку, которая позволяла валидировать номер телефона относительно двухбуквенного кода страны, к которой этот номер относится. Кроме прочего, в этой библиотеке есть и возможность форматирования номера телефона, как раз под маску, принятую в конкретной стране. В описании библиотеки также упоминался react-компонент, который использует эту самую библиотеку. И на основе уже этого компонента, я собрал своё решение с использованием ant-design (разумеется, оно не идеальное, но достаточно хорошее, чтобы использовать в условном продакшне):
import PhoneInput from 'react-phone-number-input';
import PropTypes from 'prop-types';
import { getCountries } from 'react-phone-number-input';
import { Select as AntdSelect } from 'antd';
const phoneCountryLabels = () => {
const countryLabels = {};
getCountries().forEach((country) => (countryLabels[country] = country));
return countryLabels;
};
const CountrySelect = ({ value, onChange, labels, variant = 'filled', ...rest }) => (
<AntdSelect
{...rest}
value={value}
showSearch
optionFilterProp="label"
onChange={onChange}
variant={variant}
size="large"
style={{ '--ant-select-single-item-height-lg': '3rem' }}
/>
);
CountrySelect.propTypes = {
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
labels: PropTypes.objectOf(PropTypes.string).isRequired,
variant: PropTypes.string,
};
const PhoneNumberInput = ({ onChange, country = 'US' }) => {
return (
<div className="base-phone-number">
<PhoneInput
onChange={onChange}
defaultCountry={country}
international
limitMaxLength
labels={phoneCountryLabels()}
countrySelectComponent={CountrySelect}
numberInputProps={{
className: 'ant-input ant-input-filled css-var-r1 ant-input-css-var base-input',
}}
/>
</div>
);
};
PhoneNumberInput.propTypes = {
onChange: PropTypes.func.isRequired,
country: PropTypes.string,
};
export default PhoneNumberInput;
А как вы решали подобные задачи? Делитесь, будет интересно узнать.
P. S. как появится немного времени, планирую тоже самое адаптировать под vue-компонент и поделиться результатом
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9
Переключение сред исполнения в Vite
#советы #frontend #vite
Мало ли вы не знали, но в vite можно использовать MODE. При этом, он будет браться из имени вашего файла. К примеру, у вас есть
Такой же код можно вставлять даже внутрь самих компонентов, например в JSX это может выглядеть так:
При самой сборке и запуске можно указывать аргумент -m со значением
Подробнее: https://vite.dev/guide/env-and-mode
#советы #frontend #vite
Мало ли вы не знали, но в vite можно использовать MODE. При этом, он будет браться из имени вашего файла. К примеру, у вас есть
.env.prod
и .env.dev
. Если вы хотите, чтобы какой-то код был доступен только в деве (например, когда вы только выкатываете новую фичу для теста, а ветка по какой-то причине одна, такое бывает), то вы можете использовать такой код для этого:const isProdMode = import.meta.env.MODE === 'prod';
if (isProdMode) {
// Код, который должен исполняться только в prod режиме
} else {
// Код для других режимов
}
Такой же код можно вставлять даже внутрь самих компонентов, например в JSX это может выглядеть так:
{isProdMode && <HelloWorld />}
При самой сборке и запуске можно указывать аргумент -m со значением
dev
или prod
(или каким-либо другим значением, которое вы заложите)."scripts": {
"start": "npm run dev",
"dev:prod": "vite --port 3000 -m prod",
"dev": "vite --port 3000 -m dev"
}
Подробнее: https://vite.dev/guide/env-and-mode
vitejs
Env Variables and Modes
Next Generation Frontend Tooling
👍9🔥2🤔1
Форматируем вывод числа с учётом валюты и локали на фронтенде
#frontend #intl
Несколько месяцев назад, я уже рассказывал вам про такую вещь, как Intl API. Тогда мы разбирались с датами, сегодня предлагаю научиться форматировать валюты с помощью следующей функции:
Аргумент
По-хорошему, бы тут добавить какую-то проверку, что аргументы передаваемые в
Если верить спецификации, коды валют берутся из стандарта ISO 4217. Мне не удалось найти оригинальный список кодов валют по этому стандарту не в PDF, но зато в онлайне есть наш ОКВ, который основан на этом стандарте.
Подробнее: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
#frontend #intl
Несколько месяцев назад, я уже рассказывал вам про такую вещь, как Intl API. Тогда мы разбирались с датами, сегодня предлагаю научиться форматировать валюты с помощью следующей функции:
const formatCurrency = (value, locale, currencyCode, config = {}) => {
return new Intl.NumberFormat(locale, {
style: "currency",
currency: currencyCode,
...config,
}).format(value)
}
console.log(formatCurrency(123456, "ru-RU", "RUB"))
// 123 456,00 р.
console.log(formatCurrency(123456, "en-US", "USD"))
// $123,456.00
Аргумент
config
был добавлен мною для возможности расширения текущей конфигурации экземпляра Intl
, например, можно добавить параметр maximumSignificantDigits
для округления до 3 значащих цифр:console.log(formatCurrency(123456.789, 'ru-RU', 'RUB', { maximumSignificantDigits: 3 }))
// 123 000 р.
По-хорошему, бы тут добавить какую-то проверку, что аргументы передаваемые в
config
действительно находятся внутри Intl.NumberFormat
и корректно им будут обработаны, а также защиту от перезаписывания заданных нами параметров, но моя цель была только в том, чтобы поделиться простым способом форматирования чисел с помощью встроенного браузерного API.Если верить спецификации, коды валют берутся из стандарта ISO 4217. Мне не удалось найти оригинальный список кодов валют по этому стандарту не в PDF, но зато в онлайне есть наш ОКВ, который основан на этом стандарте.
Подробнее: https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
1👍5❤4