Вещи, которые полезно знать о React.js / Хабр | Веб-студия Nat.od.ua
Вещи, которые полезно знать о React.js / Хабр
Несколько слов о мотивации написать эту статью. Большенство вещей, о которых я тут хочу рассказать, вы можете узнать из документации React. Но проводя собеседования последние пару лет я понял, что многие разработчики о них не знают по каким-то причинам, может просто не вчитывались внимательно или этому не уделено достаточного внимания в самой документации. Так что я решил собрать тут несколько моментов и разобрать их, чтобы тот кто прочитает эту статью, начал понимать React чуть лучше и стал более осознанно использовать те или иные инструменты в работе. Так же это будет полезно тем, кто хочет подготовиться к собеседованию и не ударить в грязь лицом, когда их будут гонять по реакту.
Возвращаемая функция для сброса в useEffect срабатывает чаще чем вы думаете
Многие разработчики считают, что функция, которую мы возвращаем в useEffect работает как метод жизненного цикла componentWillUnmount и будет срабатывать только при размонтировании компонента, но это не совсем так. Помимо размонтирования компонента она так же будет срабатывать при каждом ререндере компонента, если не передать список зависимостей или при каждом изменении элемента из списка зависимостей, при чем сначала отработает возвращаемая функция, а потом тело эффекта.
Пример на codesandbox
Почему это так: разработчики реакт решили реализовать в useEffect весь финкционал методов жизненного цикла (почти). Представьте себе ситуацию, когда вам нужно установить какой-нибудь timeout, который должен как-то работать с состоянием компонента. Как вы бы сделали это в классовом компоненте:
componentDidMount() {
const { stateForTimer } = this.state;
this.timer = setTimeout(() => console.log(stateForTimer), 1000);
}
-
в componentDidUpdate сбрасывали бы этот таймер и устанавливали заново, если мы это не сделаем, в таймере будет неактуальное состояние.
componentDidUpdate() {
const { stateForTimer } = this.state;
clearTimeout(this.timer);
this.timer = setTimeout(() => console.log(stateForTimer), 1000);
}
-
в componentWillUnmount мы отписываемся от таймера, тк не хотим, чтобы он сработал, если у нас уже нет компонента на экране.
componentWillUnmount() {
clearTimeout(this.timer);
}
-
Но таким образом наш таймер будет отрабатывать при каждом ререндере, в родительском компоненте, чтобы этого не происходило мы можем вместо Component использовать PureComponent
export default class ClassComponentTimer extends React.PureComponent {
constructor() {
super();
this.state = { stateForTimer: «state For Timer» };
this.timer = null;
}
componentDidMount() {
const { stateForTimer } = this.state;
this.timer = setTimeout(() => console.log(stateForTimer), 1000);
}
componentDidUpdate() {
const { stateForTimer } = this.state;
clearTimeout(this.timer);
this.timer = setTimeout(() => console.log(stateForTimer), 1000);
}
componentWillUnmount() {
clearTimeout(this.timer);
}
inputHandler = (e) => {
this.setState({ stateForTimer: e.target.value });
};
render() {
return (
Class timer
);
}
}
Согласитесь, получилось довольно многословно.
Вы могли заметить, что мы отписываемся от таймера 2 раза и подписываемся тоже 2 раза. так вот чтобы не повторяться, разработчики реакт в хуках объединили этот механизм в одном месте и теперь наш таймер сбросится, когда происходит ререндер или, если есть список зависимостей, когда меняется зависимость, чтобы избежать работы с неактуальными данными, и когда произойдет unmount компонента, чтобы не произошли сайдэфекты, которые нам уже по сути не нужны.
export default function App() {
const = useState(«state For Timer»);
useEffect(() => {
const timeout = setTimeout(() => console.log(stateForTimer), 1000);
return () => clearTimeout(timeout);
}, );
const inputHandler = (e) => {
setStateForTimer(e.target.value);
};
return (
Function timer
);
}
В плане лаконичности компоненты на хуках конечно выигрывают.
Весь код, вызываемый внутри функционального компонента будет вызываться при каждом рендере
Будьте внимательны, когда пишете код внутри функционального компонента, тк он будет вызываться при каждом ререндере.
пример на codesandbox
Это значит, что какие-то сложные вычисления не стоит делать внутри компонента напрямую, а сайдэфекты вообще нельзя, потому что они будут срабатывать бесконтрольно, лучше сделать это в useEffect или в useMemo.
Правда если мы обернем компонент в memo, то код внутри компонента будет отрабатывать только при изменении стэйта или пропсов, но все равно лучше вычисления и сайдэфекты лучше отдать на откуп useEffect или useMemo, тк мы можем контролировать, по изменению чего конкретно нам нужно перевызвать этот код.
Вызывать функции в useState будет отрабатывать на каждый ререндер
По той же причине, по которой не стоит делать сложные вычисления и сайдэфекты напрямую внутри компонента, не стоит вызывать функции внутри useState.
пример на codesandbox
Если вам нужно вычислить какое-то стартовое значение для вашего состояния, можно положить его в анонимную функцию и тогда он отработает только при маунте компонента.
import { useState } from «react»;
const twoSquared = () => {
console.log(«do some maths once»);
return 3 * 3;
};
export default function StateOnce() {
const = useState(() => twoSquared());
return (
3 * 3 = {nine}
);
}setState в функциональных компонентах может принимать функцию
В основной части документации об этом вообще нет ни слова (по крайней мере я не нашел), об этом говорится только в подробном АПИ хуков, но и то только то, что вы можете передавать в setState функцию и тогда в качестве аргумента получите предыдущее значение состояния. Но ни слова не говорится, зачем это вообще нужно. А все дело в том, что setState — асинхронная операция и если вы например в каком-нибудь цикле попытаетесь поработать с текущим значением состояния, вы получите одно и то же состояние столько раз, сколько проитерируетесь в этом цикле. Передача функции в setState исправляет эту проблему.
Выведится 5 раз 5
Выведится от 1 до 5
}
Пример немного надуманный, но может быть ситуация, когда вам нужно пробежаться по какому-нибудь массиву и на каждый элемент этого массива вызвать setState — это уже что-то более реалистичное.
Пример кода на codesandbox
Помимо useEffect есть еще спецефичный хук useLayoutEffect
useLayoutEffect так же как и useEffect сработает после рендера, но в отличии от useEffect срабатывает он синхронно.
Это значит, что при использовании useEffect у браузера будет какое-то время для отрисовки контента, а значит, что если мы пытаемся что-то отрендерить, а в useEffect мы например следим за тем, что пытаемся отрендерить и хотим перехватить это и отрисовать вместо этого какое-то другое значение, то мы увидем небольшое моргание. Но лучше один раз увидеть пример, чем сто раз прочитать его описание.
Пример кода на codesandbox
в примере из codesandbox потыкайте много раз подряд на кнопку «Get name with delay», вы увидите небольшое мерцание.
Тут происходит следующее:
-
По нажатию на кнопку мы устанавливаем Имя в состояние компонента, оно записывается в виртуальный DOM
-
Запускается useEffect
-
Код внутри него откладывается в асинхронную очередь
-
js завершает все операции что лежат в стэке вызовов (привет Event loop), в том числе отображение пользователю того, что мы записали в состояние компонента по нажатию на кнопку.
-
После того, как стэк вызовов опустел, мы переходим к очереди, в которой и лежит наш колбэк из useEffect
-
Мы его выполняем, меняется виртуальное дерево
-
Рендерится уже то, что мы задали в состояние внутри этого колбэка.
useLayoutEffect же вместо того, чтобы отправлять колбэк в асинхронную очередь, сразу же выполняет код, ну точнее не совсем сразу, сначала отрабатывает setState из кнопки, идет изменение в виртуальном дом дереве, после этого отрабатывает useLayoutEffect, но он в отличии от useEffect не откладывает колбэк в асинхронную очередь вызовов, а выполняет его сразу, еще раз меняется виртуальное ДОМ дерево и после этого уже происходит ререндер реального браузерного дерева. Попробуйте быстро потыкать кнопку «Get name without delay», моргания вы не увидите.
Аккуратно с 0 при проверке на необходимость рендеринга
Напоследок минипредостережение. В обычном JS коде мы часто делаем такую проверку:
if(!arr.length) {
//если длинна массива !== 0, работаем с элементами массива
arr.map((item) => {
//ваш код
})
}
И кажется логичным сделать такую же проверку в рендере компонента:
items.length && items.map((item) =>
)
Но реакт в таком случае отработает не так как мы ожидаем. Если длинна массива больше нуля, то все ок, мы пробежимся по массиву и отрендерим что нужно, но если она равна 0, то он вместо того, чтобы отрендерить нам «ничего», отобразит 0.
Для такого случая можно использовать такую проверку:
import «./styles.css»;
const items = [];
export default function App() {
return (
Check array on arr.length:
{items.length && items.map((item) =>
)}
Check array on arr.length {«>»} 0:
{items.length > 0 && items.map((item) =>
)}
);
}
Надеюсь эта статья была кому-нибудь полезна. Если вы увидели какие-то ошибки, буду рад, если вы напишете о них, я обязательно внесу исправления.
Создаем слайдер с изображением и текстом на React.js с нуля и оптимизируем / Хабр | Веб-студия Nat.od.ua
Создаем слайдер с изображением и текстом на React.js с нуля и оптимизируем / Хабр
В этой статье я хочу затронуть задачу, с которой вы можете столкнуться на собеседовании на позицию Front-End — создание Image Slider.
За последние 5 месяцев у меня было 15 онсайт собеседований, а также офферы от Google, Roku, Microsoft и других. (Больше информации можно прочитать в моем Telegram-канале)
Вы должны реализовать этот виджет за ~45–50 минут и рассказать об оптимизации. Эту информацию я постараюсь рассказать здесь. Основная цель состоит не в том, чтобы реализовать Image Slider с большим количеством функционала, а в том, чтобы показать, как реализовать и оптимизировать.
Требования
Начнем с требований нашего виджета.
-
Показывать изображения с котиками из API с ограничением количества слайдов.
-
Показывать описание или заголовок для каждого изображения.
-
Навигация между слайдами с помощью стрелок.
-
Возможность перехода на любой слайд.
-
Автопроигрывание для слайдера.
-
Возможность настроить ширину и высоту слайдера.
-
Слайдер должен быть респонсив.
-
Изображения слайдера должны загружаться эффективно и отображаться как можно быстрее.
МакетРендеринг в браузере
Для первой и самой простой реализации мы выведем все слайды в браузере и будем показывать только часть во вьюпорте или в элементе слайдера (когда мы задаем ширину или высоту). Этот подход загружает все изображения для всех слайдов и имеют N DOM-элементов, где N — количество слайдов.
Архитектура компонентовКод компонентов
Давайте начнем с пропсов нашего Slider-компонента. С помощью этоих пропсов мы можем конфигурировать наш слайдер.
{
autoPlay: boolean,
autoPlayTime: number,
width: ‘%’ | ‘px’,
height: ‘%’ | ‘px’,
}
В компоненте Slider нам нужно реализовать следующий функционал:
-
загружать изображения;
-
реализовать метод навигации по стрелкам;
-
реализовать метод навигации по точкам;
-
функциональность автопроигрывания;
-
рендеринг слайдов, стрелок и точек.
Мы будем хранить текущий номер слайда и загруженные данные в локальном стейте компонента. Чтобы избежать большого количества прокидывания пропсов, предлагаю использовать Context для прокидывания методов навигации по слайдам и текущей информации о слайдере.
Как вы можете видеть в архитектуре компонентов и коде, компонент Slider содержит 3 компонента: SlideList, Arrows и Dots.
У нас есть 2 стрелки слева и справа.
Для отображения стрелок с обеих сторон мы можем стилизовать их с помощью CSS.
Мы знаем количество слайдов и можем отрендерить нужное количество точек.
Каждая точка выглядит следующим образом.
Для отображения слайдов в SlideList мы можем получить элементы из контекста и визуализировать компонент Slide с ключами и данными о слайде.
Для достижения такой анимации нужно использовать transform и translateX в стилях. Мы перемещаем наш контент на следующий слайд по номеру слайда в нашем массиве.
Компонент Slide содержит 2 компонента: SlideImage и SlideTitle. Эта архитектура позволяет добавлять новые функции на будущее для каждого слайда.
Как оптимизировать решение?
Представьте, что у вас в слайдере много изображений и вам нужно его оптимизировать. Оптимизация зависит от анимации смены слайдов… да, анимации.
Я вижу здесь 2 пути оптимизации.
Давайте посмотрим на них.
Оптимизация с рендерингом 3-х слайдов
Если вы хотите использовать transform и translateX для смены слайдов, вы можете использовать следующую оптимизацию.
В этом решении рендерится только 3 слайда одновременно. Активный слайд посередине, предыдущий и следующий слайды. Это потому, что пользователь может нажимать на стрелки вперед или назад, или в случае автовоспроизведения мы каждый раз перемещаемся вперед. Это позволяет быстро показать контент пользователю.
Когда мы переходим к предыдущему или следующему слайду, мы вычисляем новые 3 слайда и рендерим их.
Оптимизация с рендерингом одного слайда
Если вы хотите использовать animation в CSS, вы можете использовать один слайд и каждый раз отображать один слайд с информацией.
Пример анимационных эффектов, которые можно применить.
Для этой оптимизации необходимо добавить некоторые изменения в коде.
Теперь нет необходимости использовать компонент SlidesList, мы должны рендерить только один компонент Slide (строка 99).
Также мы должны контролировать эффект анимации и применять его только при смене содержимого слайда (строка 41).
И последнее изменение, для быстрого отображения картинок пользователю мы должны предварительно загружать предыдущее и следующее изображения относительно текущего слайда (строка 25).
В Slide нам нужно только одно изменение. Добавим функционал для класса с анимацией, когда запускается смена слайдов.
Реализация анимации fadeIn в стилях.
Другие оптимизации
-
Правильный размер изображения — нет необходимости использовать разрешение Full HD, если у вас слайдер с ограниченным размером и он меньше.
-
Использовать формат WebP для изображений — позволяет уменьшить размер изображений. Он сжимает как минимум на 20–30% лучше, чем jpg и png.
-
Не используйте изображения со 100-процентным качеством. Нет необходимости использовать изображения со 100-процентным качеством. По моему опыту, качество 70—85% выглядит так же, как и 100%, но размер изображения меньше.
-
Минимизируйте JavaScript и стили.
-
Используйте CDN для хранения изображений.
-
Используйте Brotli для сжатия — конечный размер данных меньше gzip на 14–21%.
Заключение
Не нужно обладать какими-то специальными знаниями, чтобы реализовать свой собственный слайдер изображений. Вам нужно попрактиковаться в этом без гугления и за ограниченное время.
Весь код вы можете найти на GitHub.
В следующих статьях я расскажу о виджете Tree View, виджете Star Rating, дизайне Google Doc и Google Sheets.
Удачи на собеседованиях!
Создание WebCron плагина для Joomla 4 (Task Scheduler Plugin) / Хабр | Веб-студия Nat.od.ua
Создание WebCron плагина для Joomla 4 (Task Scheduler Plugin) / Хабр
Эта статья — дополненный перевод статьи How to Create Joomla Task Scheduler Plugin.
В Joomla! появился планировщик задач начиная с версии 4.1. Он помогает автоматизировать повторяющиеся и рутинные задачи самого широкого спектра, начиная от технического обслуживания и заканчивая сложными синхронизациями по API.
Планировщик задач запускает задачу, определенную в плагине, с помощью задания CRON.
Задания CRON (CRON Job): Задание CRON используется для автоматизации повторяющихся задач. Например, для обработки очередей электронной почты, для синхронизации заказов или остатков товаров, обновления цен интернет-магазина, для проверки веб-сайта на наличие проблем и так далее.
Также о технологии WebCron в Joomla 4.1 сообщалось в обзоре Новое в Joomla 4.1 на Хабре, в разделе «WebCron в Joomla 4.1«.
Плагин планировщика задач Joomla 4 (Task Scheduler)
Это стандартный Joomla! плагин с group=»task». В нём необходимо использовать TaskPluginTrait.
use JoomlaCMSPluginCMSPlugin;
use JoomlaComponentSchedulerAdministratorEventExecuteTaskEvent;
use JoomlaComponentSchedulerAdministratorTaskStatus;
use JoomlaComponentSchedulerAdministratorTaskTask;
use JoomlaComponentSchedulerAdministratorTraitsTaskPluginTrait;
use JoomlaEventSubscriberInterface;
class PlgTaskExample extends CMSPlugin implements SubscriberInterface
{
use TaskPluginTrait;
protected const TASKS_MAP = array(
‘plg_task_do_example’ => array(
‘langConstPrefix’ => ‘PLG_TASK_DO_EXAMPLE’,
‘method’ => ‘doExample’,
‘form’ => ‘do_example’
)
);
protected $autoloadLanguage = true;
protected $app;
protected $db;
public static function getSubscribedEvents(): array
{
return array(
‘onTaskOptionsList’ => ‘advertiseRoutines’,
‘onExecuteTask’ => ‘standardRoutineHandler’,
‘onContentPrepareForm’ => ‘enhanceTaskItemForm’,
);
}
private function doExample(ExecuteTaskEvent $event): int
{
// Code for Tasks
return Status::OK;
}
}Реализация
Плагин планировщика задач в Joomla 4 должен использовать TaskPluginTrait и определять методы, соответствующие каждой процедуре вместе с константой класса TASKS_MAP для объявления поддерживаемых процедур и связанных с ними свойств. TaskPluginTrait включает в себя стандартные методы для трансляции процедур (routines), улучшения форм задач и вызов подзадач.
advertiseRoutines()
Этот метод объявляет процедуры, поддерживаемые плагином. Он должен быть сопоставлен с событием onTaskOptionsList, позволяющим плагину объявлять свои процедуры. Метод уже определен в трейте, поэтому Вам не требуется определять его в вашем плагине.
enhanceTaskItemForm()
Этот метод расширяет форму задачи из XML-файла плагина, объявленного с помощью константы TASKS_MAP. Этот метод может быть сопоставлен с событием onContentPrepareForm. Этот метод уже определен в трейте, поэтому вам не требуется определять его в вашем плагине.
Вам нужно добавить имя формы в TASKS_MAP и константу и создать XML-форму в папке forms плагина с тем же именем.
Пример из кода плагина «Задача — Запросы GET». В TASKS_MAP указан файл формы для настроек плагина. Событие onContentPrepareForm в getSubscribedEvents позволит его отобразить в админке.standardRoutineHandler()
Метод сопоставляет стандартные процедуры задачи с методами класса. Вы можете добавить имя метода в константу TASKS_MAP и определить его в своем плагине. Выполняться метод будет при запуске задачи.
Этот метод должен быть сопоставлен с событием onExecuteTask. Ожидается, что эти методы будут принимать один аргумент (событие) и возвращать статус задачи в виде числа (integer).
logTask()
Этот метод добавляет сообщение в логи. Первый аргумент — это сообщение, а второй аргумент — приоритет: отладка, ошибка, предупреждение, уведомление, информация.
$this->logTask(‘Task is being executed’, ‘info’);Основной метод для выполнения задачи (doExample)
В примере выше это метод doExample.
use JoomlaComponentSchedulerAdministratorEventExecuteTaskEvent;
use JoomlaComponentSchedulerAdministratorTaskStatus;
/**
* Standard routine method for the get request routine.
*
* @param ExecuteTaskEvent $event The onExecuteTask event
*
* @return integer The exit code
*
* @since 4.1.0
* @throws Exception
*/
private function doExample(ExecuteTaskEvent $event): int
{
// Code for Tasks
return Status::OK;
}
В качестве параметра он принимает объект события onExecuteTask. Для выполнения задачи нам могут понадобится параметры, указанные в настройках плагина задачи. Получить параметры плагина можно с помощью метода $event->getArgument(‘params’)
$id = $event->getTaskId();
// Получаем параметры плагина, указанные в настройках
$params = $event->getArgument(‘params’);
$url = $params->url;
$timeout = $params->timeout;
$auth = (string) $params->auth ?? 0;
$authType = (string) $params->authType ?? »;
$authKey = (string) $params->authKey ?? »;
По завершении работы основного метода он должен вернуть статус выхода задачи.
Статусы выхода задачи в Joomla
На данный момент в Joomla 4 существует 12 возможных статусов завершения задачи, которым назначены числовые значения.
-
INVALID_EXIT = -2: используется, когда процедура возвращает неверный (не целочисленное, non-integer) статус завершения задачи.
-
NO_EXIT = -1: используется, когда процедура не возвращает статус завершения задачи..
-
RUNNING = 1: Используется при старте задачи. Это значение не должно быть статусом завершения задачи.
-
NO_LOCK = 2: Используется при невозможности получения pseudo-lock.
-
NO_RUN = 3: Используется при ошибке запуска задачи.
-
NO_RELEASE = 4: Используется при невозможности снять блокировку или обновить запись.
-
KNOCKOUT = 5: Используется, когда процедура «валится» из-за выкинутых исключений (Exception).
-
WILL_RESUME = 123: Используется, когда необходимо возобновить выполнение задачи. Используйте это для длительных задач, чтобы разделить их на более мелкие пакеты. Когда будет выполнен последний пакет, верните Status::OK.
-
TIMEOUT = 124: Используется в случае таймаута задачи.
-
NO_TASK = 125: Используется в случае, если задача не обнаружена.
-
NO_ROUTINE = 127: Используется в случае, если процедура задачи не обнаружена.
-
OK = 0: Успешное завершение задачи.
Пример из кода плагина Задача — GET-запрос
use JoomlaComponentSchedulerAdministratorTaskStatus as TaskStatus;
protected function makeGetRequest(ExecuteTaskEvent $event): int
{
…
try
{
$response = HttpFactory::getHttp($options)->get($url, $headers, $timeout);
}
catch (Exception $e)
{
$this->logTask(Text::sprintf(‘PLG_TASK_REQUESTS_TASK_GET_REQUEST_LOG_TIMEOUT’));
return TaskStatus::TIMEOUT;
}
…
$this->logTask(Text::sprintf(‘PLG_TASK_REQUESTS_TASK_GET_REQUEST_LOG_RESPONSE’, $responseCode));
if ($response->code !== 200)
{
return TaskStatus::KNOCKOUT;
}
return TaskStatus::OK;
}
Принимаются пожелания по улучшению статьи и качества перевода 🙂
Так же полезные ресурсыРесурсы сообщества:Telegram:
Фронтенд-новости №12. Вышел EcmaScript 2022, фавиконки в 2022, как будет выглядеть веб только с Chromium | Веб-студия Nat.od.ua
Фронтенд-новости №12. Вышел EcmaScript 2022, фавиконки в 2022, как будет выглядеть веб только с Chromium
Дайджест новостей и полезных статей из мира фронтенд-разработки за неделю 20–26 июня.
?♂️Доступность
? Выбор даты и времени для всех. Ребята из Adobe сделали библиотеку react spectrum более доступной для выбора даты и времени. Очень полезно посмотреть со стороны UX на взаимодействие с формами выбора даты и времени, так как это действительно сложно.
? Как сделать переключатель тёмного режима. Применение ARIA, когда он необходим, то есть отсутствует в HTML и приносит пользу пользователю.
? Библиотека ARIA шаблонов для популярных элементов. Узнайте, как делать самые распространенные элементы более доступными, применяя роли, состояния и свойства WAI ARIA.
? Спецификации
? Page Visibility Level 2. Вышел новый уровень спецификации для состояния видимости документа.
? EcmaScript 2022. 22 июня 2022 года 123 Генеральная ассамблея Ecma утвердила спецификацию языка ECMAScript 2022 — теперь он официально является стандартом. Внутри все новинки.
? HTML
? Всплывающие окна мертвы, да здравствуют всплывающие окна. Узнайте, как Великобритания планирует убить всплывающие окна с файлами cookie.
? Семантическая карточка-ссылка. Семантика для обычных карточек: карточка товара или карточка статьи блога.
? Как создавать иконки сайтов в 2022 году — всё о favicon. Пришло время переосмыслить то, как мы создаем набор иконок для современных браузеров, и остановить безумные генераторы.
?️ CSS
?Современная CSS-прокрутка. Большинство интерфейсов имеют полосу прокрутки у страницы или блока. Исторически так сложилось, что настройка полосы прокрутки никогда не была лёгким занятием. Кто-то даже умудряется написать тысячи строк кода, чтобы повторить работу полосы прокрутки на JavaScript.
Полосы прокрутки полезно стилистически изменять, когда меняется тема сайта с светлой на тёмную, иначе полоса прокрутки будет пестрить и перетягивать на себя внимание.
Также стоит помнить, что в различных операционных системах, десктопных и мобильных, у полос прокрутки меняется поведение и возникают кроссбраузерные проблемы.
У oveflow: auto тоже есть проблемы. Если в блоке становится много контента, то появляется полоса прокрутки, которая смещает следующий контент.
В статье рассматриваются современные CSS-возможности по настройке полосы прокрутки.
? Пожалуйста, дайте мне немного места в разметке для разделения слов. Текстовыми параметрами в стилях в тексте между словами и буквами. Трансформациями и позиционированием. Флексами. У таблиц с помощью стилей. Прозрачными блоками-заполнителями. Для каждой ситуации подойдёт свой способ.
? Две строки CSS, повышающие производительность рендеринга. Коротко:
{
content-visibility: auto;
contain-intrinsic-size: 1px 5000px;
}
-
content-visibility: auto — определяет будет браузер пропускать этапы paint и rendering для элемента. Поддержка браузерами.
-
contain-intrinsic-size — в связи с предыдущим пунктом элементы, которые не прошли этап rendering, скорее всего, будут иметь размер 0x0. contain-intrinsic-size позволяет подсказать браузеру действительные размеры элемента, чтобы интерфейс не прыгал. Поддержка браузерами.
? Многоточие для переполнения текстов вредно. Для эффективной работы text-overflow часто приходится использовать дополнительные CSS-свойства white-space: nowrap и overflow: hidden, но это может плохо сказаться на кнопках.
? Условное оформление выбранных элементов в гридах. Тренируемся в сложных селекторах, в которых используются комбинаторы и псевдоэлементы.
? Как и когда использовать :has. Теория о том, что такое :has, как с ним работать и применять в карточках и формах.
?️ JavaScript
? Блочный оператор. Помогает организовать код, придумать другое имя для той же переменной.
? В защиту блочного оператора
? Когда стоит отказаться от Map в пользу объекта
-
Используйте объект для записей, где у вас есть фиксированное и ограниченное количество свойств или полей, известных на момент создания, например, конфигурационных объектов. И вообще для всего, что предназначено для одноразового использования.
-
Используйте Map для словарей или хэш-карт с переменным количеством записей, с частыми обновлениями, ключи которых могут быть неизвестны в момент создания, например, эмиттер события.
-
Согласно тестам, если только ключи не являются строками маленьких целых чисел, Map действительно более производителен, чем объект, по скорости вставки, удаления и итерации, и он потребляет меньше памяти, чем объект того же размера.
? Точное определение времени с помощью API веб-анимации. Таймеры — необходимость для разработчика, чтобы точно знать, когда что происходит. Но на самом деле таймеры никогда не бывают вовремя. Web Animations API поможет отказаться от некоторых таймеров в некоторых случаях.
? Заметки о сервис-воркерах. Вещи, которые вы бы хотели знать раньше о сервис-воркерах.
? JavaScript: интересные возможности AbortController. Паттерны для AbortController.
? Как в современном мире обрабатывать ошибки в Javascript?
?React
? Над чем работает команда React. Команда решила начать заранее делиться тем, над чем они трудятся:
? Миграция нативных React библиотек на новую архитектуру. Обновлённые рекомендации для перехода приложения и библиотек на новую архитектуру.
?Организация react-компонентов с помощью dot-notation и почему я часто прибегаю именно к этому способу. Существует несколько способов организации компонентов и каждый из них полезен в конкретной ситуации. И нужно выбирать подходящий дизайн его реализации в зависимости от функциональности компонента и его предназначения.
? Node.js
? Используйте веб-потоки в node.js. Web-streams — это стандарт для потоков, который теперь поддерживается на всех основных веб-платформах: браузеры, node.js и Deno.
? Альтернативы глобальной установки npm-пакетов. Для npm install —global package-name в macOS и некоторых Unix-платформах требуются root-права. Узнайте об альтернативных вариантах —global.
? Небольшой релиз v18.4.0
? Анализатор аргументов командой строки. Встроен в v18.3.0. Не такой мощный, как yargs , minimist или argparse, но уже можно пробовать.
? Angular
?Как Computed Properties в Angular помогают пропускать титры
?Улучшение начальной загрузки приложения
? Vue
?Vue 2.7 в бете
А также на русском на Хабре.
? Тернистый путь к микрофронтам. Ребята из Азбуки вкуса делятся своим путём обновления приложения до Vue 2 + Nuxt 2 с поддержкой TypeScript.
? Использование v-model в Vue 3 для создания сложных форм
?️ Инструменты
-
Puppeteer 15.0
-
grammY — современная платформа Telegram-ботов
-
PSD 0.2 — синтаксический анализатор PSD с нулевой зависимостью для браузера или node.js
-
TypeScript beta 4.8 с улучшением производительности
?️Браузеры
? Что Github думает о браузерах. А точнее, какие версии браузеров, какое браузерное API поддерживают и какими инструментами они пользуются для кроссбраузерности.
? Как будет выглядеть веб только с Chromium?
? Safari Technology Preview 147
-
Live text. Переводите и взаимодействуйте с текстом внутри видео и изображениях на Venture и M-процессорах.
-
Web Push. Отправляйте пуш-уведомления со страницы в браузере.
-
Passkeys. Обезопасьте свои учётные данные на сайтах.
-
Improved Safari Web Extensions. Протестируйте улучшенный API для расширений.
-
Web Inspector Extensions. Создайте собственный инструмент для веб-инспектора.
-
Flexbox Inspector. Используйте новую визуальзацию флексов в веб-инспекторе.
? Chrome 105. Актуальная версия 103. А в 105 версии появится псевдокласс :has.
? Текущий Safari в iOS. Имеет баг перекрытия нескольких фулскрин-видео. Safari не проверяет, если ли другой ресурс, который уже получил полноэкранный доступ.
? Ку-ку. Internet Explorer никуда не делся. То, что поддержка прекращена, ещё не значит, что им перестанут пользоваться.
?Общее
? Взлет и падение неоморфизма. Скеоморфизм + плоский дизайн = Неоморфизм. Прошлое, настоящее, будущее, и почему неоморфизм быстро входит и выходит из моды.
? Что такое минималистичный веб-дизайн простыми словами. Часто дизайнеры воспринимают минимализм как набор необходимых элементов без добавления лишних деталей. Однако этим стиль не ограничивается. Основная идея минимализма — акцент на содержании сайта, а не на его дизайне.
Главные особенности минимализма в дизайне: простота, чистые цвета, изображения без фона, пространство, композиция, типографика и UX.
? Сочетания шрифтов от Google. Чтобы начать разбираться в шрифтовых парах, начните со статьи «Парные шрифты». В этой статье познакомимся с 9 парами. С этими парами можно поиграться в Figma.
Чтобы правильно подобрать пару нужно, чтобы пары вели к одной цели, которую вы хотите достичь:
-
были легко читаемыми;
-
имели различные веса и стили;
-
поддерживали необходимых языков;
-
имели интересные глифы, если это необходимо для цели.
? Прощай, Web3. Web5 уже здесь. Не успели вы даже понять, что такое Web3, а уже Web5. А Web4 вообще был? Web2 + Web3 = Web5.
? Как Apple может бить CAPTCHA? С помощью токенов частного доступа.
? Figma vs Sketch. Узнайте какой инструмент лучше подходит для UX/UI, прототипирования и совместной командной работы.
Обратная связь
Привет. Я поменял формат дайджеста и буду благодарен за обратную связь, чтобы сделать его ещё лучше. Спасибо.
Как читать статьи на английском языке
В дайджесте много статей и видео на английском языке, чтобы это не стало препятствием: в Google Chrome есть функция перевода страницы с любого популярного языка, а видео можно перевести в Яндекс Браузере.
Обзор паттернов хранения деревьев в реляционных БД / Хабр | Веб-студия Nat.od.ua
Обзор паттернов хранения деревьев в реляционных БД / Хабр
Всем привет! Меня зовут Пантелеев Александр и я бэкенд-разработчик в компании Bimeister.
Постараюсь описать исчерпывающе, кратко и понятно суть основных паттернов хранения деревьев в реляционных базах данных. Надеюсь, что статья будет полезна тем, кто до сего момента не сталкивался с такими паттернами, и станет отправной точкой в их понимании.
В этой статье не будет терминов реляционной алгебры или базы данных: таких как атрибут, домен и т. д. Также не будет привязки к какой-либо СУБД, какому-либо SQL или пользовательскому коду.
Всего существует 4 общепринятых паттерна хранения деревьев:
-
Adjacency List;
-
Nested Sets;
-
Closure Table;
-
Materialized Path.
Кратко рассмотрим каждый из них.
Adjacency ListОписание
Это самый простой и интуитивный вариант хранения. Каждому элементу сопоставляется его свойство — его родительский элемент. Если родительский элемент не задан, то он считается корневым элементом.
Когда связь сопоставления элемента и родительского элемента хранится отдельно от элемента, Adjacency List можно рассматривать как частный случай Closure Table со связями 1 уровня.
Преимущества
Лёгкость реализации, а также простота вставки, удаления и перемещения элементов в дереве.
Недостатки
Можно получить только непосредственные дочерние элементы. Чтобы получить все дочерние элементы, необходимо выполнить рекурсивный запрос либо производить множественные запросы.
ПримерыРисунок 1.
Элемент
Родительский элемент
A
—
B
A
C
B
D
C
E
B
F
B
G
A
H
G
I
A
Рассмотрим элемент «B»:
Чтобы получить все его дочерние элементы, нам необходимо выбрать элементы, удовлетворяющие условию:
Родительский элемент равен «B»
Nested SetsОписание
Каждому элементу сопоставляются свойства: левый и правый индекс, на основе которых будет производиться выборка дочерних элементов. Также, но необязательно, элемент может дополняться свойством уровень для указания желаемого уровня вложенности выбираемого элемента относительно корня или родительского элемента.
Запрос получения дочерних элементов строится на том факте, что для любого дочернего элемента выполняются условия:
-
левый индекс больше левого индекса родительского элемента;
-
правый индекс меньше правого индекса родительского элемента.
При создании и обновлении дерева левые и правые индексы элементов дерева, при его обходе в глубину, заполняются по определённым правилам.
Преимущества
Возможность получения дочерних элементов любых уровней вложенности с помощью простого одиночного запроса.
Недостатки
При использовании целочисленных типов для левого и правого индекса и уровня необходимо пересчитывать индексы всех связанных элементов в следующих случаях:
-
при вставке элементов;
-
при удалении элементов;
-
при изменении родительского элемента.
ПримерРисунок 2.
Элемент
Левый индекс
Правый индекс
Уровень
A
1
18
0
B
2
11
1
C
3
6
2
D
4
5
3
E
7
8
2
F
9
10
2
G
12
15
1
H
13
14
2
I
16
17
1
Рассмотрим элемент «B». Его значения свойств:
Чтобы получить все его дочерние элементы, нам необходимо выбрать элементы, удовлетворяющие условию:
левый индекс больше 2 И правый индекс меньше 11
Чтобы получить его непосредственные дочерние элементы, нам необходимо добавить к условию ограничение на уровень:
левый индекс больше 2 И правый индекс меньше 11 И уровень = 1
Чтобы получить дочерние элементы вместе с родительским элементом, нам необходимо ослабить условия индексов:
левый индекс больше или равен 2 И правый индекс меньше или равен 11
Closure TableОписание
Суть этого паттерна заключается в том, что мы сопоставляем каждому элементу множество связей со всеми его дочерними элементами или сопоставляем каждому элементу множество связей со всеми его родительскими элементами. Также, но необязательно, связь может содержать свойство Уровень. Уровень задаёт расстояние между элементами в дереве.
Если в запросе получения дочерних или родительских элементов по элементу необходимо получать в результате сам элемент, то нужно добавлять связь элемента самого на себя — то есть со значением уровня связи 0.
Преимущества
Возможность получения дочерних элементов любых уровней вложенности с помощью простого одиночного запроса.
Возможность получения родительских элементов любых уровней с их иерархией относительно дочернего элемента с помощью простого одиночного запроса.
Недостатки
При вставке и удалении элементов из дерева, а также при перемещении элементов в дереве необходимо пересчитывать все связи, в которых этот элемент участвует.
ПримерРисунок 3.
Родительский элемент
Дочерний элемент
Уровень
A
A
0
A
B
1
A
C
2
A
E
2
A
D
3
B
B
0
B
C
1
B
E
1
B
D
2
C
C
0
C
D
1
E
E
0
D
D
0
Рассмотрим элемент «B»:
Чтобы получить все его дочерние элементы, нам необходимо выбрать элементы, удовлетворяющие условию:
родительский элемент равен «B»
Чтобы получить его непосредственные дочерние элементы, нам необходимо добавить к условию ограничение на уровень:
родительский элемент равен «B» И уровень = 1
Чтобы получить дочерние элементы вместе с родительскими, нам необходимо ослабить условия индексов:
родительский элемент равен «B» И уровень = 0
Чтобы получить все его родительские элементы, нам необходимо выбрать элементы, удовлетворяющие условию:
дочерний элемент равен «B»
Materialized PathОписание
Каждому элементу сопоставляется свойство — его путь, который является последовательностью родительских элементов заданного элемента, отсортированных по уровням. В общем случае, чтобы формировать гибкие запросы, тип реализации свойства путь должен поддерживать сопоставление по шаблону в каком-либо виде. При денормализации пути в отдельную таблицу получается разновидность Closue Table.
Условия запросов на получение элементов заключается в применении предиката над свойством путь.
Преимущества
-
Возможность получения дочерних элементов любых уровней вложенности.
-
Возможность получения родительских элементов любых уровней с их иерархией относительно дочернего элемента.
-
Лёгкость вставки элемента.
-
Лёгкость удаления элемента.
Недостатки
Сложность изменения родителя для существующего элемента. Для всех дочерних элементов необходимо пересчитать новый путь.
Операции со свойством путь обычно происходят долго.
ПримерРисунок 4.
Элемент
Путь
A
B
A
C
A B
D
A B C
E
A B
Рассмотрим элемент «B»:
Чтобы получить все его дочерние элементы, нам необходимо выбрать элементы, удовлетворяющие условию:
путь содержит «B»
Чтобы получить его непосредственные дочерние элементы, нужно указать позицию, в которой содержится элемент. В примере путь отсортирован так, что последняя часть пути — это непосредственный родительский элемент:
последняя часть пути равна «B»
Заключение
Мы кратко рассмотрели основные паттерны хранения деревьев в реляционной базе данных. Их основные достоинства и недоставки, а также на примерах рассмотрели основные запросы к ним. В этой статье не были рассмотрены алгоритмы построения и заполнения метаданных деревьев, то есть операции добавления, обновления и удаления элементов.
Левитация — а не отделить ли нам сайт от движка? / Хабр | Веб-студия Nat.od.ua
Левитация — а не отделить ли нам сайт от движка? / Хабр
200 лет назад начались разборки с авто двигателем. Понадобилось 80 лет для создания двигателя внутреннего сгорания. Результатом разборок стало появление сразу двух индустрий — автомобилестроение и моторостроение.
20 лет назад появилось сайтостроение в виде фреймворков. Оно также началось с разборок с движком. Не с сайтом, с ним все было ясно, а именно с движком. И вот, похоже, разобрались. И с движком, и с сайтом.
Ныне фреймворки подобрались к естественному рубежу: отделение сайта от движка. Есть и достижение — первый российский (и мировой?) фреймворк Levitation с новой архитектурой.
Основная проблема нынешних фреймворков
Современные фреймворки основное внимание уделяют движку. Сайт для них является менее значимым потому, что с ним все более-менее понятно: html, css, js + шаблонизатор. А вот с движком не все было понятно, и основные усилия были направлены именно на движок. Причем на движок, который, естественно, управляет только одним сайтом.
В результате все популярные ныне фреймворки являются моно-сайтами, то есть их движок управляет одним сайтом. В их корневом дире всегда сидит `index.php` — это стартер их единственного сайта. Более того, многие компоненты сайта — конфиги, шаблоны, коды оказываются там же, где и компоненты движка. Результат такого сверх-внимания к движку — полное растворение сайта в движке!
Чтобы сделать бэкап, надо бэкапить все — не только сайт, но еще и движок! Чтобы сделать dev сайт — надо копировать все! А ведь все — это для сайта в 0.5-1 Mb еще и 50-100 Mb движка. Движок в сто раз больше полезной нагрузки!
Проблема с бэкапом сайта решалась легко — спец класс или утилита для бэкапа именно сайта.
А вот проблема `мультисайта`, которая во весь рост встала лет 10 назад, оказалась вообще говоря нерешаемой. Нерешаемой из-за того, что единственный сайт был напрочь растворен в движке, и чтобы вытащить его, надо было переписывать все! Однако переписать все — это уже не новая версия, а новый фреймворк. Компромисс нашелся в варианте ограниченного мультисайта, когда все мультисайты могли находиться только внутри некоторого спец-дира в движке. При этом основной сайт в корне фреймворка естественно остался — без него никак.
Ну и вишенка — у топовых фреймворков, с которыми я знаком (WP, Laravel, Symphony, Drupal, Yii2) вообще нет объекта (класса) Site! Как вам такое?
Фреймворк Levitation – сайт отделен от движка
Лет 5 назад появился фреймворк Grav. Он не входит в топ 20, и может даже в топ 50, но довольно продвинутый. В нем класса Site тоже нет, но зато есть шаг в верном направлении — конфиги движка и сайта разделены. Остался последний шаг — разделить коды движка и сайта.
Небольшая команда энтузиастов решилась на радикальную переделку Grav – переписать его с целью полностью отделить сайт от движка.
Это не было легкой прогулкой, но результат оказался впечатляющим:
-
сайт отдельно, движок отдельно,
-
любой сайт может обращаться к любому движку,
-
регулировать нагрузку на движок — без проблем,
-
dev сайты и движки — да сколько хочешь,
-
бэкап сайтов и движков — просто копируй/архивируй,
-
разграничить доступ разработчиков и техподдержки — один клик,
-
и вообще — один движок, много сайтов — это и есть мультисайт!
Назвали мы свой фреймворк оригинально: Lev или Levitation (Лев или Левитация). Это просто дань уважения к его предку Grav (Gravitation).
Достоинства LevСупер-скорость
По сравнению с другими фреймворками Lev отвечает на запрос не быстро, а мгновенно. И это связано не с тем, что архитектура и все сторонние компоненты Lev самые современные, нет. И даже не с тем, что система кэширования страниц — самая крутая (хотя это так и есть).
Супер-скорость связана с тем, что Lev не использует СУБД. Почему не использует? – Да потому, что нафиг просто не нужна! Об этом – ниже.
СУБД? – не нужна!
А зачем фреймворку СУБД? У фреймворка собственно только две основные функции. Первая – анализировать запрос браузера, вторая — формировать и возвращать ответ (html страницу). Спрашивается, а где здесь СУБД? А ее здесь нету. Есть файловая система и алгоритмы формирования страниц по запросу. Все.
Почему же тогда все топовые фреймворки не просто содержат СУБД, а еще и считают ее важнейшим своим компонентом? Да потому, что СУБД нужна для обработки больших данных. А большие данные могут появляться только в больших приложениях, типа магазинов, корпоративных сайтов, вики,… Ключевым здесь является слово `приложение` — СУБД нужна приложениям с большими данными.
А вот фреймворку для исполнения его базовых функций СУБД не просто не нужна – она противопоказана. Фреймворку нужна только файловая система сервера, которая однозначно быстрее любой СУБД.
Ультра-современные архитектура и компоненты
Lev – наследник Grav, а Grav — довольно свежий фреймворк, ему около 5 лет. Grav воспринял все достижения, наработанные своими предшественниками. Общая компоновка и все основные компоненты фреймфорков к моменту появления Grav были многократно отработаны. Grav собрал все самое лучшее.
А Lev просто добавил к этому последний архитектурный штрих — полностью отделил сайт от движка. Правда, для этого пришлось переписать ядро Grav.
Потоки вместо путей
Для управления файлами и директориями Lev использует систему управления потоками (streams), а не путями, как у большинства фреймворков.
Потоки гораздо удобнее, чем пути, потому, что позволяют определять не только собственно пути, но и дополнительные параметры типа `чтение-запись`, `только чтение`.
Но важнейшим достоинством потока является возможность определить его путь относительно другого потока. И даже относительно нескольких потоков. Собственно именно в последнем и кроется вся мощь управления потоками.
И вишенка — все основные операционки поддерживают не только пути, но и потоки.
Дружественный конфиг – yaml файлы
Дружественный конфиг – это правильная штука. Вот в каком плане. Любое приложение пользует две группы настроек – внутренние и внешние. Внутренние настройки определяют режимы функционирования компонентов приложения и сидят либо в конфигах, либо вообще где-то в коде.
А вот внешние настройки определяют режимы взаимодействия с внешним миром. И их лучше выносить из кода. Еще лучше отделять их от внутренних настроек. А еще лучше их декларировать не на языке программирования, а как-то более дружественно. Отличный выбор — `yaml` формат. Он легко понятен даже юзеру. Правда, прогеры не всегда с ним дружат за его фривольность, но это их проблемы. Тем более, что внешние настройки – вообще не их забота.
Компактность – ничего лишнего
Времена, когда во фреймворк пихали все, что типа может понадобиться – эти времена прошли. Индустрия развилась, специализация рулит. Фреймворк должен содержать только нужные ему самому компоненты – и ничего больше. Всякие дополнительные фичи (типа СУБД), нужные конкретному приложению, оно может подключить само. Точка.
Так вот, Lev не просто компактен – он сверх-компактен. Дистрибутив весит ~20 Mb. Из которых более половины — вендоры. Это без админки. А с админкой — еще ~20 Mb.
Разделение сайта и движка
Главной фишкой Lev является полное разделение сайта и движка. Сайт Lev сидит в своей собственной директории. Сидит весь, со всеми своими потрохами. То же относится и к движку Lev.
Понятно, что сайт и движок – это разные сущности. Сайт определяет свои роуты, конфиги, страницы и бизнес-логику приложения. Тогда как движок определяет логику взаимодействия с сайтом и логику обработки запроса браузера.
Взаимодействие сайт-движок такое:
-
Сайт получает запрос браузера и передает его на обработку движку.
-
Движок анализирует запрос браузера и формирует html страницу ответа, обращаясь к настройкам, шаблонам страниц и бизнес-логике приложения вызвавшего его сайта.
-
Движок возвращает сформированную страницу браузеру.
Мультисайт – полный и неограниченный
Благодаря разделению сайта и движка Lev поддерживает неограниченный мультисайт: один движок — много сайтов.
Такая архитектура имеет множество выгод.
Самой очевидной выгодой является бэкап – просто делаешь копию или архив директории сайта, и никаких проблем.
Другая выгода касается разработки и саппорта. Разделение движка и сайта автоматом приводит к разделению специализаций. Разработчики и саппортеры естественным образом делится на две разных группы по интересам – разработка и саппорт собственно сайта, и разработка и саппорт движка. И, что совсем хорошо, рабочие площадки группы сайта и группы движка не пересекаются — у каждой своя директория!
Ничто не мешает иметь не один движок, а сколько угодно, исходя из нагруженности, безопасности, бизнес-значимости, разработки/сопровождения и т.д. При этом все движки будут абсолютно одинаковы (в рамках одной версии).
Ничто не мешает выделить свой собственный движок для особо значимого сайта, или иметь тучу низко-нагруженных сайтов на один движок.
Ничто не мешает разместить движок на другом сервере для особо затратных или специфических запросов, скажем поиски, бэкапы, отчеты, … и тем самым освободить основной движок от таких запросов.
В общем, никаких ограничений для любой архитектуры пар сайт-движок!
Это как в автомобилестроении 100 лет назад, когда двигатель отделился от авто, а индустрия моторостроения — от автостроения.
Lev отделил сайт от движка — и это правильно!
Проект Levitation
Приветствую каждого, кто добрался сюда!
Проект Levitation еще совсем молод, но он уже передовой и он чисто российский!
У проекта амбициозные планы по дальнейшему совершенствованию. Главным направлением видится глубокая декомпозиция движка для уменьшения связности компонент и улучшения скоростных характеристик. Это потребует значительных усилий и ресурсов.
Если Вы желаете поддержать Levitation любым образом, включая спонсорство или участие в разработке, вот контакты:
Сергей Гусев, [email protected]
https://github.com/getlev/lev
UI редактора блок-схем / Хабр | Веб-студия Nat.od.ua
UI редактора блок-схем / Хабр
Dgrm.net
Придумывать интерфейс интересно. Похоже на головоломку. Вот что получается для Dgrm.net.
Dgrm.net это редактор блок-схем
-
работает на пк, планшетах и телефонах;
-
нет лишних кнопок;
-
быстрый;
-
бесплатный.
Редактор открывает схемы из картинокРис 1. Dgrm.net открывает картинки схем
Не нужно хранить исходники, картинка и есть исходник.
Например: посылаем схему по почте. Через месяц письмо возвращается — сразу правим картинку из письма, не ищем исходники.
Соединительные линии вытягиваем, а не добавляем как отдельную фигуруРис 2. Для соединения фигур вытягиваем стрелкиТочки входа подсвечиваются заранее при приближении к фигуре
На рисунке 3 показаны скрытые контуры.
Рис 3. Точки входа подсвечиваются заранее при приближении к фигуреФигуры автоматически подстраиваются под длину текста
Не нужно вручную менять размер.
Фигуры увеличиваются не плавно, а шагами. Чтобы на схеме не рябило от разных размеров.
Рис 4. Фигуры автоматически меняют размер в зависимости от текстаМинимум элементов управления
Возможности редактора сознательно ограничены.
Большие элементы управления
На телефонах элементы управления становятся еще больше.
Рис 5. Большие элементы управления на телефонахБлижайшие планы
-
Групповое перетаскивание фигур. Сейчас можно только одну фигуру перетащить.
-
Изменение цвета фигур.
-
Приближать/удалить схему.
-
Короткие ссылки на диаграммы.
-
Диалог “Сохранить как”. Сейчас сохраняет в “Загрузки” без возможности указать имя файла.
-
Копирование схемы из редактора в Word.
-
Перетаскивание схемы из редактора в Word.
Dgrm.net | GitHub (исходный код)
Оптимизация загрузки js бандла использующего icon pack’и / Хабр | Веб-студия Nat.od.ua
Оптимизация загрузки js бандла использующего icon pack’и / Хабр
Иконки в проекте часто становятся причиной проблем разбухания размера бандла. Все из-за того что svg-иконки могут быть достаточно объемными.
Если мы загружаем только те иконки, что мы реально используем то это не самый плохой вариант. Все становится намного хуже когда мы устанавливаем готовые пакеты иконок, но используем только их малую часть, так произошло и со мной.
На скриншоте видно, что это приложение, состоящее из 2х элементов, весит 722kb. Но почему?
Исходные данные
Давайте посмотрим на код, в котором и находится проблема.
Вроде все просто. У иконки по пропсу name мы понимаем, какую именно иконку из пака нам нужно достать. Но бандл слишком много весит, тут же только одна иконка, почему так? Давайте посмотрим, что происходит внутри компонента иконки.
./components/Icon
Тут видно, что виной раздувания бандла является fontawesome и его наборы пакетов: free-regular-svg-icons, free-brands-svg-icons, free-solid-svg-icons, и т.д.
Мы импортируем сразу все иконки из всех наших пакетов, что и является основной проблемой.
Теперь взглянем на результаты анализа бандла.
602 kb сжатых gzip данных мы заставляем грузить наших пользователей только для того, чтобы показать им одну иконку! С этим точно нужно что-то сделать.
Сразу оговорюсь, вы можете сказать, что можно было бы загружать только нужные нам иконки через прямые импорты, а потом прогнать из через svg-sprite. Но тогда это привело бы к значительной переработке имеющихся проектов, что было бы крайне нежелательно.
Мне хотелось бы сохранить интерфейс взаимодействия с компонентом Icon и не заставлять разработчиков каждый раз добавлять новые иконки в проект и писать какой либо дополнительный код. То есть все должно остаться как есть, и при этом наша проблема должна быть решена.
Решение
Для сохранения обратной совместимости внешний интерфейс работы с компонентом Icon не должен меняться. И должна остаться возможность использовать любые иконки из установленных пакетов, а также кастомные иконки. Для достижения этих целей будем использовать динамические импорты. Для каждой иконки из наших пакетов нужно создать отдельную строку в специальных map-файлах.
Благодаря этим файлам наш бандлер поймет что нужно будет создать отдельный чанк под каждую иконку. Тогда мы сможем скачивать их по требованию.
В рабочем проекте были куплены расширенные пакеты fontawesome, кол-во иконок в них было примерно 7-8т шт. Писать мапы для такой кучи иконок однозначно задача не одного дня.
Поэтому был написан скрипт который сделает всю рутинную работы за нас.
Результат работы скрипта iconPackMapsGenerator
Внутри конфиг для его работы.
-
name: название генерируемого файл;
-
lib: путь до папки с файлами иконок;
-
prefix: префикс для названия иконки. Заменяет префикс fa.
Для кастомных иконок создаем отдельную папку, где каждый файл описан следующим образом:
И переделываем наш компонент Icon не трогая его интерфейс.
components/Icon.tsx
Вроде все. Что же у нас получилось в итоге?
Давайте вновь посмотрим на бандл.
Как видно, каждая наша иконка помещена в отдельный чанк, и также можно заметить, что размер бандла значительно увеличился. Было 600kb стало 2.01 Mb. Почему так?
Бандлер для создания чанка оборачивает его в специальный код который, как оказалось, весит больше чем многие наши svg.
При ближайшем рассмотрении чанка с компонентом Icon видно что, больше всего места занимают мапы иконок. Но нам нужны эти файлы так как иначе бандлер не поймет что нам нужно создать отдельный чанк под всевозможные иконки. И имея мапу можем создавать ts-типы для названий всех иконок.
А что же происходит теперь в браузере?
Теперь при загрузке страницы мы видим, что последний чанк размером 1.1kb это наша иконка.
При первой загрузки в начальном варианте мы загружали 722kb, а сейчас 89kb! Уже победа. Помимо этого мы получили:
-
Сохранение обратной совместимости с исходным проектом;
-
Динамическая загрузка иконок по требованию;
-
Возможность добавлять кастомные иконки
Заключение
Надеюсь эта статья вам поможет с оптимизацией ваших бандлов и сэкономит вам время и силы. Исходных код используемого тестового проекта вы можете найти по ссылке.
Также если вам интересно в своем Telegram я время от времени выкладываю интересные находки по фронтенду. И всем легких бандлов.
Фронтенд-новости №11. JQuery живее всех живых, замена CAPTCHA, вариативные шрифты в Figma | Веб-студия Nat.od.ua
Фронтенд-новости №11. JQuery живее всех живых, замена CAPTCHA, вариативные шрифты в Figma
Дайджест новостей и полезных статей из мира фронтенд-разработки за неделю 13–19 июня.
Доступность
Аспекты доступности — семантика, контратность и… тревога?
Спецификации
Замените CAPTCHA на Private Access Tokens. Cloudflare уже начинает тестирование
HTML
Несколько способов добавить пробел в размерку
Разбираемся с нативными веб-компонентами
CSS
10 трюков, которые должен знать каждый веб-разработчик
Руководство по высококонтрастному режиму в Windows
JavaScript
Сравнение простых примеров кода между React, Vue, Angular, Vue, Ember, Lit
AbortController для отмены синхронных задач
Исследователи: мы изучили 1 000 000 сайтов и видим, что jQuery самый популярный, а вы говорите, что не используете его.
React
Масштабируемая архитектура для создания React-приложений
Энциклопедия компонентов из более 5000 UI-элементов
Как я создал приложение для фокусировки с помощью React и Rust
Подробно про элементы, компоненты и экземпляры в React
История о том, как уменьшить количество ошибок в кодовой базе
Как поддерживать внутреннюю библиотеку компонентов
Node.js
Локальная разработка NestJS и Postgress с помощью Docker Compose.
Node.js 16 прекратит развитие в сентябре 2023. Пора переходить на node.js 18
Angular и Vue
Жду вас в комментариях с ссылкой на ресурсы по Angular и Vue. Спасибо
Инструменты
В Figma появились вариативные шрифты
Генератор CSS для теней с градиентами
Трассировка зависимостей node.js
Браузеры
История веб-браузеров за 28 лет
Firefox внедряет Total Cookie Protection по умолчанию
Общее
Шрифт Atkinson Hyperlegible разработанный специально для повышения разборчивости для людей с плохим зрением.
Исследование о том как шрифты могут вызывать эмоциональную реакцию.
Эдди Османи делиться софт скилами для разработки ПО.
P.S. Всю неделю писали и шутили о том, что IE всё, а я, пожалуй, продолжу выпуск дайджеста.
Как читать статьи на английском языке
В дайджесте много статей и видео на английском языке, чтобы это не стало препятствием: в Google Chrome есть функция перевода страницы с любого популярного языка, а видео можно перевести в Яндекс Браузере.
Запускаем DOS игру в браузере / Хабр | Веб-студия Nat.od.ua
Запускаем DOS игру в браузере / Хабр
В 2022 году мало кого можно удивить DOS игрой в браузере. Благодаря dosbox они доступны на многих платформах. А поддержка браузера появилась с развитием компилятора emscripten. js-dos один из самых заметных проектов портирования dosbox в браузер. Я начал работу над ним в 2014 году. Проект прошел долгую эволюцию, было выпущено 3 разных версии с разной архитектурой и API (v2/v3 — 2015/2017, 6.22 — 2018/2020, v7 — 2021+).
Благодаря мощнейшему развитию технологий производительность эмулятора неуклонно росла, самые первые версии были на чистом JavaScript, затем был asm.js и наконец WebAssembly. Последняя версия js-dos уже имеет достаточную производительность для комфортной игры в такие игры как Duke, Doom, C&C, Red Alert, WarCraft и многие другие.
Преимущества
На мой взгляд js-dos лучшее API для запуска DOS игр в браузере. В отличии от других реализаций:
-
сохраняет прогресс между сессиями (запусками браузера);
-
работает в worker, а значит не блокирует UI браузера;
-
работает в node.js;
-
есть headless режим, т.е. без вывода на экран (идеальное решение для VR);
-
поддерживает сетевой протокол IPX;
-
нацелен на мобильные устройства;
-
имеет огромную библиотек готовых игр.
Многие по достоинству оценили это решение, список интеграций перевалил уже за 50 сайтов.
Однако, в российском сегменте, js-dos по прежнему не переменятся на известных порталах.
Надеюсь, хороший обзор поможет решить эту проблему. Эта статья призвана объяснить как запустить DOS игру используя js-dos. А способов на самом деле много.
Подготовка игрового архива
js-dos оперирует понятием ‘пакет‘ (bundle). Пакет это zip архив, который содержит все необходимое для запуска игры. Сообщество js-dos развивает репозиторий DOS игр и предоставляет в открытый доступ более 1900+ пакетов DOS игр. Многие из них имеют поддержку мобильных устройств.
Проще всего подготовить пакет с игрой для js-dos — использовать «Творческую студию». Это редактор который не только сформиует пакет, но и в наглядном режиме позволяет проектировать мобильные элементы управления.
Запуск в браузере
Рассмотрим способы запуска DOS игры начиная с самого простого:
iframe интеграция
js-dos это не только API, но и экосистема, она включает плеер для ингерации через iframe. Что бы добавить игру на сайт через iframe, достаточно использовать этот код:











