Как определить с какого устройства открыт сайт
Самым простым способом является анализ строки User-Agent’a — это специальный заголовок, который передаётся браузером устройства вместе с любым запросом.
Сразу рассмотрим на примере:
Из этой строки можно достать информацию об устройстве пользователя, его операционной системе и браузере.
Чтобы проанализировать строку, можно воспользоваться либо готовыми библиотеками, либо написать примерно такой код:
Выше достаточно упрощённая версия кода, просто чтобы передать основную идею.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #javascript
Самым простым способом является анализ строки User-Agent’a — это специальный заголовок, который передаётся браузером устройства вместе с любым запросом.
Сразу рассмотрим на примере:
Запрос с айфона из Safari:
Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1
Запрос с комьютера из Google Chrome:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
Из этой строки можно достать информацию об устройстве пользователя, его операционной системе и браузере.
Чтобы проанализировать строку, можно воспользоваться либо готовыми библиотеками, либо написать примерно такой код:
const isMobile = () => /Mobi|Android/i.test(navigator.userAgent)
if (isMobile()) {
console.log("Мобильное устройство");
} else {
console.log("Десктопное устройство");
}
Выше достаточно упрощённая версия кода, просто чтобы передать основную идею.
Пригодиться это может в нескольких кейсах:
— для изменения вёрстки под особенности устройства
— для сбора аналитики
— подключение и использование специфичных API (геолокация, сенсорные жесты, вибрация и пр.)
— ну и точно что-то ещё, о чём я мог забыть
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #javascript
👍25🔥6❤5🐳2
Что такое Callback Hell и как с ним бороться?
Частый вопрос с собеса, особенно если идти куда-то повыше стажёра.
Callback Hell — это ситуация в асинхронном программировании, когда вложенные друг в друга функции обратного вызова (он же callback) образуют "лесенку", что делает код трудным для чтения и сопровождения.
Как с этим можно бороться?
Конечно же промисы и async/await синтаксис:
Промисы позволяют писать асинхронный код более линейно и читаемо благодаря цепочке вызовов (chaining):
А async/await — это более современный синтаксис над промисами, который так же позволяет развернуть вложенные колбеки в плоский код:
И о Promise, и о async/await у меня уже есть более подробные посты
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #javascript #patterns #data
Частый вопрос с собеса, особенно если идти куда-то повыше стажёра.
Callback Hell — это ситуация в асинхронном программировании, когда вложенные друг в друга функции обратного вызова (он же callback) образуют "лесенку", что делает код трудным для чтения и сопровождения.
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doAnotherThing(newResult, function(finalResult) {
doSomethingMore(finalResult, function(lastResult) {
console.log(lastResult);
});
});
});
});
Как с этим можно бороться?
Конечно же промисы и async/await синтаксис:
Промисы позволяют писать асинхронный код более линейно и читаемо благодаря цепочке вызовов (chaining):
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doAnotherThing(newResult))
.then(finalResult => console.log(finalResult))
.catch(error => console.error(error));
А async/await — это более современный синтаксис над промисами, который так же позволяет развернуть вложенные колбеки в плоский код:
async function process() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doAnotherThing(newResult);
console.log(finalResult);
} catch (error) {
console.error(error);
}
}
process();
И о Promise, и о async/await у меня уже есть более подробные посты
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #javascript #patterns #data
🔥18❤9👍9🐳3
CSS для печати страниц
Несколько лет назад я делал пет-проект — конструктор резюме. Кейс был такой, что хотелось сразу же печатать созданное резюме при нажатии сочетания клавиш
То есть во время печати мы можем скрыть все ненужные элементы:
Ну или ещё проще:
А сам класс
Также есть ещё специальные CSS свойства печати, например,
В реальной практике также особенно полезно использовать подобные поднастройки стилей для печати кастомных чеков, накладных, этикеток для маркетплейсов и прочих документов, что иногда реально верстаются где-то кроме ворда.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #web #useful
Несколько лет назад я делал пет-проект — конструктор резюме. Кейс был такой, что хотелось сразу же печатать созданное резюме при нажатии сочетания клавиш
Ctrl+P
в браузере. Но проблема в том, что помимо самого резюме на печать попадало не только само резюме, а все кнопки, поля и прочие элементы страницы для его настройки. Такие кейсы можно решать при помощи CSS медиазапроса @media print
:@media print {
/* стили для печати */
}
То есть во время печати мы можем скрыть все ненужные элементы:
@media print {
.navbar, .footer, .controls {
display: none;
}
}
Ну или ещё проще:
@media print {
.no-print {
display: none;
}
}
А сам класс
.no-print
проставлять на все скрываемые при печати элементы.Также есть ещё специальные CSS свойства печати, например,
break-inside
, которое дополнительно настраивает можно ли переместить блок со страницы на страницу при печати, что особенно актуально для целостных блоков типа “контакты” в резюме. Если указать break-inside: avoid
, то такой элемент не сможет быть расположен на двух страницах сразу, а перенесётся весь, если для него не хватает места.В реальной практике также особенно полезно использовать подобные поднастройки стилей для печати кастомных чеков, накладных, этикеток для маркетплейсов и прочих документов, что иногда реально верстаются где-то кроме ворда.
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #code #web #useful
👍34🔥8❤4🐳1
Как отменить уже отправленный HTTP запрос?
Для отмены уже отправленного запроса нам пригодится встроенный в JavsScript объект —
Этот объект позволяет отменять уже запущенные асинхронные операции,
Нужно это много где, я приведу самый очевидный пример с реактом:
Представим, что пользователь открывает страницу. На странице в
Использование🌚
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #javascript #code #data
Для отмены уже отправленного запроса нам пригодится встроенный в JavsScript объект —
AbortController
Этот объект позволяет отменять уже запущенные асинхронные операции,
fetch
в том числеconst controller = new AbortController()
// отправляем запрос
fetch('https://.../', { signal: controller.signal })
// отменяем его
controller.abort()
Нужно это много где, я приведу самый очевидный пример с реактом:
Представим, что пользователь открывает страницу. На странице в
useEffect
идёт запрос к API, но пользователь, не дожидаясь ответа от сервера, переходит на другую страницу. Запрос есть, трафик занят, есть риск нарушения жизненного цикла компонента, а результаты этого запроса уже и вовсе не нужны. Вот так это решается:useEffect(() => {
const controller = new AbortController()
// делаем запрос на маунт компонента
fetch('https://.../', { signal: controller.signal })
// отменяем запрос на анмаунт компонента
return () => controller.abort()
}, [])
Использование
AbortController'a
помогает избежать потенциальных утечек памяти и гарантирует, что запросы не будут выполняться после того, как компонент был размонтирован. Полезно это при любых запросах, так что можно смело сделать свой хук обёртку. Или просто использовать @tanstack/react-query
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #javascript #code #data
Please open Telegram to view this post
VIEW IN TELEGRAM
👍32❤11🔥3🐳2🤯1🤩1
Разница между CommonJS и ES Modules
Если по простому, то CommonJS и ES (ECMAScript) Modules — это два способа импорта и экспорта чего либо из файлов в JavaScript
CommonJS — относительно старый API, который был разработан для NodeJS. Выглядит он следующим образом:
ES Modules — это подход, который в язык принес ECMAScript 2015 (ES6). Он задумывался как замена устаревшему CommonJS и выглядит так:
ИМХО, в современной разработке от CommonJS можно отказаться как от рудимента. Я стараюсь использовать модули по максимуму
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #javascript #code
Если по простому, то CommonJS и ES (ECMAScript) Modules — это два способа импорта и экспорта чего либо из файлов в JavaScript
CommonJS — относительно старый API, который был разработан для NodeJS. Выглядит он следующим образом:
// Экспорт
module.exports = function() {
console.log(...);
};
// Импорт
const module = require('./module');
module();
Особенностью CommonJS является синхронная загрузка модулей. Это означает, что когда один модуль требует другой модуль, выполнение кода приостанавливается до тех пор, пока требуемый модуль не будет загружен и выполнен
Для браузера, синхронная загрузка — крайне неудачный подход из-за блокировки интерфейса в момент загрузки. Поэтому в браузере использовать CommonJS — моветон, лучше использовать ES Modules
ES Modules — это подход, который в язык принес ECMAScript 2015 (ES6). Он задумывался как замена устаревшему CommonJS и выглядит так:
// Экспорт
export function foo() {
console.log(...)
}
// Импорт
import { foo } from './module.js';
foo();
ES Modules поддерживают асинхронную загрузку модулей и лучше умеют в статический анализ, что лучше как со стороны пользователя, так и разработчика.
Также ES Modules могут использоваться не только в браузере, а и в NodeJS, начиная аж с 12 версии. Как же давно это было🥲
ИМХО, в современной разработке от CommonJS можно отказаться как от рудимента. Я стараюсь использовать модули по максимуму
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #javascript #code
👍30❤11🐳6🔥5
Как убрать стили на под-дереве — мини-задача с реального проекта
Недавно был следующий кейс: в долгоживущий проект интегрировали
То есть мы приходим к ситуации, когда у нас есть некоторые глобальные стили, которые нужны везде по проекту, кроме каких-то под-деревьев исключений. Самый дешевый вариант решить эту проблему — обнулить стили на под-дереве
Обнулить все стили на под-дереве можно с помощью CSS свойства
Свойство
Далее полученный класс
Конечно же, можно всё и с нуля переписать, или даже указать все необходимые стили для каждого тега вручную, но какой-то необходимости в этом нет. Всё можно решить буквально в одну строчку
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #web #code
Недавно был следующий кейс: в долгоживущий проект интегрировали
tailwind
со своими обнуляющими стилями. Эти стили очень нужны проекту, но на одной из страничек есть Rich-текст, приходящий с апишки, который, к сожалению, частично завязан на браузерные стили, и, после добавления обнуляющих стилей tailwind
, весь поплылОбнуляющие стили — это когда все заголовки, списки и прочие HTML теги сбрасывают свои стандартные браузерные стили, чтобы тег нёс за собой только семантику
То есть мы приходим к ситуации, когда у нас есть некоторые глобальные стили, которые нужны везде по проекту, кроме каких-то под-деревьев исключений. Самый дешевый вариант решить эту проблему — обнулить стили на под-дереве
Обнулить все стили на под-дереве можно с помощью CSS свойства
all
. Выглядеть это может так:.tailwind-reset-styles * {
all: revert;
}
Свойство
all
может сбросить все CSS свойства до их начального или унаследованного значения, что конкретно в моём случае полностью решало все проблемыДалее полученный класс
.tailwind-reset-styles
вешаем на родительский тег, все под-дерево которого уже будет со сброшенными ненаследуемыми стилями. Это, наверное, самое элегантное и быстрое решение, что можно придумать в этой ситуацииКонечно же, можно всё и с нуля переписать, или даже указать все необходимые стили для каждого тега вручную, но какой-то необходимости в этом нет. Всё можно решить буквально в одну строчку
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #web #code
👍29❤6🔥5🐳5
Что такое SWC и немного о Rust
SWC — это аббревиатура, которая всё чаще появляется в мире фронтенда. Расшифровывается это как Speedy Web Compiler и полностью оправдывает своё название, ведь SWC — это ультрабыстрый компилятор и пакетный обработчик, написанный на Rust. Он предназначен для преобразования современного JavaScript и TypeScript кода в код, который будет поддерживаться большинством браузеров. Возможно это во многом из-за полифилов, о которых я писал в этом посте
Уже сейчас SWC используется в продуктах Vercel (в Next.js и Turbopack в том числе), Tencent, Shopify и других компаний, а также ложится в основу Vite начиная ещё с 4 версии (сейчас 5), так что с нами он плюс минус на долго
Да и больше про SWC сказать обзорно особо нечего. Мне во всём этом очень занимательно наблюдать за тем, какой огромный фокус на скорость появляется в фронтенде. Rust постепенно захватывает тулинг фронтенда: сборщики, компиляторы, линтеры и так далее — уже почти в каждой части тулинга есть решение на Rust и это, очевидно, не просто так
Ну и также не стоит забывать, что Rust уже давно имеет полную поддержку WebAssembly и является одним из основных языков этой технологии. Лично мне очень интересно понаблюдать, куда это всё приведёт. Накидывайте китов , сделаю отдельный пост по WebAssembly
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #web #code
SWC — это аббревиатура, которая всё чаще появляется в мире фронтенда. Расшифровывается это как Speedy Web Compiler и полностью оправдывает своё название, ведь SWC — это ультрабыстрый компилятор и пакетный обработчик, написанный на Rust. Он предназначен для преобразования современного JavaScript и TypeScript кода в код, который будет поддерживаться большинством браузеров. Возможно это во многом из-за полифилов, о которых я писал в этом посте
Если ещё проще, то SWC выполняет все функции Babel, только делает это в более чем 20 раз быстрее на одном ядре, и более чем в 70 раз быстрее на 4 ядрах.
Снова Rust всех победил🤷♂️ 🤷♂️ 🤷♂️
Уже сейчас SWC используется в продуктах Vercel (в Next.js и Turbopack в том числе), Tencent, Shopify и других компаний, а также ложится в основу Vite начиная ещё с 4 версии (сейчас 5), так что с нами он плюс минус на долго
Да и больше про SWC сказать обзорно особо нечего. Мне во всём этом очень занимательно наблюдать за тем, какой огромный фокус на скорость появляется в фронтенде. Rust постепенно захватывает тулинг фронтенда: сборщики, компиляторы, линтеры и так далее — уже почти в каждой части тулинга есть решение на Rust и это, очевидно, не просто так
Ну и также не стоит забывать, что Rust уже давно имеет полную поддержку WebAssembly и является одним из основных языков этой технологии. Лично мне очень интересно понаблюдать, куда это всё приведёт. Накидывайте китов , сделаю отдельный пост по WebAssembly
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #web #code
Please open Telegram to view this post
VIEW IN TELEGRAM
🐳39❤7👍3🔥2
Что такое виртуализация?
Это крайне популярная техника оптимизации, которая позволяет эффективно управлять отображением большого количества элементов на странице. Особенно она полезна при отображении большого массива данных в списках, таблицах и прочих подобных элементах
Если объяснять на пальцах как это работает, то я бы сказал так:
Мы можем представить, что хотим послушать музыку. Открываем что-то типа Тындыкс.Музыки и видим 10.000 разных треков — это 10.000 строк огромного списка
Так вот, зачем отображать все 10.000 сразу, если на экране в лучшем случае поместиться треков 20-30? Давайте рисовать только первые, например, 50 треков, а дальше следить за тем что видит пользователь — за его скроллом. Если пользователь прокрутит ещё на 20 строк вниз, то мы нарисуем следующие 20 строк, а первые 20 удалим из вёрстки
Под коробкой все виртуализаторы как раз этим и занимаются:
1. Определяют какие элементы видны пользователю сейчас
2. Определяют сколько элементов скрыто над и под окном просмотра
Чаще всего это реализуют через события
Виртуализация может пригодиться в огромном срезе приложений, поэтому очень советую с ней ознакомиться заранее, если вы ещё не успели
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #javascript #code #react
Это крайне популярная техника оптимизации, которая позволяет эффективно управлять отображением большого количества элементов на странице. Особенно она полезна при отображении большого массива данных в списках, таблицах и прочих подобных элементах
Я очень рекомендую TanStack Virtual для этой задачи. Мне уже удалось применить его на нескольких проектах и никаких проблем он не вызывал
Если объяснять на пальцах как это работает, то я бы сказал так:
Мы можем представить, что хотим послушать музыку. Открываем что-то типа Тындыкс.Музыки и видим 10.000 разных треков — это 10.000 строк огромного списка
Так вот, зачем отображать все 10.000 сразу, если на экране в лучшем случае поместиться треков 20-30? Давайте рисовать только первые, например, 50 треков, а дальше следить за тем что видит пользователь — за его скроллом. Если пользователь прокрутит ещё на 20 строк вниз, то мы нарисуем следующие 20 строк, а первые 20 удалим из вёрстки
Под коробкой все виртуализаторы как раз этим и занимаются:
1. Определяют какие элементы видны пользователю сейчас
2. Определяют сколько элементов скрыто над и под окном просмотра
Чаще всего это реализуют через события
scroll
на контейнере или через IntersectionObserver
, подробности реализации для особо любопытных всегда доступны в сорсах библиотек (в TanStack Virtual можете начать с функции observeElementRect)Виртуализация может пригодиться в огромном срезе приложений, поэтому очень советую с ней ознакомиться заранее, если вы ещё не успели
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #theory #javascript #code #react
🐳26👍9❤5🔥4
Как менять состояние вкладки по интервалу
Полезная фича для демонстрации, например, уведомлений, которая может пригодиться на очень большом количестве сайтов
Для реализации нам необходимо создать состояния, между которыми мы хотим перемещаться:
Далее необходимо завести состояние, которое будет определять на каком этапе из списка состояний мы сейчас находимся. Это состояние можно будет менять через обычный интервал:
И в зависимости от текущего состояния изменяем параметры вкладки:
Очевидно, что в таком исполнении решение выглядит не очень красиво. Можно создать кастомный хук и вынести всю логику туда, например, или воспользоваться нативными контекстами. В общем, сделать из этого решения либу, которую можно будет легко перенести из проекта в проект
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #code #web #theory #javascript #react
Полезная фича для демонстрации, например, уведомлений, которая может пригодиться на очень большом количестве сайтов
Для реализации нам необходимо создать состояния, между которыми мы хотим перемещаться:
const states = [
{ title: "Мессенджер", icon: Favicon1 },
{ title: "Новое сообщение", icon: Favicon2 },
]
Далее необходимо завести состояние, которое будет определять на каком этапе из списка состояний мы сейчас находимся. Это состояние можно будет менять через обычный интервал:
const [stateIndex, setStateIndex] = useState(0);
// раз в секунду переходим на следующее состояние
useEffect(() => {
const intervalId = setInterval(() => {
setStateIndex((prevIndex) => (prevIndex + 1) % states.length);
}, 1000);
return () => clearInterval(intervalId);
}, []);
И в зависимости от текущего состояния изменяем параметры вкладки:
useEffect(() => {
const link = document.querySelector("link[rel~='icon']");
const title = document.querySelector("head title");
if (link) {
link.href = states[stateIndex].icon;
title.textContent = states[stateIndex].title;
}
}, [stateIndex]);
Очевидно, что в таком исполнении решение выглядит не очень красиво. Можно создать кастомный хук и вынести всю логику туда, например, или воспользоваться нативными контекстами. В общем, сделать из этого решения либу, которую можно будет легко перенести из проекта в проект
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #code #web #theory #javascript #react
🐳22😐8👍5❤4🔥2
Как определить, активна ли вкладка у пользователя?
Если вам нужно узнать, активна ли вкладка в браузере у пользователя, существует несколько простых методов для этого
Во-первых, у объекта
Это может быть полезно для определения состояния в моменте, хотя пригождается достаточно редко
Чаще всего используется второй способ, а именно отслеживание события
Тут мы отслеживанием фокус вкладки и выполняем какие-то действия на уже при изменении состояния. Особенно полезен такой способ будет где нибудь в React и прочих либах
Зачем это можно использовать? Да абсолютное множество применений:
— дополнительно сохранять данные на закрытие вкладки
— выключать аудио/видео при скрытии вкладки
— сбор аналитики
— и куча всего ещё
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #code #web #theory #javascript
Если вам нужно узнать, активна ли вкладка в браузере у пользователя, существует несколько простых методов для этого
Во-первых, у объекта
document
есть свойство hidden
, которое указывает открыта ли вкладка на экране пользователя в конкретный момент времени:// если true, значит вкладка работает в фоне
document.hidden // true
// вкладка открыта на весь экран
document.hidden // false
Это может быть полезно для определения состояния в моменте, хотя пригождается достаточно редко
Чаще всего используется второй способ, а именно отслеживание события
visibilitychange
:document.addEventListener("visibilitychange", function() {
if (document.hidden) {
console.log("Вкладка неактивна")
} else {
console.log("Вкладка активна")
}
})
Тут мы отслеживанием фокус вкладки и выполняем какие-то действия на уже при изменении состояния. Особенно полезен такой способ будет где нибудь в React и прочих либах
Зачем это можно использовать? Да абсолютное множество применений:
— дополнительно сохранять данные на закрытие вкладки
— выключать аудио/видео при скрытии вкладки
— сбор аналитики
— и куча всего ещё
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #code #web #theory #javascript
👍23🔥5🐳3❤2
Немного о __proto__ и prototype в JavaScript
В чём разница?
Если рассмотреть в коде, то получим следующее:
То есть, по факту, свойство
В примере выше, мы при помощи
Зачем всё это нужно? Для корректной работы прототипного наследования, конечно же. Рассмотрим ещё один пример кода:
Вообще, вся эта теория скорее всего не нужна рядовому разработчику. Это что-то из разряда теории ядра JavaScript, которая вроде есть, но не понятно зачем она нужна в таком приложении в реальной разработке
Если моё объяснение не очень понятно, то есть отличный видос у камасутры на эту тему, где всё разжёвано максимально понятно, советую
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #theory #code
__proto__
— это внутреннее свойство любого объекта или примитива в JavaScript, которое ссылается на объект, от которого он наследует свойства и методыprototype
— это свойство функций-конструкторов (или классов), которое используется для определения объектов, которые будут выступать в роли прототипов для всех экземпляров, созданных этим конструкторомВ чём разница?
__proto__
есть у каждого объекта и ссылается на прототип, из которого этот объект черпает свои свойства и методыprototype
есть только у функций-конструкторов (и классов) и используется для задания свойств и методов, которые будут доступны всем экземплярам, созданным с помощью этого конструктораЕсли рассмотреть в коде, то получим следующее:
const name = "Denis"
const surname = "Putnov"
name.__proto__ === String.prototype // true
name.prototype // undefined
name.__proto__ === surname.__proto__ // true
const age = 23
age.__proto__ === Number.prototype // true
age.prototype // undefined
То есть, по факту, свойство
__proto__
можно назвать некоторым костылём языка, благодаря которому мы можем понять с помощью чего конкретно был создан наш новый объект__proto__
всегда ссылается на какой-то прототип, на основе которого был создан новый объект В примере выше, мы при помощи
__proto__
можем увидеть, что name
в итоге создан при помощи прототипа String
Зачем всё это нужно? Для корректной работы прототипного наследования, конечно же. Рассмотрим ещё один пример кода:
const channel = {
name: "progway"
}
channel.toString()
// как же javascript'у понять, откуда взять метод?
// под капотом вызывается это вот так:
(channel.__proto__).toString()
// а мы знаем, что channel.__proto__ === Object.ptototype
// поэтому выглядеть вызов будет примерно вот так
Object.prototype.toString.call(channel)
Вообще, вся эта теория скорее всего не нужна рядовому разработчику. Это что-то из разряда теории ядра JavaScript, которая вроде есть, но не понятно зачем она нужна в таком приложении в реальной разработке
Зато вопросы о механизме прототипного наследования часто встречаются на собеседованиях, так что это может быть полезно как минимум там
Если моё объяснение не очень понятно, то есть отличный видос у камасутры на эту тему, где всё разжёвано максимально понятно, советую
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #theory #code
👍35🐳4🔥3❤1
Как я использую шину событий
Шина событий — это паттерн, который используется для взаимодействия различных компонентов системы не напрямую, а через некоторый посредник — саму шину
Главным преимуществом шины событий я бы назвал ослабленную связность между компонентами системы. Благодаря этому, компоненты могут взаимодействовать через события, не зная о существовании друг друга и не дожидаясь ответной реакции
Это позволяет легко расширять или изменять систему без необходимости изменения остальной части приложения, что улучшает гибкость, масштабируемость и существенно упрощяет интеграцию нового модуля
Главным минусамом я бы назвал некоторую непрозрачность: порой бывает сложно отследить куда и как протекают данные, особенно в больших приложениях, но ИМХО это относительно легко решается базовой организацией кода
Лично мне уже не раз доводилось использовать шину в проде. Нравится она мне своей простотой и тем, что позволяет легко соединять несоединяемое. В связке с реактом, можно легко избавиться от лишних ререндеров или, например, от prop-drilling'a
Также мне не нравится иметь какую-то "глобальную" шину на весь проект. Мне больше нравится создавать специфичные каналы событий, потому что это разгружает саму шину и позволяет более просто отслеживать потоки данных. В моей реализации всё решение выглядит примерно так:
Я создаю отдельный файл для инициализации канала
И далее использую экспортируемые сущности примерно так:
Конечно же, реагировать на событие в шине можно не только в компонентах: можно вообще где угодно. Для этого в
Вся "магия" тут заложена внутри функции
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #theory #code
Шина событий — это паттерн, который используется для взаимодействия различных компонентов системы не напрямую, а через некоторый посредник — саму шину
Шина может принимать в себя публикацию событий, а далее оповещать о произошедшем всех своих подписчиков. Такая слабая связанность позволяет сделать систему более модульной и гибкой
Главным преимуществом шины событий я бы назвал ослабленную связность между компонентами системы. Благодаря этому, компоненты могут взаимодействовать через события, не зная о существовании друг друга и не дожидаясь ответной реакции
Это позволяет легко расширять или изменять систему без необходимости изменения остальной части приложения, что улучшает гибкость, масштабируемость и существенно упрощяет интеграцию нового модуля
Также с помощью шины можно существенно улучшать перфоманс приложения, более подробно об этом сказано в отличном видео синяка
Главным минусамом я бы назвал некоторую непрозрачность: порой бывает сложно отследить куда и как протекают данные, особенно в больших приложениях, но ИМХО это относительно легко решается базовой организацией кода
Лично мне уже не раз доводилось использовать шину в проде. Нравится она мне своей простотой и тем, что позволяет легко соединять несоединяемое. В связке с реактом, можно легко избавиться от лишних ререндеров или, например, от prop-drilling'a
Также мне не нравится иметь какую-то "глобальную" шину на весь проект. Мне больше нравится создавать специфичные каналы событий, потому что это разгружает саму шину и позволяет более просто отслеживать потоки данных. В моей реализации всё решение выглядит примерно так:
Я создаю отдельный файл для инициализации канала
export const {useEvent: useSpecificEvent, ...specificEventChannel} = createEventChannel<{
event: (options: Foo) => void;
}>()
И далее использую экспортируемые сущности примерно так:
// где угодно: публикация события в шину
specificEventChannel.emit('event', options)
// в react-компоненте: реакция на событие
useSpecificEvent('event', (options) => {
...
})
Конечно же, реагировать на событие в шине можно не только в компонентах: можно вообще где угодно. Для этого в
specificEventChannel
есть дополнительные функции on
, off
и once
для подписки, отписки и единоразовой реакции соответственноВся "магия" тут заложена внутри функции
createEventChannel
. Сама по себе шина там достаточно типовая, что-то невероятное придумать сложно, однако код всё равно достаточно занятный, полный код можно найти в этом гистеСпасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #theory #code
❤23🐳7👍6🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Pattern matching
Pattern matching — это крутой концепт, который позволяет делать что либо в зависимости от совпадения с тем или иным шаблоном
Самой примитивной реализацией концепта в JavaScript можно считать объекты или конструкцию
Примерно то же самое можно реализовать и со
Есть такая библиотечка — ts-pattern. Она же позволяет отойти от примитивных примеров и использовать полноценный pattern matching, включая анализ вложенных структур и более сложные условия
На главной странице библиотечки есть очень наглядная гифка. Я предлагаю посмотреть на неё повнимательнее, описывать что-то дополнительно не вижу смысла
Скажу лишь то, что мне доводилось пользоваться этой библиотечкой и, как по мне, работает она шикарно. У меня остался исключительно положительный опыт)
Сам по себе концепт невероятно полезен для упрощения кода. Такой код проще читать, изменять и поддерживать. А в совокупности с
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #theory #code
Pattern matching — это крутой концепт, который позволяет делать что либо в зависимости от совпадения с тем или иным шаблоном
В качестве шаблона может выступать какая-то константа или предикат
Самой примитивной реализацией концепта в JavaScript можно считать объекты или конструкцию
switch-case
:const statusIcon = {
warning: <WarningIcon />,
success: <SuccessIcon />,
error: <ErrorIcon />,
loading: <Spinner />
}
const status = "error"
const matched = statusIcon[status]
Примерно то же самое можно реализовать и со
switch-case
, но всё это не так интересноЕсть такая библиотечка — ts-pattern. Она же позволяет отойти от примитивных примеров и использовать полноценный pattern matching, включая анализ вложенных структур и более сложные условия
На главной странице библиотечки есть очень наглядная гифка. Я предлагаю посмотреть на неё повнимательнее, описывать что-то дополнительно не вижу смысла
Скажу лишь то, что мне доводилось пользоваться этой библиотечкой и, как по мне, работает она шикарно. У меня остался исключительно положительный опыт)
Сам по себе концепт невероятно полезен для упрощения кода. Такой код проще читать, изменять и поддерживать. А в совокупности с
ts-pattern
, решение будет ещё и типобезопасно, что также неоспоримый плюсСпасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #theory #code
🔥24👍7❤3🐳3🤔1
Порталы в React
Сталкивались когда-нибудь с проблемой, когда нужно рендерить элемент за пределами текущей DOM-иерархии?
Для таких задач React предлагает решение — порталы
В документации реакта приведён такой код:
Работает этот код проще простого: элемент, переданный в функцию
Такой код будет работать, но он не очень удобен, поэтому многие компонентные библиотеки максимально упрощают порталы для разработчиков и делают подобные компоненты — они есть в Chakra UI, Material UI, Semantic UI и других либах
Если максимально упростить, то можно прийти к такому варианту:
Тут стоит уточнить две детали:
1. Мы создаём новый
Если мы будем рендерить контент сразу в
2. В
Тоже полезно, чтобы лишний раз не задумываться о том, как живёт портал и не создавать ненужных элементов в вёрстке
Да и всё. Тут компонент на 10 строчек буквально, ничего сверхъестественного. Если вам нужны порталы, то задумайтесь — скорее всего вам хватит такой простой реализации
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #theory #code
Сталкивались когда-нибудь с проблемой, когда нужно рендерить элемент за пределами текущей DOM-иерархии?
Например, модальные окна, которые не должны быть вложены в основное дерево из-за проблем с позиционированием или всплывающие подсказки, которые всегда должны быть на переднем плане
Для таких задач React предлагает решение — порталы
В документации реакта приведён такой код:
import { createPortal } from 'react-dom';
<div>
<p>Текст расположен в родительском диве</p>
{createPortal(
<p>Текст расположен в document.body </p>,
document.body
)}
</div>
Работает этот код проще простого: элемент, переданный в функцию
createPortal
, будет маунтиться реактом не в родительский див, а в document.body
. Работать это будет с деревом любой вложенностиТакой код будет работать, но он не очень удобен, поэтому многие компонентные библиотеки максимально упрощают порталы для разработчиков и делают подобные компоненты — они есть в Chakra UI, Material UI, Semantic UI и других либах
Но, на самом деле, там нет ничего сложного
Если максимально упростить, то можно прийти к такому варианту:
const Portal = ({ children }: PropsWithChildren) => {
const [container] = useState(() => document.createElement('div'));
useLayoutEffect(() => {
document.body.appendChild(container);
return () => {
document.body.removeChild(container);
};
}, [container]);
return createPortal(children, container);
}
// ...
<Portal>
<p>Текст внутри портала</p>
</Portal>
Тут стоит уточнить две детали:
1. Мы создаём новый
div
внутри useState
, чтобы проще было контролировать порталЕсли мы будем рендерить контент сразу в
document.body
, то можно словить много проблем со стилями и отслеживанием самого порталаИспользуем мы именно useState, чтобы создавать элемент единожды и гарантировано на первый рендер компонента. Элемент создается внутри колбека инициализации состояния — он всегда вызывается единожды на маунт компонента
Как альтернатива, можно обойтись и рефом
2. В
useLayoutEffect
мы привязываем жизненный цикл тега-обёртки к циклу компонента порталаТоже полезно, чтобы лишний раз не задумываться о том, как живёт портал и не создавать ненужных элементов в вёрстке
useLayoutEffect используется вместо useEffect, чтобы обрабатывать портал без лишних мерцаний и более плавно
Вообще, тема разных эффектов и где какой использовать — это отдельная крупная тема, возможно сделаю об этом пост в будущем
Да и всё. Тут компонент на 10 строчек буквально, ничего сверхъестественного. Если вам нужны порталы, то задумайтесь — скорее всего вам хватит такой простой реализации
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #theory #code
❤21👍11🐳3🔥1👏1
Составные компоненты
Есть такой паттерн для реакта, который называется Compound Components. Это можно перевести как "составные компоненты"
Запутались в словах? Лучше посмотреть в коде:
Вот пример прям из доки Ant Design:
Обратили внимание, как компоненты
Зачем же так сделали? Тут преследуется три цели:
1. Явно показать на уровне нейминга, что использовать
2. Расшарить общий контекст между всеми компонентами
3. Корректно стилизовать части
С неймингом и стилями, думаю, всё предельно ясно. Но что насчёт контекста? На самом деле,
В итоге получится, что все дочерние компоненты, пытающиеся получить доступ к контексту, без обёртки работать будут криво или и вовсе не будут
С точки зрения типов всё тоже не сложно. Тот же Ant Design делает так:
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #typescript #theory #code #react #patterns
Есть такой паттерн для реакта, который называется Compound Components. Это можно перевести как "составные компоненты"
Смысл этого паттерна заключается в том, что мы можем связать компоненты общим окружением и эффективнее использовать их вместе
То есть мы можем заранее объединить компоненты каким-то контекстом и переиспользовать их, например, через общее пространство имён, в качестве которого чаще всего выступает родительский компонент
Вот пример прям из доки Ant Design:
import { Layout } from 'antd';
<Layout>
<Layout.Header>Header</Layout.Header>
<Layout.Content>Content</Layout.Content>
<Layout.Footer>Footer</Layout.Footer>
</Layout>
Обратили внимание, как компоненты
Header
, Content
и Footer
мы получаем напрямую из компонента Layout
? Это и есть пример паттерна Compound Components. Компоненты связаны, а используются они из общего пространства — компонента Layout
Зачем же так сделали? Тут преследуется три цели:
1. Явно показать на уровне нейминга, что использовать
Layout.Footer
вне Layout
не нужно2. Расшарить общий контекст между всеми компонентами
Layout
3. Корректно стилизовать части
Layout
в зависимости от значения внутри общего контекстаС неймингом и стилями, думаю, всё предельно ясно. Но что насчёт контекста? На самом деле,
Layout
под собой содержит ещё и LayoutContext
, который содержит в себе состояние компонента Sider
и распространяет его на все дочерние компоненты. Схематически это выглядит примерно так:InternalLayout
└ LayoutContext <-- инициализируем контекст
├ Header
├ Content <-- а в этих компонентах получаем его значение
├ Footer
└ Sider
В итоге получится, что все дочерние компоненты, пытающиеся получить доступ к контексту, без обёртки работать будут криво или и вовсе не будут
С точки зрения типов всё тоже не сложно. Тот же Ant Design делает так:
import InternalLayout, { Content, Footer, Header } from './layout';
import Sider from './Sider';
// получаем тип базового layout компонента
type InternalLayoutType = typeof InternalLayout;
// создаём тип, который определит какие компоненты мы вложим в layout
type CompoundedComponent = InternalLayoutType & {
Header: typeof Header;
Footer: typeof Footer;
Content: typeof Content;
Sider: typeof Sider;
};
// нагло переприсваиваем тип
const Layout = InternalLayout as CompoundedComponent;
// нагло биндим нужные компоненты
Layout.Header = Header;
Layout.Footer = Footer;
Layout.Content = Content;
Layout.Sider = Sider;
// не менее нагло экспортируем как public-api
export default Layout;
Спасибо за прочтение, это важно для меня ❤️
@prog_way_blog — чат — #web #javascript #typescript #theory #code #react #patterns
👍26❤8🔥5🐳3
Теги для шаблонных строк
В JavaScript есть, как по мне, крайне странный синтаксис. Самым очевидным его применением можно считать
В результате выполнения этого чуда мы получим компонент на основе нативного
Но вы когда нибудь задумывались, что
На самом деле, самый базовый пример такого синтаксиса можно рассмотреть так:
Всё, что делает эта функция — собирает строку из шаблона и подставленных переменных
—
—
Попробуем вызвать нашу функцию:
Использование обратных кавычек после именования функции вызывает эту самую функцию
По такому же принципу и работает
Этот синтаксис очень специфичный, ему не так много применений, но всё таки в некоторых случаях он бывает очень удобен. Например, в призме, с помощью такого такого синтаксиса можно кинуть запрос в БДшку
В реальной жизни вам скорее всего не понадобится писать подобные функции, но вдруг..
Спасибо за прочтение, это важно для меня❤️
@prog_way_blog — чат — #web #javascript #theory #data #code
В JavaScript есть, как по мне, крайне странный синтаксис. Самым очевидным его применением можно считать
styled-components
и выглядит всё это примерно так:const display = 'flex';
const Button = styled.button`
padding: 10px;
color: red;
display: ${display}
`
В результате выполнения этого чуда мы получим компонент на основе нативного
button
с предустановленными стилями из литераловНо вы когда нибудь задумывались, что
styled.button
— это тоже функция? А как она вызывается? Как устроена внутри?На самом деле, самый базовый пример такого синтаксиса можно рассмотреть так:
function foo(strings, ...values) {
let result = strings[0];
values.forEach((value, index) => {
result += value + strings[index + 1];
});
return result;
}
Всё, что делает эта функция — собирает строку из шаблона и подставленных переменных
—
strings
— массив строк, содержащий все части текста, разделенные переменными—
values
— массив значений, которые вставляются внутрь шаблонаПопробуем вызвать нашу функцию:
const name = "Денис"
const channel = "progway"
foo`Меня зовут ${name} и я люблю ${channel}`
Использование обратных кавычек после именования функции вызывает эту самую функцию
Для нашего примера, strings — это:
[
"Меня зовут ",
" и я люблю ",
""
]
а values:
[
"Денис",
"progway"
]
По такому же принципу и работает
styled-components
, конечно же, с более сложной логикой внутриЭтот синтаксис очень специфичный, ему не так много применений, но всё таки в некоторых случаях он бывает очень удобен. Например, в призме, с помощью такого такого синтаксиса можно кинуть запрос в БДшку
В реальной жизни вам скорее всего не понадобится писать подобные функции, но вдруг..
Спасибо за прочтение, это важно для меня
@prog_way_blog — чат — #web #javascript #theory #data #code
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👍9❤8😐2🥰1🐳1
progway — программирование, IT
Теги для шаблонных строк В JavaScript есть, как по мне, крайне странный синтаксис. Самым очевидным его применением можно считать styled-components и выглядит всё это примерно так: const display = 'flex'; const Button = styled.button` padding: 10px; …
Ещё один пример
Бывает такое, что нужно встроить в строку значение, которое может быть пустым. Обычно пишутся доп. проверки:
Можно решить эту же задачу с помощью функции из поста выше, вот так это будет:
Просто вернем
Ну типа костыль. А вроде и нет. Просто ещё один пример посмотреть как это можно применить
@prog_way_blog — чат — #javascript #code
Бывает такое, что нужно встроить в строку значение, которое может быть пустым. Обычно пишутся доп. проверки:
const order = {
city: "Москва" // представим, что возможно undefined или null
}
const { city } = order
// могут писать что-то типа такой проверки
city ? `Ваш город: ${city}` : null
Можно решить эту же задачу с помощью функции из поста выше, вот так это будет:
type SubstitutionPrimitive = string | number | boolean | undefined | null;
const isNullOrUndefined = (value: SubstitutionPrimitive): value is undefined | null => {
return value === undefined || value === null;
};
const safePaste = (strings: TemplateStringsArray, ...substitutions: SubstitutionPrimitive[]) => {
let result = strings[0];
for (let index = 0; index < substitutions.length; index++) {
const value = substitutions[index];
if (isNullOrUndefined(value)) return null;
result += value + strings[index + 1];
}
return result;
};
Просто вернем
null
вместо строки, если какое либо из значений в подстановках null
или undefined
. Вот так это будет вызываться:const apple = {
name: 'Яблоко',
};
const orange = {};
safePaste`Товар: "${apple?.name}"`;
// Товар: "Яблоко"
safePaste`Товар: "${orange?.name}"`;
// null
Ну типа костыль. А вроде и нет. Просто ещё один пример посмотреть как это можно применить
@prog_way_blog — чат — #javascript #code
🔥14👍6❤4🐳4
Связываем React и localStorage через useSyncExternalStore
Как согласовать изменение состояния в реакте и поля в
Также можно обработать какое-то не-реактовое значение через комбинацию
Но не так давно в 18 версию
Многие скипнули его и даже не знают зачем он нужен, что, в целом, достаточно ожидаемо, ведь даже команда разработчиков позиционировала его больше как хук для разработчиков библиотек, а мы тут далеко не все пишем свои либы
Короче, что это за хук вообще? Очень просто — этот хук нужен для более глубокой интеграции внешних хранилищ в модель React. Говоря проще — хук нужен для того, чтобы триггерить рендер из внешних хранилищ, а не только через
Как раз этот хук и поможет нам интегрироваться с
На коленке код будет выглядеть примерно так:
В чём тут идея:
1. При вызове
2. В функции подписки
Использовать будем как обычный
Теперь хук при вызове с одним и тем же ключом к
Используя тот же подход, можно реализовать порой очень полезные хуки
Спасибо за прочтение, это важно для меня❤️
@prog_way_blog — чат — #web #javascript #theory #data #code #react
Как согласовать изменение состояния в реакте и поля в
localStorage
? До недавнего времени самым простым вариантом было создать контекст с внутренним React состоянием и обрабатывать всё взаимодействие с localStorage
через него — вариант рабочий, но далеко не идеален: легко напороться на ререндеры, много кода писать нужно ну и вот это вот всёТакже можно обработать какое-то не-реактовое значение через комбинацию
useState
+ useEffect
, но это ещё менее надёжно, ведь браузерные значения могут меняться и без уведомления реакта, и, соответственно, без ререндераКрасиво в одной из статей на хабре описали:
Для работы с состоянием в React используются хуки useState и useReducer, но они не умеют работать с состоянием, которое "живет" за пределами React, поскольку в один момент времени доступна только одна версия внешнего состояния.
Значения внешнего состояния могут меняться со временем без ведома React, что может приводить к таким проблемам, как отображение двух разных значений для одних и тех же данных.
Статья: https://habr.com/ru/companies/timeweb/articles/720136/
Но не так давно в 18 версию
React
добавили хук useSyncExternalStore
, который такую задачу решает намного изящнееМногие скипнули его и даже не знают зачем он нужен, что, в целом, достаточно ожидаемо, ведь даже команда разработчиков позиционировала его больше как хук для разработчиков библиотек, а мы тут далеко не все пишем свои либы
Короче, что это за хук вообще? Очень просто — этот хук нужен для более глубокой интеграции внешних хранилищ в модель React. Говоря проще — хук нужен для того, чтобы триггерить рендер из внешних хранилищ, а не только через
setState
функции Как раз этот хук и поможет нам интегрироваться с
localStorage
сильно проще и безопаснее. Тут localStorage
в понятие внешнего хранилища ложится просто шикарноНа коленке код будет выглядеть примерно так:
const useLocalStorageState = (key: string, defaultValue?: string) => {
const subscribe = (listener: () => void) => {
window.addEventListener("update-local-storage", listener);
return () => void window.removeEventListener("update-local-storage", listener);
};
const getSnapshot = () => localStorage.getItem(key) ?? defaultValue;
const store = useSyncExternalStore(subscribe, getSnapshot);
const updateStore = (newValue: string) => {
localStorage.setItem(key, newValue);
window.dispatchEvent(new StorageEvent("update-local-storage", { key, newValue }));
};
return [store, updateStore] as const;
};
В чём тут идея:
1. При вызове
updateStore
будем помимо изменения значения в localStorage
диспатчить на window
ещё и StorageEvent
с ключом, например, "update-local-storage"
2. В функции подписки
subscribe
объясним когда нужно вызывать getSnapshot
для получения актуального состояния из внешнего хранилища и когда от его прослушивания нужно отписаться. Можно воспринимать как эффектИспользовать будем как обычный
useState
:const [name, setName] = useLocalStorageState("name", "progway");
Теперь хук при вызове с одним и тем же ключом к
localStorage
(name
в примере выше) будет обновлять все зависимые компоненты при регистрации события "update-local-storage"
на window
Используя тот же подход, можно реализовать порой очень полезные хуки
useMediaQuery
, useWindowSize
и другие. О первых двух можно прочитать в статье от Timeweb CloudСпасибо за прочтение, это важно для меня
@prog_way_blog — чат — #web #javascript #theory #data #code #react
Please open Telegram to view this post
VIEW IN TELEGRAM
❤28👍11🔥6🐳2
Что такое Server-Sent Events
Часто
1. Нам нужно постоянно получать обновления с сервера
2. Не нужно постоянно отправлять что-то с клиента
Фикус в том, что держать
Для реализации понадобится только простенький эндпоинт на сервере, а далее процесс выглядит так:
1. Клиент делает
2. Сервер создаёт
3. Клиент подписывается на новое сообщение в стриме
На сервере это будет выглядеть примерно так:
На клиенте это будет выглядеть примерно так:
С таким кодом мы будем получать на клиенте сообщение "ПРИВЕТ!" каждую секунду
При этом, конечно же, никто не мешает усложнить логику со стороны сервера, и пушить новые сообщения в стрим не каждую секунду, а только при изменении данных
И конечно же никто не запрещает обернуть стрим в какой-нибудь React хук и сделать дженеричное решение для всего проекта/проектов
Если вы ни разу не работали SSE, то очень рекомендую потыкать хотя бы в песочнице — очень крутая штука!
Спасибо за прочтение, это важно для меня🥰
@prog_way_blog — чат — #theory #javascript #code #data #web
SSE
— это технология для однонаправленного соединения между сервером и клиентом, которая позволяет серверу отправлять обновления данных в реальном времениЧасто
SSE
могут стать отличной альтернативой WebSocket
. Он отлично подойдёт для кейсов, когда:1. Нам нужно постоянно получать обновления с сервера
2. Не нужно постоянно отправлять что-то с клиента
Такая односторонняя связь полезна при реализации:
— уведомлений
— обновления данных в реальном времени (цен, загрузки CPU...)
— индикатора прогресса загрузки большого файла
— даже в играх
И многих других случаях
Фикус в том, что держать
SSE
гораздо проще и дешевле, чем держать WebSocket
. Как по коду, так и по перфомансуДля реализации понадобится только простенький эндпоинт на сервере, а далее процесс выглядит так:
1. Клиент делает
GET
запрос на подготовленный эндпоинт через EventStream
2. Сервер создаёт
event-stream
, просто устанавливая нужный заголовок. Соединение не закрывается, и с этого момента сервер может пушить в стрим любые строковые данные3. Клиент подписывается на новое сообщение в стриме
На сервере это будет выглядеть примерно так:
const http = require('http');
http.createServer((req, res) => {
if (req.url === '/stream') {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
setInterval(() => {
res.write('data: ПРИВЕТ!\n\n');
}, 1000);
}
}).listen(3000);
На клиенте это будет выглядеть примерно так:
const source = EventSource('/stream')
sourse.addEventListener('message', (message) => {
console.log(message.data)
})
С таким кодом мы будем получать на клиенте сообщение "ПРИВЕТ!" каждую секунду
При этом, конечно же, никто не мешает усложнить логику со стороны сервера, и пушить новые сообщения в стрим не каждую секунду, а только при изменении данных
И конечно же никто не запрещает обернуть стрим в какой-нибудь React хук и сделать дженеричное решение для всего проекта/проектов
Если вы ни разу не работали SSE, то очень рекомендую потыкать хотя бы в песочнице — очень крутая штука!
Если кратко:
SSE — технология однонаправленной связи от сервера к клиенту
С помощью SSE можно обновлять данные на клиенте в рамках одного соединения в реальном времени
Спасибо за прочтение, это важно для меня
@prog_way_blog — чат — #theory #javascript #code #data #web
Please open Telegram to view this post
VIEW IN TELEGRAM
👍39❤13🔥7🐳3
Как реагировать на изменения объекта
В JavaScript обычные объекты не умеют уведомлять о своих изменениях, однако эту задачу можно решить с помощью
Ловушек много —
Например, можно переопределить поведение объекта при обращении к какому-нибудь из его свойств:
Но это лишь частный случай, можно сделать более утилитарный пример:
Сам
В первую очередь прокси удобен конечно же для создания реактивных систем, но также его можно применять, например, для валидации свойств и логирования
Удобно, что
Спасибо за прочтение, это важно для меня❤️
@prog_way_blog — чат — #theory #useful #javascript #code #web #patterns
В JavaScript обычные объекты не умеют уведомлять о своих изменениях, однако эту задачу можно решить с помощью
Proxy
Proxy
— это специальный встроенный в язык объект-обёртка, который позволяет изменить поведение других объектов, перехватывая действия над нимиnew Proxy(target, handlers)
создаёт прокси для объекта target
, где handler
содержит ловушки для перехвата операцийЛовушек много —
get
, set
, deleteProperty
, has
... (подробнее на MDN) — каждая из ловушек переопределяет реакцию объекта на взаимодействие с нимНапример, можно переопределить поведение объекта при обращении к какому-нибудь из его свойств:
const user = { name: "Денис", age: 23 };
const proxyUser = new Proxy(user, {
get(target, key) {
return key in target ? target[key] : "Не найдено";
}
});
proxyUser.name // "Денис"
proxyUser.city // "Не найдено"
Но это лишь частный случай, можно сделать более утилитарный пример:
const reactive = (obj, callback) => {
return new Proxy(obj, {
set(target, key, value) {
target[key] = value; // обновляем значение
callback(key, value); // вызываем реакцию
return true;
}
});
};
// Используем:
const state = reactive({ count: 0 }, (key, value) => {
console.log(`Свойство "${key}" изменилось:`, value);
});
state.count = 1; // Лог: "Свойство 'count' изменилось: 1"
state.count = 5; // Лог: "Свойство 'count' изменилось: 5"
Прикрутить сюда типы и рекурсивный вызов функции reactive на каждый вложенный объект и у вас почти получится свой vue.js🗿
Сам
Proxy
— это крайне нишевый инструмент, особенно в экосистеме реакта, где его встретить крайне сложно. Обычно можно обойтись более простыми вещами, но знать про прокси тоже нужно, может и пригодится. По крайней мере, у меня такой кейс на практике всё же былВ первую очередь прокси удобен конечно же для создания реактивных систем, но также его можно применять, например, для валидации свойств и логирования
Удобно, что
Proxy
крайне прост и к нему можно прикрутить что угодно. Например, к прокси можно прилепить zod
для валидации, как это сделано в zoxy. Тут вы ограничены лишь своей фантазиейЕсли кратко:
— Proxy — обёртка, которая позволяет переопределить реакцию на операцию для объекта
— Переопределение поведения происходит при помощи "ловушек"
Спасибо за прочтение, это важно для меня
@prog_way_blog — чат — #theory #useful #javascript #code #web #patterns
Please open Telegram to view this post
VIEW IN TELEGRAM
❤28🔥15👍7🐳3🤓1