📝 Объект
Недавно у меня возникла необходимость красиво вывести результат выполнения одного скрипта в консоль, в идеале в виде таблички. И тут я вспомнила, что у объекта
📌 Первые, и самые простые, это
📌 Следующий метод как раз помог мне решить мою первоначальную задачу. Метод
📌 Совсем неожиданными для меня стали методы
📌 Ещё интересные методы - это
📌 Ну и последнее, но не менее важное, что хотелось бы рассмотреть - это методы
🔗 И это были не все методы, которые есть в объекте
console и его методыНедавно у меня возникла необходимость красиво вывести результат выполнения одного скрипта в консоль, в идеале в виде таблички. И тут я вспомнила, что у объекта
console в JavaScript существуют не только методы log и error, которые чаще всего используются, но ещё и ряд других, и поэтому я решила немного в них покопаться. 📌 Первые, и самые простые, это
log, info, warn, error и debug. Первый просто выводит сообщения любого вида в консоль, а остальные в целом делают то же самое, но задают сообщению так называемый log level. Например, warn покрасит сообщение в жёлтый, а error в красный, но итоговое оформление будет зависеть от среды выполнения. А метод debug выведет сообщение только в том случае, если в консоли включен режим дебага. 📌 Следующий метод как раз помог мне решить мою первоначальную задачу. Метод
table принимает либо двумерный массив, задающий строки таблицы, либо массив объектов с одинаковыми ключами, и на основе этих данных выводит данные в виде таблицы. Её вид также может отличаться в зависимости от среды выполнения.📌 Совсем неожиданными для меня стали методы
group и groupEnd , которые позволяют группировать выводимые в консоль сообщения, при этом групп может быть несколько и они могут быть вложенными. Например (выравнивание сделано для лучшей читаемости):
console.group("Группа");
console.log("Внутри группы");
console.group("Подгруппа");
console.log("Внутри подгруппы");
console.groupEnd();
console.groupEnd();
📌 Ещё интересные методы - это
assert и count , которые могут быть полезны при дебаге. Первый позволяет проверить истинность выражения, а второй может посчитать количество вызовов сообщений с определённой меткой. Метод count может также быть полезен, если нужно посчитать количество ререндеров конкретного компонента. Примеры:
console.assert(1 === 2, "Неправильно!"); // Выведет "Неправильно!", если утверждение ложное
console.count("Метка"); // Выведет "Метка: 1"
console.count("Метка"); // Выведет "Метка: 2"
📌 Ну и последнее, но не менее важное, что хотелось бы рассмотреть - это методы
time и timeEnd, которые берут на себя измерение времени между двумя точками в выполнении кода. Например:
console.time("Таймер"); // Задаём таймеру метку
for(let i = 0; i < 1000000; i++) {} // Некий код, который занимает время
console.timeEnd("Таймер"); // Выведет время, затраченное на выполнение цикла
🔗 И это были не все методы, которые есть в объекте
console. Подробнее об этих и других методах можно почитать на MDN, а поэкспериментировать с ними можно прямо сейчас в консоли вашего бразуера!👍2
Часто при разработке тех или иных приложений возникает потребность поддерживать двустороннюю связь между клиентом и сервером, то есть не только подгружать некоторые данные по запросу клиента, но и уметь динамически их обновлять при изменениях на сервере.
Самый простой вариант реализации такого поведение - делать периодические запросы к серверу за новыми данными. Но, во-первых, это порождает кучу лишних запросов и излишнюю нагрузку на сервер, и, во-вторых, данные будут приходить с очевидной задержкой.
Простой пример реализации API с использованием библиотеки Express:
const messagesQueue = [];
// Route для отправки сообщений на сервер
app.post('/send', express.json(), (req, res) => {
const { message } = req.body;
messagesQueue.push(message);
res.status(200).send('Message received');
});
// Route для long polling
app.get('/poll', (req, res) => {
function checkForMessages() {
if (messagesQueue.length > 0) {
const messages = messagesQueue;
messagesQueue = [];
res.json(messages);
} else {
// Проверяем сообщения каждые 500 мс
setTimeout(checkForMessages, 500);
}
}
checkForMessages();
});
Как результат, на клиентах обновления будут ловиться с помощью метода
/poll, а обновляться с помощью метода /send. Один из плюсов этого метода с точки зрения сервера в том, что для его поддержания не нужны какие-либо дополнительные настройки, так как используются обыкновенные http-запросы.1. Использование ресурсов сервера - каждый Long Polling запрос требует, чтобы сервер удерживал соединение в открытом состоянии, что потребляет ресурсы сервера. Это может стать проблемой при большом количестве клиентов.
2. Задержка ответа - существует задержка между моментом появления данных и их получением клиентом, поскольку сервер должен дождаться окончания заданного интервала или появления данных перед тем, как ответить на заявку.
3. Управление ошибками и таймаутами - требуется аккуратное управление ошибками и таймаутами, чтобы избежать утечки ресурсов и удерживания соединений открытыми без необходимости.
4. Масштабируемость - масштабирование приложений с использованием Long Polling может быть сложным, поскольку каждое удерживаемое соединение занимает серверные ресурсы.
5. Отсутствие симметричности отправки и приёма сообщений - Long Polling оптимизирован для сценариев, где клиент чаще получает данные, чем отправляет. Направленная связь в обратном направлении не так эффективна.
🪄 Чуть более сложный пример реализации для обыкновенного чата на основе Long Polling есть в нашем репозитории. А в Live Demo можно потрогать результат и пообщаться!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3🔥2
📌 Навигация по каналу
Если вы только присоединились к нам или просто хотите освежить знания, мы составили для вас удобную подборку наших материалов по ключевым темам:
👨💻 JavaScript:
- Потеря контекста, this и globalThis:
- Часть 1
- Часть 2
- Объект console и его методы
- IntersectionObserver
- CommonJS и ECMAScript Modules: в чём разница?
🖌️ CSS:
- Анимации в CSS:
- Часть 1
- Часть 2
- Z-index и контекст наложения:
- Часть 1
- Часть 2
- Видео на канале
- Как сделать адаптивный iframe
- Как подключить локальные шрифты к вашему сайту
📜 HTML теги:
- picture
- audio и video
- Теги для форм
- svg
💻 TypeScript:
- Полезные директивы TypeScript
- TypeScript 5.5:
- Часть 1
- Часть 2
🔥 React:
- Обзор новинок React 19
- Новые хуки React 19 с примерами:
- Часть 1
- Часть 2
- React Compiler
- Server Components и Server Actions:
- Часть 1
- Часть 2
- State-менеджеры vs React Hooks: когда и что лучше использовать
- Библиотека react-use
- useLayoutEffect
🤝 Двусторонняя клиент-серверная связь:
- Long Polling
- WebSocket
- Server-Sent Events
🌐 Сети и инфраструктура:
- Что такое CDN
- Что такое S3
- JWT токены
🌟 Веб-разработка:
- Как работают поисковые системы и как поднять свой сайт в поисковой выдаче
- Что нужно знать фронтенд-разработчику за пределами фронтенда
- Полезные фишки DevTools
- UI-библиотеки
🛠️ Инструменты сборки:
- Виды зависимостей в package.json
- Семантическое версионирование
- Линтеры на фронтенде: Prettier, ESLint, Stylelint
- Prettier, ESLint и Stylelint: в чем разница?
- Как настроить Prettier, ESlint, Styleling и Husky в проекте с нуля
- Пресет для демо с помощью Vite и Express
- Инструменты для сборки на фронтенде
🔒 Безопасность:
- Что такое CSRF
💡 Архитектура:
- Отличия Stateless и Statefull
📖 Приятного чтения!
Лайки, комментарии и репосты помогают нам развиваться и делать контент ещё лучше! 👍
Если вы только присоединились к нам или просто хотите освежить знания, мы составили для вас удобную подборку наших материалов по ключевым темам:
👨💻 JavaScript:
- Потеря контекста, this и globalThis:
- Часть 1
- Часть 2
- Объект console и его методы
- IntersectionObserver
- CommonJS и ECMAScript Modules: в чём разница?
🖌️ CSS:
- Анимации в CSS:
- Часть 1
- Часть 2
- Z-index и контекст наложения:
- Часть 1
- Часть 2
- Видео на канале
- Как сделать адаптивный iframe
- Как подключить локальные шрифты к вашему сайту
📜 HTML теги:
- picture
- audio и video
- Теги для форм
- svg
💻 TypeScript:
- Полезные директивы TypeScript
- TypeScript 5.5:
- Часть 1
- Часть 2
🔥 React:
- Обзор новинок React 19
- Новые хуки React 19 с примерами:
- Часть 1
- Часть 2
- React Compiler
- Server Components и Server Actions:
- Часть 1
- Часть 2
- State-менеджеры vs React Hooks: когда и что лучше использовать
- Библиотека react-use
- useLayoutEffect
🤝 Двусторонняя клиент-серверная связь:
- Long Polling
- WebSocket
- Server-Sent Events
🌐 Сети и инфраструктура:
- Что такое CDN
- Что такое S3
- JWT токены
🌟 Веб-разработка:
- Как работают поисковые системы и как поднять свой сайт в поисковой выдаче
- Что нужно знать фронтенд-разработчику за пределами фронтенда
- Полезные фишки DevTools
- UI-библиотеки
🛠️ Инструменты сборки:
- Виды зависимостей в package.json
- Семантическое версионирование
- Линтеры на фронтенде: Prettier, ESLint, Stylelint
- Prettier, ESLint и Stylelint: в чем разница?
- Как настроить Prettier, ESlint, Styleling и Husky в проекте с нуля
- Пресет для демо с помощью Vite и Express
- Инструменты для сборки на фронтенде
🔒 Безопасность:
- Что такое CSRF
💡 Архитектура:
- Отличия Stateless и Statefull
📖 Приятного чтения!
Лайки, комментарии и репосты помогают нам развиваться и делать контент ещё лучше! 👍
👍3
Настя Котова // Frontend & Node.js pinned «📌 Навигация по каналу Если вы только присоединились к нам или просто хотите освежить знания, мы составили для вас удобную подборку наших материалов по ключевым темам: 👨💻 JavaScript: - Потеря контекста, this и globalThis: - Часть 1 - Часть 2 - Объект…»
🚀 React Compiler
Сегодня разберем новый экспериментальный компилятор от команды React — React Compiler. Этот компилятор призван улучшить производительность приложений React, оптимизируя их на этапе сборки. Пока он еще в стадии разработки, но уже доступен для тестирования.
Особенности
- Работает с обычным JavaScript и следует правилам React, что позволяет использовать его без переписывания кода.
- Включает в себя плагин eslint, который помогает улучшить качество кода, показывая анализ компилятора прямо в вашей IDE.
- Поддерживает React 19 RC.
Как это работает
React Compiler помогает ре-рендерить компоненты только там, где это действительно необходимо. Например:
В стандартной ситуации, каждое изменение в input приводит к перерендеру кнопки в форме. Чтобы избежать этого раньше нам пришлось бы обернуть этот компонент в React.memo(). Однако, с React Compiler компонент Button будет перерисовываться только когда это действительно требуется, благодаря автоматической мемоизации.
🔗 Полезные ссылки:
- Плагин для eslint
- Подключение к проекту с помощью babel-плагина
- Пример использования в проекте на vite можно найти в нашем репозитории на GitHub
Это звучит как магия, и я, если честно, не до конца верила, что такое может работать, но оно действительно работает!)
Сегодня разберем новый экспериментальный компилятор от команды React — React Compiler. Этот компилятор призван улучшить производительность приложений React, оптимизируя их на этапе сборки. Пока он еще в стадии разработки, но уже доступен для тестирования.
Особенности
- Работает с обычным JavaScript и следует правилам React, что позволяет использовать его без переписывания кода.
- Включает в себя плагин eslint, который помогает улучшить качество кода, показывая анализ компилятора прямо в вашей IDE.
- Поддерживает React 19 RC.
Как это работает
React Compiler помогает ре-рендерить компоненты только там, где это действительно необходимо. Например:
import { useState } from 'react';
function Button({ title }) {
console.log('[Button] Render');
return <button type="button">{title}</button>;
}
function Form() {
const [name, setName] = useState('');
const handleChangeName = (event) => setName(event.target.value);
console.log('[Form] Render');
return (
<form>
<input name="firstName" value={name} onChange={handleChangeName} />
<Button title="Ok" />
</form>
);
}В стандартной ситуации, каждое изменение в input приводит к перерендеру кнопки в форме. Чтобы избежать этого раньше нам пришлось бы обернуть этот компонент в React.memo(). Однако, с React Compiler компонент Button будет перерисовываться только когда это действительно требуется, благодаря автоматической мемоизации.
🔗 Полезные ссылки:
- Плагин для eslint
- Подключение к проекту с помощью babel-плагина
- Пример использования в проекте на vite можно найти в нашем репозитории на GitHub
Это звучит как магия, и я, если честно, не до конца верила, что такое может работать, но оно действительно работает!)
👍1
🤝🏻 Двусторонняя клиент-серверная связь, часть 2: WebSocket
Несколько постов назад я писала о том, что порой нам нужно поддерживать двустороннюю связь между клиентом и сервером и приводила пример реализации такой связи с помощью Long Polling. Сегодня поговорим о другом более распространенном методе реализации такой связи - использование WebSocket.
Итак, с помощью WebSocket можно открыть так называемый “туннель” между клиентом и сервером, который будет пересылать данные в обоих направлениях. При этом сервер должен предоставлять адрес, по которому этот туннель можно открыть, и обрабатывать входящие соединения, а клиент, в свою очередь, будет инициировать эти соединения.
Передавать в такой туннель можно не любые, а только текстовые и двоичные данные (binary data), а также специальные фреймы, которые помогают отслеживать и менять статус соединения. То есть перекидываться совсем любыми JavaScript-объектами не получится, но вряд ли это будет проблемой. В качестве примера можно рассмотреть небольшой фрагмент кода на Express, который принимает объекты сообщений, которые приведены к строке через
Если наш сервер поднят на
Вот и всё! На самом деле, базово всё очень просто. Конечно, WebSocket API предоставляет больше методов, чем
🔗 Более подробный пример реализации для чата есть в нашем репозитории. И для этого типа связи мы также подготовили Live Demo, в котором можно потрогать результат и пообщаться.
🤔 И снова вопрос: какие тут могут быть сложности? А вот какие:
1. Безопасность: По умолчанию WebSocket не обеспечивает шифрование данных, что может создать уязвимости. Чтобы обеспечить безопасность, следует использовать WSS (WebSocket Secure), который работает через TLS/SSL.
2. Совместимость с браузерами: Хотя поддержка WebSockets хороша в современных браузерах, в старых версиях или некоторых браузерах поддержка может отсутствовать или быть неполной.
3. Масштабируемость: Управление большим количеством соединений WebSocket также может быть ресурсоемким, и требует дополнительных настроек на сервере для поддержания большого числа одновременных соединений.
4. Управление состоянием: В отличие от стандартных HTTP-запросов, WebSocket поддерживает постоянное соединение, что означает дополнительную сложность в управлении состоянием соединения.
5. Обработка ошибок и повторные соединения: Необходимо предусмотреть механизмы для обработки потери соединения и автоматического повторного соединения.
⚡ Но если отойти от нюансов, то WebSockets - это достаточно мощный интрумент, которым пользуется огромное количество сервисов с real-time данными!
Несколько постов назад я писала о том, что порой нам нужно поддерживать двустороннюю связь между клиентом и сервером и приводила пример реализации такой связи с помощью Long Polling. Сегодня поговорим о другом более распространенном методе реализации такой связи - использование WebSocket.
Итак, с помощью WebSocket можно открыть так называемый “туннель” между клиентом и сервером, который будет пересылать данные в обоих направлениях. При этом сервер должен предоставлять адрес, по которому этот туннель можно открыть, и обрабатывать входящие соединения, а клиент, в свою очередь, будет инициировать эти соединения.
Передавать в такой туннель можно не любые, а только текстовые и двоичные данные (binary data), а также специальные фреймы, которые помогают отслеживать и менять статус соединения. То есть перекидываться совсем любыми JavaScript-объектами не получится, но вряд ли это будет проблемой. В качестве примера можно рассмотреть небольшой фрагмент кода на Express, который принимает объекты сообщений, которые приведены к строке через
JSON.stringify():
const express = require('express');
const expressWs = require('express-ws');
const app = express();
const wsApp = expressWs(app);
const allMessages = [];
// Открываем урл для соединений
app.ws('/chat', (ws, _) => {
// Подписываемся на сообщения от клиентов
ws.on('message', (message) => {
// Сохраняем сообщение в виде исходного объекта
allMessages.push(JSON.parse(message));
// Рассылаем сообщение всем остальным подключенным клиентам
wsApp.getWss().clients.forEach((client) => {
client.send(message.toString());
});
});
});
Если наш сервер поднят на
http://localhost:8000, то туннель будет доступен по адресу ws://localhost:8000/chat. При этом подключение на клиенте будет выглядеть следующим образом:
const ws = new WebSocket('ws://localhost:8000/chat');
// Подписываемся на сообщения от сервера
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
};
// Отправляем новое сообщение на сервер
const sendMessage = (message) => {
ws.send(JSON.stringify(message));
}
Вот и всё! На самом деле, базово всё очень просто. Конечно, WebSocket API предоставляет больше методов, чем
onmessage и send, но про них можно почитать на MDN.🔗 Более подробный пример реализации для чата есть в нашем репозитории. И для этого типа связи мы также подготовили Live Demo, в котором можно потрогать результат и пообщаться.
🤔 И снова вопрос: какие тут могут быть сложности? А вот какие:
1. Безопасность: По умолчанию WebSocket не обеспечивает шифрование данных, что может создать уязвимости. Чтобы обеспечить безопасность, следует использовать WSS (WebSocket Secure), который работает через TLS/SSL.
2. Совместимость с браузерами: Хотя поддержка WebSockets хороша в современных браузерах, в старых версиях или некоторых браузерах поддержка может отсутствовать или быть неполной.
3. Масштабируемость: Управление большим количеством соединений WebSocket также может быть ресурсоемким, и требует дополнительных настроек на сервере для поддержания большого числа одновременных соединений.
4. Управление состоянием: В отличие от стандартных HTTP-запросов, WebSocket поддерживает постоянное соединение, что означает дополнительную сложность в управлении состоянием соединения.
5. Обработка ошибок и повторные соединения: Необходимо предусмотреть механизмы для обработки потери соединения и автоматического повторного соединения.
⚡ Но если отойти от нюансов, то WebSockets - это достаточно мощный интрумент, которым пользуется огромное количество сервисов с real-time данными!
👍2
Please open Telegram to view this post
VIEW IN TELEGRAM
YouTube
Как работает браузер: Критический путь рендеринга
В этом видео расскажу, как работает браузер и как он рендерит web страницы. Что такое критический путь рендеринга, layout, reflow, repaint, composite, DOM дерево, CSSOM дерево, Render Tree.
#javascript #фронтенд #frontend #htmlcssjavascript #html #css #react…
#javascript #фронтенд #frontend #htmlcssjavascript #html #css #react…
👍1
Server Components и Server Actions. Часть 1.
Саму концепцию серверных компонентов нам представили еще несколько лет назад. В том числе, она активно используется в 14 версии Next.js в App Router, который создатели фреймворка теперь предлагают по умолчанию для разработки новых приложений. Однако в React серверные компоненты и экшены были анонсированы только в 19 версии. Я решила посмотреть поподробнее, что это за покемоны.
Server Component - это неинтерактивный компонент, который работает исключительно на стороне сервера.
Server Action - это функция, которая работает на сервере, но может использоваться клиентским компонентом, например как пропс-промис, который передается в этот компонент.
Давайте посмотрим на ещё несколько интересных примеров работы с ними поподробнее. Для них будем использовать все тот же Next.js 15, который поддерживает React 19.
👀 По умолчанию в App Router все страницы и компоненты внутри них являются серверными, до тех пор, пока мы не указали обратное.
В чем особенности серверного компонента в сравнении с клиентским
🔹 Серверный компонент может быть асинхронным.
Пример простого серверного компонента, который получает данные из API другого сервиса и сразу же отображает их:
Здесь нам не нужно дополнительное состояние loading внутри компонента, т.к. мы работаем в едином потоке. Вместо запроса к другому API, мы можем использовать запрос на чтение из файла или базы данных.
🔹 Серверный компонент рендерится только один раз на сервере.
Для компонента выше у нас будет всего лишь один вывод в консоль '[List] Server Component Render' - на сервере. При классическом Server Side Rendering таких выводов будет два - один на сервере, во время формирования HTML, и один на клиенте - во время гидратации. Для серверных же компонентов гидратация не нужна.
🔹 Серверный компонент не может рендерится напрямую в клиентском компоненте, но его можно передавать туда как props.
Рассмотрим пример, у нас есть компонент Switcher, который в зависимости от состояния отрисовывает один или другой компонент:
Дальше как пропсы в этот компонент мы можем передать как клиентский компонент, так и серверный:
🔹 При динамической подгрузке серверные компоненты передаются на клиент не в виде HTML, а в специальном формате
Вот как примерно выглядят пришедшие в браузер данные для новой страницы /about в нашем приложении на Next.js:
Из этой структуры React на клиенте сможет построить DOM-дерево, но при этом не будет производить процесса гидратации, так как серверные компоненты не обладают динамикой.
📂 Подробнее примеры можно посмотреть в нашем репозитории на GitHub.
А в следующей части поговорим про Server Actions.
Саму концепцию серверных компонентов нам представили еще несколько лет назад. В том числе, она активно используется в 14 версии Next.js в App Router, который создатели фреймворка теперь предлагают по умолчанию для разработки новых приложений. Однако в React серверные компоненты и экшены были анонсированы только в 19 версии. Я решила посмотреть поподробнее, что это за покемоны.
Server Component - это неинтерактивный компонент, который работает исключительно на стороне сервера.
Server Action - это функция, которая работает на сервере, но может использоваться клиентским компонентом, например как пропс-промис, который передается в этот компонент.
Давайте посмотрим на ещё несколько интересных примеров работы с ними поподробнее. Для них будем использовать все тот же Next.js 15, который поддерживает React 19.
👀 По умолчанию в App Router все страницы и компоненты внутри них являются серверными, до тех пор, пока мы не указали обратное.
В чем особенности серверного компонента в сравнении с клиентским
🔹 Серверный компонент может быть асинхронным.
Пример простого серверного компонента, который получает данные из API другого сервиса и сразу же отображает их:
export async function List() {
const data = (await fetch('https://dummyjson.com/products/search?limit=10').then((res) => res.json()));
console.log('[List] Server Component Render');
return (
<ul>
{data.products.map((product) => (
<li key={product.id}>
{product.id}: <b>{product.title}</b>
</li>
))}
</ul>
);
}Здесь нам не нужно дополнительное состояние loading внутри компонента, т.к. мы работаем в едином потоке. Вместо запроса к другому API, мы можем использовать запрос на чтение из файла или базы данных.
🔹 Серверный компонент рендерится только один раз на сервере.
Для компонента выше у нас будет всего лишь один вывод в консоль '[List] Server Component Render' - на сервере. При классическом Server Side Rendering таких выводов будет два - один на сервере, во время формирования HTML, и один на клиенте - во время гидратации. Для серверных же компонентов гидратация не нужна.
🔹 Серверный компонент не может рендерится напрямую в клиентском компоненте, но его можно передавать туда как props.
Рассмотрим пример, у нас есть компонент Switcher, который в зависимости от состояния отрисовывает один или другой компонент:
type ComponentType = 'list' | 'searchedList';
export function Switcher({ ListComponent, SearchedListComponent }: Props) {
const [type, setType] = useState<ComponentType>('list');
const handleClick = (newType: ComponentType) => () => setType(newType);
return (
<section>
<div>
<button onClick={handleClick('list')}>List</button>
<button onClick={handleClick('searchedList')}>Searched List</button>
</div>
{type === 'list' ? ListComponent : SearchedListComponent}
</section>
);
}
Дальше как пропсы в этот компонент мы можем передать как клиентский компонент, так и серверный:
<Switcher
// серверный компонент
ListComponent={<List />}
// клиентский компонент
SearchedListComponent={<SearchedList />}
/>
🔹 При динамической подгрузке серверные компоненты передаются на клиент не в виде HTML, а в специальном формате
Вот как примерно выглядят пришедшие в браузер данные для новой страницы /about в нашем приложении на Next.js:
5:I["(app-pages-browser)/./node_modules/next/dist/client/link.js",...
6:I["(app-pages-browser)/...
7:I["(app-pages-browser)/...
2:{"name":"","env":"Server","owner":null}
1:D"$2"
4:{"name":"About","env":"Server","owner":null}
3:D"$4"
3:["$","div",null,{"className":"page_page__bHvK0","children":...
...
Из этой структуры React на клиенте сможет построить DOM-дерево, но при этом не будет производить процесса гидратации, так как серверные компоненты не обладают динамикой.
📂 Подробнее примеры можно посмотреть в нашем репозитории на GitHub.
А в следующей части поговорим про Server Actions.
👍1
Server Components и Server Actions. Часть 2.
В предыдущем посте мы поговорили про Server Components, а сегодня рассмотрим Server Actions.
Ранее, когда мы рассказывали про новые хуки React, мы уже посмотрели на примерах, как можно работать с серверными экшенами в сочетании с хуком useActionForm (вот здесь можно почитать подробнее).
Но на самом деле нам не обязательно использовать новые хуки React или даже серверные компоненты для работы с серверными экшенами.
Посмотрим на пример простого компонента со списком товаров. Он также осуществляет поиск товаров по строке через внешнее API, в которое передается поисковый запрос:
Здесь search выглядит как обычная функция, но на самом деле это серверный экшен. Делает её таким директива 'use server'.
В чем отличия серверного экшена
🔹 Серверный экшен выполняется только на сервере.
В нашем примере вывод в консоль '[SearchedList] Server Action' будет выполнен на сервере. Если бы это была обычная функция, то этот вывод был бы в консоли браузера.
🔹 Общение между клиентом и сервером для серверных экшенов происходит посредством специальных API-зпросов.
При клике на кнопку Search в браузере будет происходить POST-запрос на тот же URL, на котором мы находимся (локально это http://localhost:3000/).
В ответ придут данные в специальном формате:
Таким образом нам не нужно добавлять новый эндпоинт в API, в коде мы получаем эти данные, как если бы мы просто вызвали какую-то функцию. При этом внутри функции search вместо запроса к внешнему API может быть запрос на чтение из файла или напрямую из базы данных.
Кроме этого мы можем передать promise из серверного экшена как пропс в наш клиентский компонент:
И дальше использовать новый метод use для того, чтобы дождаться результат его выполнения и применить как initialValue в хуке useState:
📂 Подробнее примеры можно посмотреть в нашем репозитории на GitHub.
Я считаю, что появление Server Components и Server Actions приближает ту эпоху, когда фронтендеру нужно уметь работать не только в среде браузера, но и быть немного бэкендером. Новые инструменты, с одной стороны, предоставляют много новых возможностей, в том числе и для оптимизации. Но как и со всеми новыми технологиями, нужно знать, как их готовить.
✍️ А как cчитаете вы - поделитесь в комментариях, что думаете о развитии React в сторону серверных компонетов и экшенов!
В предыдущем посте мы поговорили про Server Components, а сегодня рассмотрим Server Actions.
Ранее, когда мы рассказывали про новые хуки React, мы уже посмотрели на примерах, как можно работать с серверными экшенами в сочетании с хуком useActionForm (вот здесь можно почитать подробнее).
Но на самом деле нам не обязательно использовать новые хуки React или даже серверные компоненты для работы с серверными экшенами.
Посмотрим на пример простого компонента со списком товаров. Он также осуществляет поиск товаров по строке через внешнее API, в которое передается поисковый запрос:
'use client';
import { useState } from 'react';
import { search } from './searchAction';
export function SearchedList({ fetchInitialData }: Props) {
const [searchText, setSearchText] = useState('');
const [data, setData] = useState<SearchResponce | null>(null);
const handleChangeSearch = (event) => setSearchText(event.target.value);
const handleClick = async () => {
const result = await search(searchText);
setData(result);
};
return (
<section>
<div>
<input name="search" onChange={handleChangeSearch} value={searchText} />
<button type="submit" onClick={handleClick}>Search</button>
</div>
{data && (
<ul>
{data.products.map((product) => (
...
))}
</ul>
)}
</section>
);
}
Здесь search выглядит как обычная функция, но на самом деле это серверный экшен. Делает её таким директива 'use server'.
'use server';
export const search = async (query: string) => {
console.log('[SearchedList] Server Action');
const data = (await fetch(`https://dummyjson.com/products/search?limit=10&q=${query}`).then((res) => res.json()));
return data;
};
В чем отличия серверного экшена
🔹 Серверный экшен выполняется только на сервере.
В нашем примере вывод в консоль '[SearchedList] Server Action' будет выполнен на сервере. Если бы это была обычная функция, то этот вывод был бы в консоли браузера.
🔹 Общение между клиентом и сервером для серверных экшенов происходит посредством специальных API-зпросов.
При клике на кнопку Search в браузере будет происходить POST-запрос на тот же URL, на котором мы находимся (локально это http://localhost:3000/).
В ответ придут данные в специальном формате:
0:["$@1",["development",null]]
1:{"products":[{"id":101,"title":"Apple AirPods Max Silver","description":"The...
Таким образом нам не нужно добавлять новый эндпоинт в API, в коде мы получаем эти данные, как если бы мы просто вызвали какую-то функцию. При этом внутри функции search вместо запроса к внешнему API может быть запрос на чтение из файла или напрямую из базы данных.
Кроме этого мы можем передать promise из серверного экшена как пропс в наш клиентский компонент:
import { search } from './searchAction';
export function SearchedList() {
const fetchInitialData = search('');
return (
<Suspense fallback="Click Me Load More...">
<SearchedListBase fetchInitialData={fetchInitialData} />
</Suspense>
);
}
И дальше использовать новый метод use для того, чтобы дождаться результат его выполнения и применить как initialValue в хуке useState:
type Props = {
fetchInitialData: Promise<SearchResponce>;
};
export function SearchedList({ fetchInitialData }: Props) {
const initialData = use(fetchInitialData);
const [data, setData] = useState<SearchResponce>(initialData);
...
}
📂 Подробнее примеры можно посмотреть в нашем репозитории на GitHub.
Я считаю, что появление Server Components и Server Actions приближает ту эпоху, когда фронтендеру нужно уметь работать не только в среде браузера, но и быть немного бэкендером. Новые инструменты, с одной стороны, предоставляют много новых возможностей, в том числе и для оптимизации. Но как и со всеми новыми технологиями, нужно знать, как их готовить.
✍️ А как cчитаете вы - поделитесь в комментариях, что думаете о развитии React в сторону серверных компонетов и экшенов!
🤝🏻 Двусторонняя клиент-серверная связь, часть 3: Server-Sent Events
Недавно я уже рассказывала о двух методах для обеспечения двусторонней связи между клиентом и сервером – Long Polling и WebSockets. Сегодня хочется рассмотреть ещё одну технологию, которая может помочь в организации такой связи - механизм Server-Sent Events (SSE).
👉🏻 SSE — это технология, позволяющая серверам инициировать отправку данных клиентам, подписанным на эти уведомления. Основное применение SSE – отправка обновлений в режиме реального времени от сервера к клиенту через стандартный HTTP-протокол. Как можно заметить, это не совсем двусторонняя, а скорее односторонняя связь, но она в том числе позволяет обмениваться данными между разными клиентами в реальном времени.
Как это работает:
Клиент создаёт объект
Сервер обрабатывает запрос, устанавливает необходимые заголовки ответа (самый важный из которых –
Клиент в это время подписывается на события и обрабатывает их привычным способом:
⚡️ Вот и всё! Снова всё предельно просто. Пример реализации этого подхода для уже знакомого нам чата можно найти в нашем репозитрии. Здесь как раз используется подход SSE для обеспечения двусторонней связи, так как сервер инициирует отправку данных клиентам через SSE в ответ на получение сообщения от клиентов. А также можно потрогать Live Demo, которое мы сделали и для этой технологии.
Итого, это достаточно несложная в реализации технология, которая работает поверх знакомого всем HTTP, поддерживается во многих браузерах, имеет встроенный механизм переподключения и свой специфичный, но при этом достаточно простой, формат передачи данных.
Из минусов, это все-таки односторонняя коммуникация с ограниченным форматом данных. Но для многих несложных задач этот механизм может прекрасно подойти. Подробнее о нём и о некоторых нюансах как всегда можно почитать на MDN.
Недавно я уже рассказывала о двух методах для обеспечения двусторонней связи между клиентом и сервером – Long Polling и WebSockets. Сегодня хочется рассмотреть ещё одну технологию, которая может помочь в организации такой связи - механизм Server-Sent Events (SSE).
👉🏻 SSE — это технология, позволяющая серверам инициировать отправку данных клиентам, подписанным на эти уведомления. Основное применение SSE – отправка обновлений в режиме реального времени от сервера к клиенту через стандартный HTTP-протокол. Как можно заметить, это не совсем двусторонняя, а скорее односторонняя связь, но она в том числе позволяет обмениваться данными между разными клиентами в реальном времени.
Как это работает:
Клиент создаёт объект
EventSource, указывая URL сервера, от которого будет получать обновления:
const eventSource = new EventSource('http://example.com/sse');
Сервер обрабатывает запрос, устанавливает необходимые заголовки ответа (самый важный из которых –
Content-Type: text/event-stream), и начинает передавать сообщения в формате, специфичном для SSE. Примеры:
data: Сообщение №1
retry: 2000
--
data: Сообщение №2
id: 2
--
event: specificevent
data: Сообщение с пользовательским типом события
Клиент в это время подписывается на события и обрабатывает их привычным способом:
eventSource.onopen = function(event) {
console.log('Соединение установлено');
};
eventSource.onmessage = function(event) {
console.log('Новое сообщение:', event.data);
};
eventSource.addEventListener('specificevent', function(event) {
console.log('Сообщение с пользовательским типом:', event.data);
});
⚡️ Вот и всё! Снова всё предельно просто. Пример реализации этого подхода для уже знакомого нам чата можно найти в нашем репозитрии. Здесь как раз используется подход SSE для обеспечения двусторонней связи, так как сервер инициирует отправку данных клиентам через SSE в ответ на получение сообщения от клиентов. А также можно потрогать Live Demo, которое мы сделали и для этой технологии.
Итого, это достаточно несложная в реализации технология, которая работает поверх знакомого всем HTTP, поддерживается во многих браузерах, имеет встроенный механизм переподключения и свой специфичный, но при этом достаточно простой, формат передачи данных.
Из минусов, это все-таки односторонняя коммуникация с ограниченным форматом данных. Но для многих несложных задач этот механизм может прекрасно подойти. Подробнее о нём и о некоторых нюансах как всегда можно почитать на MDN.
👍1
1️⃣ Социальная сеть (2010)
Фильм рассказывает об истории создания Facebook Марком Цукербергом. В нём показано, как идея, начатая в студенческом общежитии, может изменить мир, а также эта история подчеркивает важность инноваций и предпринимательской смекалки.
2️⃣ Джобс: Империя соблазна (2013)
Этот фильм сосредоточен на восходящем пути Стива Джобса, от студента, который сбросил обучение, до одного из самых известных предпринимателей в области технологий. Фильм также подчеркивает, как визионерское мышление и инновации могут изменять технологии и культуру.
3️⃣ Игра в имитацию (2014)
Основанный на реальных событиях, фильм рассказывает о жизни Алана Тьюринга, который во время Второй мировой войны внес существенный вклад в разработку компьютерных технологий, расшифровав шифры немецких машин Enigma. Фильм показывает, как технологические достижения могут иметь значительное историческое значение.
4️⃣ Хакеры (1995)
Фильм о группе молодых хакеров, сталкивающихся с корпоративным заговором. Он показывает, как молодые, умные и креативные люди могут использовать свои навыки для борьбы за справедливость.
5️⃣ Стартап (сериал, 2016-2018)
Хотя это и сериал, а не фильм, он заслуживает упоминания за изображение тяжелых испытаний, через которые проходят стартапы. Это напоминает о значении упорства и инноваций в индустрии стартапов, которая славится своей конкуренцией.
А есть ли у вас любимый фильм про IT? Делитесь в комментариях 👇
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
🖇 IntersectionObserver – что он пересекает и кого обозревает?
IntersectionObserver в JavaScript – это API, с помощью которого можно подписаться на изменение видимости определённых элементов страницы относительно их родительских элементов или глобальной области просмотра (viewport). Таким образом, можно асинхронно отслеживать появление или исчезновение тех или иных элементов документа.
💡 Для чего он может быть полезен?
1. Ленивая загрузка (lazy loading) изображений и видео: загрузка медиа-контента только тогда, когда пользователь прокручивает страницу, и контент должен скоро появиться в области видимости.
2. “Бесконечный” скролл: динамическая подгрузка данных при достижении конца страницы, чтобы улучшить производительность при работе с большими объёмами данных.
3. Отслеживание взаимодействия с рекламой: определение, просматривал ли пользователь рекламные блоки достаточное время для учета показов.
4. Анимации: активация анимации только тогда, когда элемент становится видимым на экране.
📝 Рассмотрим пример применения IntersectionObserver для реализации бесконечного скролла. Пусть на странице есть 2 элемента:
И мы хотим загружать новые данные, когда пользователь промотает страницу до конца контейнера. Тогда JavaScript-код будет выглядеть следующим образом:
Таким образом, мы подписываемся на невидимый элемент, который находится в самом низу нашего списка данных. Как только этот элемент появится в области видимости (то есть выполнится условие
🔗 В итоге мы имеем, что IntersectionObserver – это мощный инструмент для оптимизации производительности и улучшения пользовательского интерфейса на веб-страницах. Подробнее о нём и его возможностях можно почитать в официальной документации на сайте MDN.
IntersectionObserver в JavaScript – это API, с помощью которого можно подписаться на изменение видимости определённых элементов страницы относительно их родительских элементов или глобальной области просмотра (viewport). Таким образом, можно асинхронно отслеживать появление или исчезновение тех или иных элементов документа.
💡 Для чего он может быть полезен?
1. Ленивая загрузка (lazy loading) изображений и видео: загрузка медиа-контента только тогда, когда пользователь прокручивает страницу, и контент должен скоро появиться в области видимости.
2. “Бесконечный” скролл: динамическая подгрузка данных при достижении конца страницы, чтобы улучшить производительность при работе с большими объёмами данных.
3. Отслеживание взаимодействия с рекламой: определение, просматривал ли пользователь рекламные блоки достаточное время для учета показов.
4. Анимации: активация анимации только тогда, когда элемент становится видимым на экране.
📝 Рассмотрим пример применения IntersectionObserver для реализации бесконечного скролла. Пусть на странице есть 2 элемента:
<div id="container">
<!-- Сюда динамически добавляем данные -->
</div>
<div id="sentinel"></div> <!-- Элемент, за которым мы наблюдаем -->
И мы хотим загружать новые данные, когда пользователь промотает страницу до конца контейнера. Тогда JavaScript-код будет выглядеть следующим образом:
document.addEventListener("DOMContentLoaded", function() {
const container = document.getElementById('container');
const sentinel = document.getElementById('sentinel');
// Обработчик загрузки и добавления новых данных
const loadItems = () => { /* ... */ };
// Обработчик измненения видимости элемента
const handler = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadItems();
}
});
}
// Создаем объект IntersectionObserver с колбэком
const observer = new IntersectionObserver(handler);
// Подписываемся на наш элемент
observer.observe(sentinel);
// Загружаем первую порцию данных
loadItems();
});
Таким образом, мы подписываемся на невидимый элемент, который находится в самом низу нашего списка данных. Как только этот элемент появится в области видимости (то есть выполнится условие
entry.isIntersecting), будет инициирована загрузка новых данных, после чего элемент снова окажется внизу страницы. Также в реализации в реальной жизни стоит учесть, что элементы могут закончиться, и не инициировать лишние загрузки при каждом появлении отслеживаемого элемента в области видимости.🔗 В итоге мы имеем, что IntersectionObserver – это мощный инструмент для оптимизации производительности и улучшения пользовательского интерфейса на веб-страницах. Подробнее о нём и его возможностях можно почитать в официальной документации на сайте MDN.
📏 Prettier, ESLint и Stylelint: в чем разница?
Если вы фронтенд-разработчик, то, скорее всего, сталкивались с инструментами Prettier и ESLint. Оба они широко используются в мире JavaScript для улучшения качества кода, часто встречаются вместе на одном проекте, однако выполняют разные функции. И сегодня хочется разобраться, чем они отличаются и как каждый из них может быть полезен. А также обсудим еще один важный инструмент, который часто игнорируют в этой теме — Stylelint.
👉 Prettier — это инструмент для автоматического форматирования кода. Его основная задача — обеспечить консистентный стиль кода по всему проекту. Вы можете настроить Prettier так, чтобы он автоматически форматировал ваш код при сохранении файла или при коммите, что позволяет избежать разношерстности стиля в проекте и сосредоточиться на функциональности.
👉 ESLint — это анализатор кода (линтер), который проверяет ваш код на наличие потенциальных ошибок и плохих практик. ESLint позволяет настроить множество правил на проекте, чтобы обеспечить соблюдение различных стандартов кода и предотвращать ошибки.
👉 Stylelint — это инструмент для линтинга стилей, который проверяет ваш CSS (а также Sass, Less и другие варианты CSS-препроцессоров), чтобы обеспечить соблюдение заданных вами правил и предотвратить ошибки. Stylelint очень полезен для поддержания консистентности и качества ваших стилей.
В чем разница?
- Цель:
- Prettier фокусируется на автоматическом форматировании кода.
- ESLint ориентирован на проверку качества кода и обнаружение ошибок в JavaScript и TypeScript.
- Stylelint занимается линтингом и проверкой CSS-стилей.
- Форматирование против линтинга:
- Prettier просто форматирует код.
- ESLint более глубоко анализирует код и сообщает о проблемах, которые нужно исправить вручную.
- Настройка и правила:
- Prettier практически не требует настроек, он просто работает "из коробки".
- ESLint и Stylelint требуют бОльшей настройки для определения, какие ошибки и правила должны быть проверены, однако позволяют расширять свой функционал через множество плагинов. Например, один из таких интересных плагинов — это eslint-plugin-react-compiler, плагин для React, который помогает улучшить качество кода, показывая анализ нового компилятора React Compiler прямо в IDE (подробнее писали тут).
Prettier, ESLint и Stylelint не являются взаимозаменяемыми инструментами, но они дополняют друг друга. Используя все три инструмента в вашем проекте, вы можете гарантировать, что ваш код будет не только правильно отформатирован, но и лишен потенциальных ошибок, анти-паттернов, а также будет содержать качественно написанные стили.
👍 Ставьте реакции, если интересно узнать подробнее о настройке Prettier, ESLint и Stylelint в проекте с нуля!
Если вы фронтенд-разработчик, то, скорее всего, сталкивались с инструментами Prettier и ESLint. Оба они широко используются в мире JavaScript для улучшения качества кода, часто встречаются вместе на одном проекте, однако выполняют разные функции. И сегодня хочется разобраться, чем они отличаются и как каждый из них может быть полезен. А также обсудим еще один важный инструмент, который часто игнорируют в этой теме — Stylelint.
👉 Prettier — это инструмент для автоматического форматирования кода. Его основная задача — обеспечить консистентный стиль кода по всему проекту. Вы можете настроить Prettier так, чтобы он автоматически форматировал ваш код при сохранении файла или при коммите, что позволяет избежать разношерстности стиля в проекте и сосредоточиться на функциональности.
👉 ESLint — это анализатор кода (линтер), который проверяет ваш код на наличие потенциальных ошибок и плохих практик. ESLint позволяет настроить множество правил на проекте, чтобы обеспечить соблюдение различных стандартов кода и предотвращать ошибки.
👉 Stylelint — это инструмент для линтинга стилей, который проверяет ваш CSS (а также Sass, Less и другие варианты CSS-препроцессоров), чтобы обеспечить соблюдение заданных вами правил и предотвратить ошибки. Stylelint очень полезен для поддержания консистентности и качества ваших стилей.
В чем разница?
- Цель:
- Prettier фокусируется на автоматическом форматировании кода.
- ESLint ориентирован на проверку качества кода и обнаружение ошибок в JavaScript и TypeScript.
- Stylelint занимается линтингом и проверкой CSS-стилей.
- Форматирование против линтинга:
- Prettier просто форматирует код.
- ESLint более глубоко анализирует код и сообщает о проблемах, которые нужно исправить вручную.
- Настройка и правила:
- Prettier практически не требует настроек, он просто работает "из коробки".
- ESLint и Stylelint требуют бОльшей настройки для определения, какие ошибки и правила должны быть проверены, однако позволяют расширять свой функционал через множество плагинов. Например, один из таких интересных плагинов — это eslint-plugin-react-compiler, плагин для React, который помогает улучшить качество кода, показывая анализ нового компилятора React Compiler прямо в IDE (подробнее писали тут).
Prettier, ESLint и Stylelint не являются взаимозаменяемыми инструментами, но они дополняют друг друга. Используя все три инструмента в вашем проекте, вы можете гарантировать, что ваш код будет не только правильно отформатирован, но и лишен потенциальных ошибок, анти-паттернов, а также будет содержать качественно написанные стили.
👍 Ставьте реакции, если интересно узнать подробнее о настройке Prettier, ESLint и Stylelint в проекте с нуля!
👍4
📢 Сегодня обсудим одну из опасных веб-атак - CSRF (Cross-Site Request Forgery).
CSRF, или Cross-Site Request Forgery, это вид атаки на веб-приложения, при котором злоумышленник обманывает пользователя и заставляет его выполнить нежелательные действия на сайте, на котором он уже аутентифицирован (например, совершить покупку, изменить настройки аккаунта и т.д.).
Как это работает?
1. Пользователь аутентифицируется на доверенном сайте (например, на сайте банка).
2. Затем он попадает на вредоносный сайт, созданный злоумышленником.
3. Вредоносный сайт отправляет скрытый запрос к доверенному сайту, используя аутентификационные данные (куки, сессионные данные и т.д.).
4. Доверенный сайт выполняет этот запрос, считая его легитимным.
Эта атака возможна благодаря тому, что браузерные запросы автоматически включают в себя все куки, включая сессионные.
Примеры атак:
- Перевод денег: Пользователь заходит на фальшивый сайт с вредоносным кодом, который делает запрос на перевод денег со счета пользователя на счет злоумышленника.
- Смена электронной почты: Вредоносный сайт может отправить запрос на изменение контактного адреса электронной почты пользователя на сервере легитимного сайта.
Для защиты от CSRF-атак разработчику следует:
1. Использовать встроенную защиту от CSRF в вашем фреймворке, если она есть.
2. Добавлять CSRF-токены ко всем запросам, изменяющим состояние, и проверять их на сервере.
3. Использовать атрибут SameSite Cookie для сессионных куки.
4. Проверять происхождение запроса с использованием стандартных заголовков: origin и referer.
5. НЕ использовать GET-запросы для операций, изменяющих состояние. Если используете, защитите эти ресурсы от CSRF.
⚠️ Важно помнить, что XSS-уязвимости могут обойти все методы защиты от CSRF!
Подробнее про методы защиты можно почитать здесь
CSRF, или Cross-Site Request Forgery, это вид атаки на веб-приложения, при котором злоумышленник обманывает пользователя и заставляет его выполнить нежелательные действия на сайте, на котором он уже аутентифицирован (например, совершить покупку, изменить настройки аккаунта и т.д.).
Как это работает?
1. Пользователь аутентифицируется на доверенном сайте (например, на сайте банка).
2. Затем он попадает на вредоносный сайт, созданный злоумышленником.
3. Вредоносный сайт отправляет скрытый запрос к доверенному сайту, используя аутентификационные данные (куки, сессионные данные и т.д.).
4. Доверенный сайт выполняет этот запрос, считая его легитимным.
Эта атака возможна благодаря тому, что браузерные запросы автоматически включают в себя все куки, включая сессионные.
Примеры атак:
- Перевод денег: Пользователь заходит на фальшивый сайт с вредоносным кодом, который делает запрос на перевод денег со счета пользователя на счет злоумышленника.
- Смена электронной почты: Вредоносный сайт может отправить запрос на изменение контактного адреса электронной почты пользователя на сервере легитимного сайта.
Для защиты от CSRF-атак разработчику следует:
1. Использовать встроенную защиту от CSRF в вашем фреймворке, если она есть.
2. Добавлять CSRF-токены ко всем запросам, изменяющим состояние, и проверять их на сервере.
3. Использовать атрибут SameSite Cookie для сессионных куки.
4. Проверять происхождение запроса с использованием стандартных заголовков: origin и referer.
5. НЕ использовать GET-запросы для операций, изменяющих состояние. Если используете, защитите эти ресурсы от CSRF.
⚠️ Важно помнить, что XSS-уязвимости могут обойти все методы защиты от CSRF!
Подробнее про методы защиты можно почитать здесь
🌟 Простой способ сделать красивый скролл!
Вы можете использовать два CSS-свойства - scrollbar-width и scrollbar-color, которые поддерживаются всеми браузерами, кроме Safari. А для того, чтобы поддержать стилизацию и в нём, можно использовать псевдоклассы с вендорным префиксом ::-webkit-scrollbar.
Вот так быстро можно сделать скролл красивым, используя лишь чистый CSS!
Вы можете использовать два CSS-свойства - scrollbar-width и scrollbar-color, которые поддерживаются всеми браузерами, кроме Safari. А для того, чтобы поддержать стилизацию и в нём, можно использовать псевдоклассы с вендорным префиксом ::-webkit-scrollbar.
Вот так быстро можно сделать скролл красивым, используя лишь чистый CSS!
Как и обещали, возвращаемся к вам с разбором задачи с LeetCode!)
Ставьте лайки этому видео и пишите в комментариях, какие еще задачи разобрать 💻
https://youtu.be/c44ISTawL1c
Ставьте лайки этому видео и пишите в комментариях, какие еще задачи разобрать 💻
https://youtu.be/c44ISTawL1c
YouTube
1 LeetCode: Two Sum - подробный разбор на javascript
1. Two Sum - Разбор задач для собеседований
📌 https://leetcode.com/problems/two-sum
Всем привет! В этом видео мы разберем популярную задачу с LeetCode под названием "Two Sum". Эта задача часто встречается на собеседованиях по программированию, и понимание…
📌 https://leetcode.com/problems/two-sum
Всем привет! В этом видео мы разберем популярную задачу с LeetCode под названием "Two Sum". Эта задача часто встречается на собеседованиях по программированию, и понимание…
📚 Начало рабочей недели - самое время, чтобы продолжить разговор о линтерах!
Сегодня мы рассмотрим, как настроить с нуля в своем проекте ESLint, Prettier, Stylelint, а также Husky и lint-staged.
P.S. Мы пробуем новый формат статей в Telegraph. Если вам он понравится, поддержите этот пост реакциями!)
Сегодня мы рассмотрим, как настроить с нуля в своем проекте ESLint, Prettier, Stylelint, а также Husky и lint-staged.
P.S. Мы пробуем новый формат статей в Telegraph. Если вам он понравится, поддержите этот пост реакциями!)
Telegraph
Как настроить Prettier, ESLint, Stylelint и Husky в проекте с нуля
В одном из прошлых постов мы говорили о таких инструментах, как Prettier, ESLint и Stylelint, и чем они отличаются. А сегодня мы попробуем поэтапно настроить все три библиотеки на нашем проекте, а также настроить прекоммитную проверку с помощью Husky и lint…
👍4
📌 Полезное свойство в JSDoc, особенно при рефакторинге! Помогает отмечать функции и константы как
В комментариях также полезно указывать альтернативу старому решению.
IDE автоматически будет зачеркивать такие функции в коде и отображать специальную подсказку.
deprecated, то есть устаревшими и не рекомендованными к использованию.В комментариях также полезно указывать альтернативу старому решению.
IDE автоматически будет зачеркивать такие функции в коде и отображать специальную подсказку.
