<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>Pavel Osin</title><subtitle>Павел Осин.  Frontend-разработчик.  Заметки о веб-разработке, программировании и не только.
https://t.me/httpresponse418</subtitle><author><name>Pavel Osin</name></author><id>https://teletype.in/atom/osinpaul</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/osinpaul?offset=0"></link><link rel="alternate" type="text/html" href="https://osinpaul.ru/?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/osinpaul?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-08T22:23:16.979Z</updated><entry><id>osinpaul:_zOl28unbRz</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/_zOl28unbRz?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>Webstorm, как исключить из поиска папки</title><published>2025-12-09T09:05:12.909Z</published><updated>2025-12-09T09:05:46.467Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img3.teletype.in/files/a4/f4/a4f4666e-5b8e-487d-bab4-41e7dc8ca423.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/7a/fb/7afbce43-2dfb-4b03-b86b-ad837d14f48b.png&quot;&gt;На новом проекте WS активно искал по файлам, которые относятся к результатам билда nx-репозитория. Искал как решить, поэтому зафиксирую здесь.

Чтобы отключить поиск по файлам, которые Git игнорирует (например, в директории node_modules или dist), нужно настроить WebStorm, чтобы он не индексировал эти директории как часть проекта, а не отключать сам .gitignore (он нужен для Git), делая это через Settings/Preferences &gt; Directories, добавив папку как &quot;Excluded&quot; (Исключенная). Это предотвратит их участие в поиске, рефакторинге и других операциях IDE, сохраняя .gitignore для Git. 

Шаги для исключения папки в WebStorm:</summary><content type="html">
  &lt;p id=&quot;rz74&quot;&gt;На новом проекте WS активно искал по файлам, которые относятся к результатам билда nx-репозитория. Искал как решить, поэтому зафиксирую здесь.&lt;br /&gt;&lt;br /&gt;Чтобы отключить поиск по файлам, которые Git игнорирует (например, в директории node_modules или dist), нужно настроить WebStorm, чтобы он не индексировал эти директории как часть проекта, а не отключать сам .gitignore (он нужен для Git), делая это через Settings/Preferences &amp;gt; Directories, добавив папку как &amp;quot;Excluded&amp;quot; (Исключенная). Это предотвратит их участие в поиске, рефакторинге и других операциях IDE, сохраняя .gitignore для Git. &lt;br /&gt;&lt;br /&gt;Шаги для исключения папки в WebStorm:&lt;/p&gt;
  &lt;pre id=&quot;Chdn&quot;&gt;
File (Файл) -&amp;gt; 
Settings (Настройки) -&amp;gt; 
Preferences (Предпочтения)&lt;/pre&gt;
  &lt;p id=&quot;dnUr&quot;&gt;&lt;br /&gt;- В левой панели кликаем по Project Structure (Структура проекта).&lt;br /&gt;- Находим папку, которую хотите игнорировать (например, node_modules, dist, build), клик по ней правой кнопкой мыши и пометить как Excluded (Исключенная).&lt;br /&gt;&lt;/p&gt;
  &lt;figure id=&quot;IzBm&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/7a/fb/7afbce43-2dfb-4b03-b86b-ad837d14f48b.png&quot; width=&quot;2692&quot; /&gt;
  &lt;/figure&gt;

</content></entry><entry><id>osinpaul:hbKeVmZbl13</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/hbKeVmZbl13?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>Centi Conf. Frontend Day</title><published>2025-12-08T20:11:17.110Z</published><updated>2025-12-08T20:11:17.110Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/52/0a/520a642c-c990-405a-bd89-8d30ccf2cefd.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://img4.teletype.in/files/77/a6/77a6c19c-7665-40d4-9b3c-274ef9078147.jpeg&quot;&gt;Посетил конференцию по разработке на Angular от Centicore. Один поток, шесть докладов. Рассказывали о новом и хорошо забытом старом во фронтенде.</summary><content type="html">
  &lt;p id=&quot;11VD&quot;&gt;Посетил конференцию по разработке на Angular от Centicore. Один поток, шесть докладов. Рассказывали о новом и хорошо забытом старом во фронтенде.&lt;/p&gt;
  &lt;figure id=&quot;GNpI&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/77/a6/77a6c19c-7665-40d4-9b3c-274ef9078147.jpeg&quot; width=&quot;2268&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;erOR&quot;&gt;Отмечу, что запомнилось и сухой текст разбавлю парой фоточек. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;1) Computer Science во фронтенде&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;AshM&quot;&gt;Иван Черняков рассказал на практических примерах, что фронтенд требует не только владения фреймворком, но и понимания алгоритмов, структур данных, сложности операций. &lt;/p&gt;
  &lt;p id=&quot;Qg1b&quot;&gt;Что интересно освежил:&lt;/p&gt;
  &lt;ul id=&quot;TMLY&quot;&gt;
    &lt;li id=&quot;vzPx&quot;&gt;Битовые маски&lt;/li&gt;
  &lt;/ul&gt;
  &lt;blockquote id=&quot;H45t&quot;&gt;Используются для хранения множества булевых признаков &lt;strong&gt;в одном числе&lt;/strong&gt;: &lt;br /&gt;- экономия памяти &lt;br /&gt;- быстрые проверки (&lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt;)&lt;br /&gt;- удобно для наборов флагов (права доступа, возможности, состояния)&lt;br /&gt;&lt;br /&gt;Примерно так же работают feature flags — только привычнее, когда флаги строковые, а не побитовые.&lt;/blockquote&gt;
  &lt;ul id=&quot;r3oe&quot;&gt;
    &lt;li id=&quot;pSIs&quot;&gt;Двунаправленные связные списки&lt;/li&gt;
  &lt;/ul&gt;
  &lt;blockquote id=&quot;5jt0&quot;&gt;Актуальны там, где: &lt;br /&gt;- много &lt;strong&gt;вставок/удалений в середине коллекции &lt;br /&gt;- &lt;/strong&gt;порядок элементов важен и часто меняется &lt;br /&gt;&lt;br /&gt;В JavaScript списки есть только абстрактно (&lt;code&gt;Array&lt;/code&gt; ≠ linked list), поэтому, когда нужен реальный O(1) на вставку — без собственной структуры не обойтись. Полезно помнить, что &lt;strong&gt;не всё должно быть массивом&lt;/strong&gt;, если требуется производительность.&lt;/blockquote&gt;
  &lt;hr /&gt;
  &lt;ul id=&quot;ujPe&quot;&gt;
    &lt;li id=&quot;HC91&quot;&gt;TypeScript Transformers&lt;/li&gt;
  &lt;/ul&gt;
  &lt;blockquote id=&quot;oe0a&quot;&gt;Это &lt;strong&gt;вмешательство в процесс компиляции&lt;/strong&gt; TypeScript: &lt;br /&gt;- переписывание типов и кода &lt;br /&gt;- автоматическое добавление полей, проверок, метаданных&lt;br /&gt;- снижение дублирования &lt;br /&gt;Использование трансформеров позволяет: формализовать архитектурные правила генерировать код там, где он предсказуем и однообразен&lt;/blockquote&gt;
  &lt;p id=&quot;aMjR&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;gt2s&quot;&gt;Вывод простой: инженерный подход повышает оптимальность и масштабируемость приложений. Доклад полезен как напоминание: фронтенд — это не только фреймворки. Хорошая мотивация скинуть пыль с алгоритмических знаний.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;2) Сигналы в Angular&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;0i8J&quot;&gt;Олег Щеголев рассказал, что нового принесли сигналы в Angular. При сухом чтении документации не возникает желания их использовать, особенно когда давно пишешь на RxJS. На примерах же становится сильно понятнее, что сигналы позволяют экономить ресурсы, перерендеры и открывают дорогу в прекрасное будущее без Zone.js :)&lt;/p&gt;
  &lt;p id=&quot;MbYz&quot;&gt;Заметки:&lt;/p&gt;
  &lt;ul id=&quot;iTnc&quot;&gt;
    &lt;li id=&quot;5AGr&quot;&gt;&lt;strong&gt;&lt;code&gt;Resource&lt;/code&gt;&lt;/strong&gt; для асинхронных данных&lt;/li&gt;
    &lt;li id=&quot;5l8z&quot;&gt;debounce для сигналов через &lt;code&gt;toSignal&lt;/code&gt; → уже можно использовать&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;JHLs&quot;&gt;и закончу докладом Глеба Михеева. &lt;/p&gt;
  &lt;figure id=&quot;48pI&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/10/2a/102a3a55-e1d3-40ed-8ab0-b1fb6d861906.jpeg&quot; width=&quot;2268&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;8jK5&quot;&gt;3) Генеративный пользовательский интерфейс&lt;/h3&gt;
  &lt;p id=&quot;0cls&quot;&gt;Доклад о будущем, которое скоро наступит. А возможно уже наступило.&lt;br /&gt;Пока мы по-старинке верстаем компоненты и пишем логику систем, все вокруг бежит так быстро, что ИИ делает просто нереальными попытки остающих зацепиться за реальность. Завернул. &lt;/p&gt;
  &lt;p id=&quot;nN1c&quot;&gt;Доклад о формировании интерфейса на основе данных и контекста. Основная идея — UI может не быть жёстко заданным, а строится динамически. Технически звучит перспективно для систем с большим количеством вариативных экранов и пользовательских сценариев.&lt;/p&gt;
  &lt;p id=&quot;sgZP&quot;&gt;Смысл подхода: UI не верстается вручную заранее, а формируется автоматически в момент использования — на основе данных, бизнес-правил и контекста.&lt;/p&gt;
  &lt;p id=&quot;WcJM&quot;&gt;В основе лежат БЯМ (большие языковые модели), которые обучаются на описанной логике, моделях данных, сценариях, ограничениях и данных о пользователе. Все остальное генерируется автоматически. Разработчик становится по-сути оператором нейросети, чтобы задавать правила построения UI, а не расположение кнопок и форм. Тут риторический вопрос, нужны-ли разработчики в этой цепочке, когда такой интерфейс может построиться бизнес-аналитиком.&lt;/p&gt;
  &lt;p id=&quot;2iFr&quot;&gt;Фактически для нас, как разработчиков смещается фокус.&lt;/p&gt;
  &lt;p id=&quot;HPCW&quot;&gt;Было:&lt;/p&gt;
  &lt;pre id=&quot;2yn5&quot;&gt;сверстать экран → внедрить бизнес-логику → править по запросу бизнеса&lt;/pre&gt;
  &lt;p id=&quot;kybh&quot;&gt;Становится:&lt;/p&gt;
  &lt;pre id=&quot;dY7o&quot;&gt;описать правила → настроить компоненты → обеспечить совместимость&lt;/pre&gt;
  &lt;p id=&quot;jMbw&quot;&gt;Пока это выглядит как направление развития интерфейсов корпоративного уровня, где стоимость изменений высока. Но попробовать хочется уже на новогодних.&lt;/p&gt;
  &lt;p id=&quot;PD2D&quot;&gt;&lt;br /&gt;Немного жалею, что торопился и не остался на афтерпати)&lt;/p&gt;

</content></entry><entry><id>osinpaul:tsalLqGq6TM</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/tsalLqGq6TM?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>performance() в браузере</title><published>2025-12-05T07:12:11.016Z</published><updated>2025-12-05T07:12:11.016Z</updated><summary type="html">Когда речь заходит о производительности веб-приложений, большинство разработчиков вспоминают Lighthouse, Web Vitals и профайлеры в DevTools. Но в современном JavaScript есть ещё один мощный инструмент, который часто недооценивают — Performance API. Это встроенный интерфейс браузера, который позволяет точно измерять время выполнения операций внутри веб-приложения: от загрузки страницы до конкретных участков кода. Он даёт доступ к высокоточным временным меткам (DOMHighResTimeStamp) и системам метрик.</summary><content type="html">
  &lt;p id=&quot;Luxa&quot;&gt;Когда речь заходит о производительности веб-приложений, большинство разработчиков вспоминают Lighthouse, Web Vitals и профайлеры в DevTools. Но в современном JavaScript есть ещё один мощный инструмент, который часто недооценивают — Performance API. Это встроенный интерфейс браузера, который позволяет точно измерять время выполнения операций внутри веб-приложения: от загрузки страницы до конкретных участков кода. Он даёт доступ к высокоточным временным меткам (DOMHighResTimeStamp) и системам метрик.&lt;/p&gt;
  &lt;p id=&quot;xHGq&quot;&gt;Я использовал его для отладки интервалов и таймеров. Надеюсь, ни для кого не секрет, что в js interval(1000) - это далеко не 1 секунда. &lt;/p&gt;
  &lt;p id=&quot;gtxe&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Lvni&quot;&gt;&lt;strong&gt;Например, задача&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;Qzl5&quot;&gt;Имеется временное отклонение t при работе таймеров в системе. Ожидаем, что таймер выполнится через 1000 мс, и проверяем реальное время:&lt;/p&gt;
  &lt;pre id=&quot;gJbS&quot; data-lang=&quot;typescript&quot;&gt;const interval = 1000;
let ticks = 0;
let last = performance.now();

const id = setInterval(() =&amp;gt; {
  ticks++;
  const now = performance.now();
  const drift = now - last - interval; 

  console.log(
    &amp;#x60;Тик #${ticks} — задержка: ${drift.toFixed(2)}ms, текущее время: ${(now - start).toFixed(2)}ms&amp;#x60;
  );

  last = now;

  if (ticks &amp;gt;= 5) {
    clearInterval(id);
    console.log(&amp;#x27;Интервал остановлен&amp;#x27;);
  }
}, interval);

const start = performance.now();
&lt;/pre&gt;
  &lt;p id=&quot;F2NI&quot;&gt;&lt;br /&gt;В реальности получил от 920мс до 1100. Иногда больше - иногда меньше. В зависимости от загруженности EventLoop или процессора ПК, активности вкладки и так далее.&lt;/p&gt;

</content></entry><entry><id>osinpaul:2voohx8AhF4</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/2voohx8AhF4?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>Шпаргалка по Conventional Commits</title><published>2025-11-29T11:11:55.060Z</published><updated>2025-11-29T11:11:55.060Z</updated><summary type="html">Система Conventional Commits помогает писать единообразные коммиты, которые легко читать, анализировать и автоматически использовать в релизах.</summary><content type="html">
  &lt;p id=&quot;FuDM&quot;&gt;Система &lt;strong&gt;Conventional Commits&lt;/strong&gt; помогает писать единообразные коммиты, которые легко читать, анализировать и автоматически использовать в релизах. &lt;br /&gt;&lt;/p&gt;
  &lt;p id=&quot;3u7i&quot;&gt;Собрал базовые примеры использования, чтобы документация была всегда под рукой.&lt;/p&gt;
  &lt;p id=&quot;kdoq&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;Ezxc&quot;&gt;&lt;strong&gt;Основная идея&lt;/strong&gt;&lt;/h2&gt;
  &lt;p id=&quot;ffjD&quot;&gt;Каждый коммит должен отвечать на три простых вопроса:&lt;/p&gt;
  &lt;ol id=&quot;Eg8e&quot;&gt;
    &lt;li id=&quot;DLNT&quot;&gt;&lt;strong&gt;Что изменили?&lt;/strong&gt; — тип (&lt;code&gt;type&lt;/code&gt;)&lt;/li&gt;
    &lt;li id=&quot;jW0y&quot;&gt;&lt;strong&gt;Где?&lt;/strong&gt; — область (&lt;code&gt;scope&lt;/code&gt;) — необязательно, но очень рекомендуется&lt;/li&gt;
    &lt;li id=&quot;xBXb&quot;&gt;&lt;strong&gt;Зачем и что именно?»&lt;/strong&gt; — короткое и понятное описание (&lt;code&gt;subject&lt;/code&gt;)&lt;/li&gt;
  &lt;/ol&gt;
  &lt;pre id=&quot;cywb&quot; data-lang=&quot;yaml&quot;&gt;&amp;lt;type&amp;gt;[optional scope]: &amp;lt;subject&amp;gt;
[optional body]
[optional footer]&lt;/pre&gt;
  &lt;p id=&quot;pjc1&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;lvaE&quot;&gt;1. Типы коммитов&lt;/h3&gt;
  &lt;ul id=&quot;NGTY&quot;&gt;
    &lt;li id=&quot;3qL5&quot;&gt;feat (новый функкционал).&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;n3T6&quot;&gt;feat(auth): добавить refresh token механизм&lt;/pre&gt;
  &lt;ul id=&quot;b6XL&quot;&gt;
    &lt;li id=&quot;zCq4&quot;&gt;fix (исправление ошибки, дефекта)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;wOGF&quot;&gt;fix(profile): корректно сохранять изменения аватара&lt;/pre&gt;
  &lt;ul id=&quot;hSlU&quot;&gt;
    &lt;li id=&quot;Omxn&quot;&gt;&lt;code&gt;docs (&lt;/code&gt;изменения в документации)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;f4ZL&quot;&gt;docs(readme): обновить раздел по установке проекта&lt;/pre&gt;
  &lt;ul id=&quot;hWT1&quot;&gt;
    &lt;li id=&quot;V33C&quot;&gt;&lt;code&gt;style&lt;/code&gt;(изменения, не влияющие на логику — форматирование, пробелы, точки с запятой.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;aNOK&quot;&gt;style(ui): выровнять отступы в модальном окне&lt;/pre&gt;
  &lt;ul id=&quot;pQ5l&quot;&gt;
    &lt;li id=&quot;xj3j&quot;&gt;&lt;code&gt;refactor (&lt;/code&gt;перереписывание кода без изменения поведения)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;LPJw&quot;&gt;refactor(api): упростить структуру сервисов&lt;/pre&gt;
  &lt;ul id=&quot;3Sbe&quot;&gt;
    &lt;li id=&quot;eZgm&quot;&gt;&lt;code&gt;perf&lt;/code&gt;(Оптимизация производительности)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;Qcob&quot;&gt;perf(table): ускорить рендер на 20%&lt;/pre&gt;
  &lt;ul id=&quot;4J7f&quot;&gt;
    &lt;li id=&quot;QxSm&quot;&gt;&lt;code&gt;test (&lt;/code&gt;добавление или исправление тестов)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;l7HR&quot;&gt;test(cart): покрыть логику пересчёта сумм&lt;/pre&gt;
  &lt;ul id=&quot;yCu2&quot;&gt;
    &lt;li id=&quot;5wwD&quot;&gt;&lt;code&gt;build&lt;/code&gt;(изменения в сборке, CI, зависимостях)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;slv4&quot;&gt;build: обновить Webpack до версии 5&lt;/pre&gt;
  &lt;ul id=&quot;60Kw&quot;&gt;
    &lt;li id=&quot;Zz0u&quot;&gt;&lt;code&gt;chore&lt;/code&gt;(Прочие изменения, не затрагивающие код приложения)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;GHSA&quot;&gt;chore: почистить скрипты в package.json&lt;/pre&gt;
  &lt;p id=&quot;AbxO&quot;&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;h3 id=&quot;4ssK&quot;&gt;2. Scope&lt;/h3&gt;
  &lt;p id=&quot;Cden&quot;&gt;Всегда, когда это помогает локализовать изменение. Хорошие примеры областей:&lt;/p&gt;
  &lt;ul id=&quot;32ja&quot;&gt;
    &lt;li id=&quot;ijY6&quot;&gt;&lt;code&gt;auth&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;klHe&quot;&gt;&lt;code&gt;live&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;WyH4&quot;&gt;&lt;code&gt;ui&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;Hdph&quot;&gt;&lt;code&gt;api&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;ydNz&quot;&gt;Некорректные примеры:&lt;/p&gt;
  &lt;ul id=&quot;zofn&quot;&gt;
    &lt;li id=&quot;hap3&quot;&gt;&lt;code&gt;misc&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;Ne6v&quot;&gt;&lt;code&gt;stuff&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;RaG3&quot;&gt;&lt;code&gt;temp&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;fAsr&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;cjKG&quot;&gt;3. Subject&lt;/h3&gt;
  &lt;p id=&quot;TxwY&quot;&gt;Короткий принцип:&lt;/p&gt;
  &lt;ul id=&quot;zz4h&quot;&gt;
    &lt;li id=&quot;5cCw&quot;&gt;Пишем в &lt;strong&gt;повелительном наклонении&lt;/strong&gt;, как будто даём команду коду.&lt;/li&gt;
    &lt;li id=&quot;L0Qr&quot;&gt;Не ставим точку в конце.&lt;/li&gt;
    &lt;li id=&quot;B8sA&quot;&gt;Максимум ясности, минимум слов.&lt;/li&gt;
    &lt;li id=&quot;SGes&quot;&gt;Если в команде принято соглашении о привязке задач к коммитам, можно указать номер задачи&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;qm2h&quot;&gt;Примеры хороших subject:&lt;/p&gt;
  &lt;ul id=&quot;DGjO&quot;&gt;
    &lt;li id=&quot;A6oX&quot;&gt;&lt;code&gt;перенести логику в сервис&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;i2SQ&quot;&gt;&lt;code&gt;добавить debounce при вводе&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;I0fn&quot;&gt;&lt;code&gt;исправить ошибку валидации номера&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;STK3&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;Gedv&quot;&gt;4. Body&lt;/h3&gt;
  &lt;p id=&quot;JFy1&quot;&gt;Используем только если нужно объяснение:&lt;/p&gt;
  &lt;ul id=&quot;qCAH&quot;&gt;
    &lt;li id=&quot;5DCZ&quot;&gt;почему так сделано;&lt;/li&gt;
    &lt;li id=&quot;gZIf&quot;&gt;какие альтернативы рассматривались;&lt;/li&gt;
    &lt;li id=&quot;pFjL&quot;&gt;есть ли побочные эффекты.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;pre id=&quot;zua5&quot;&gt;refactor(order): вынести расчет скидки в отдельный модуль
Чтобы упростить дальнейшую поддержку и подготовить почву для многоуровневой системы скидок.&lt;/pre&gt;
  &lt;p id=&quot;46nx&quot;&gt;&lt;br /&gt;&lt;/p&gt;
  &lt;h3 id=&quot;1LjW&quot;&gt;5. Footer&lt;/h3&gt;
  &lt;pre id=&quot;64GH&quot;&gt;Closes #123&lt;/pre&gt;
  &lt;h3 id=&quot;dvmN&quot;&gt;&lt;/h3&gt;
  &lt;p id=&quot;p6L9&quot;&gt;&lt;strong&gt;BREAKING CHANGE:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;sGo2&quot;&gt;Если коммит ломает обратную совместимость, обязательно указываем:&lt;/p&gt;
  &lt;pre id=&quot;DLkV&quot;&gt;feat(api): изменить структуру ответа метода /users

BREAKING CHANGE: метод возвращает объект вместо массива&lt;/pre&gt;

</content></entry><entry><id>osinpaul:A2Lxs5sghPx</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/A2Lxs5sghPx?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>Перенос файлов между ветками git</title><published>2025-11-24T08:57:53.077Z</published><updated>2025-11-24T09:00:40.856Z</updated><summary type="html">Иногда в Git нужно не переключаться полностью на другую ветку, а забрать из неё один или несколько конкретных файлов. Это существенно упрощает работу, особенно если нужно подтянуть изменения коллеги, а они разбиты по нескольким коммитам. Git позволяет сделать это в одну команду — git checkout (в новых версиях — git restore).</summary><content type="html">
  &lt;p id=&quot;VI8q&quot;&gt;Иногда в Git нужно не переключаться полностью на другую ветку, а забрать из неё &lt;strong&gt;один или несколько конкретных файлов. &lt;/strong&gt;Git позволяет сделать это в одну команду — &lt;strong&gt;git checkout&lt;/strong&gt; (в новых версиях — &lt;strong&gt;git restore&lt;/strong&gt;). Это существенно упрощает работу, особенно если нужно подтянуть изменения коллеги, а они разбиты по нескольким коммитам&lt;/p&gt;
  &lt;p id=&quot;rZUo&quot;&gt;Находясь в текущей ветке (например, develop):&lt;/p&gt;
  &lt;pre id=&quot;5SvV&quot;&gt;git checkout &amp;lt;имя-ветки&amp;gt; -- &amp;lt;путь/к/файлу&amp;gt;&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;p id=&quot;DtGF&quot;&gt;Пример&lt;/p&gt;
  &lt;p id=&quot;JaAg&quot;&gt;Мы хотим взять файл &lt;code&gt;config.yml&lt;/code&gt; из ветки &lt;code&gt;develop&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;SAgn&quot;&gt;git checkout origin/develop -- config.yml&lt;/pre&gt;
  &lt;p id=&quot;gkU7&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;eQfR&quot;&gt;После выполнения команда не переключит нас на ветку &lt;code&gt;develop&lt;/code&gt;, а просто заменит файл &lt;code&gt;config.yml&lt;/code&gt; в текущей ветке версией из &lt;code&gt;develop&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;78lS&quot;&gt;Если файлов несколько, пути можно указать через пробел (или просто указать директорию)&lt;/p&gt;

</content></entry><entry><id>osinpaul:lgp0E9j8sgh</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/lgp0E9j8sgh?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>Signal forms в Angular</title><published>2025-11-24T06:52:07.682Z</published><updated>2025-11-24T06:52:07.682Z</updated><summary type="html">Angular продолжает движение в сторону чистой реактивности без RxJS, и сигналы стали первым большим шагом. Теперь к ним добавились сигнальные формы — экспериментальный API, появившийся в Angular 21.</summary><content type="html">
  &lt;p id=&quot;YyWd&quot;&gt;Angular продолжает движение в сторону &lt;strong&gt;чистой реактивности без RxJS&lt;/strong&gt;, и сигналы стали первым большим шагом. Теперь к ним добавились &lt;strong&gt;сигнальные формы&lt;/strong&gt; — экспериментальный API, появившийся в Angular 21.&lt;/p&gt;
  &lt;p id=&quot;WBSt&quot;&gt;Это логичное продолжение идеи: если уже есть реактивное состояние на сигналах, то и формы тоже должны жить в этой модели — без подписок, без форм-групп, без FormControl, без грязных зон.&lt;/p&gt;
  &lt;p id=&quot;RgAh&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;kZMY&quot;&gt;Первое, что бросается в глаза — состояние формы мы объявляем через обычный сигнал без Observable и без подписок:&lt;/p&gt;
  &lt;pre id=&quot;QgUR&quot; data-lang=&quot;typescript&quot;&gt;formData = signal({
  username: &amp;#x27;btomer&amp;#x27;,
  firstName: &amp;#x27;Tomer&amp;#x27;,
  lastName: &amp;#x27;Bill&amp;#x27;,
  age: 35,
});&lt;/pre&gt;
  &lt;p id=&quot;1Ieg&quot;&gt;Далее создаём саму форму:&lt;/p&gt;
  &lt;pre id=&quot;uKHy&quot; data-lang=&quot;typescript&quot;&gt;form = form(this.formData, (user) =&amp;gt; {
  required(user.username);
  required(user.lastName);
  pattern(user.firstName, /^[A-Za-z]+$/, { message: &amp;#x27;Проверьте ввод, допустимы только символы&amp;#x27; });
  max(user.age, 40);
});&lt;/pre&gt;
  &lt;p id=&quot;DYbU&quot;&gt;По сравнению со старым подходом все стало сильно проще: никаких FormControl, FormBuilder и подписок на изменения. &lt;/p&gt;
  &lt;p id=&quot;Fb6I&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;zFPl&quot;&gt;Связывание с шаблоном так же упростилось:&lt;/p&gt;
  &lt;pre id=&quot;AoqB&quot; data-lang=&quot;typescript&quot;&gt;&amp;lt;input [control]=&amp;quot;form.username&amp;quot; /&amp;gt;
&amp;lt;input [control]=&amp;quot;form.firstName&amp;quot; /&amp;gt;
&amp;lt;input type=&amp;quot;number&amp;quot; [control]=&amp;quot;form.age&amp;quot; /&amp;gt;

&amp;lt;app-custom [control]=&amp;quot;form.lastName&amp;quot; /&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;fT1v&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;7iIb&quot;&gt;Состояние формы так же выводится через сигналы:&lt;/p&gt;
  &lt;pre id=&quot;ZVjK&quot; data-lang=&quot;typescript&quot;&gt;&amp;lt;pre&amp;gt;{{ form().valid() }}&amp;lt;/pre&amp;gt;

@let errors = form().errorSummary();
@for (error of errors; track $index) {
  &amp;lt;p&amp;gt;{{ error | json }}&amp;lt;/p&amp;gt;
}

&amp;lt;pre&amp;gt;{{ form().value() | json }}&amp;lt;/pre&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;T2lh&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;Btix&quot;&gt;И самое главное, Angular уходит от использования CVA (Control Value Accessor)&lt;/p&gt;
  &lt;p id=&quot;sDC1&quot;&gt;Чтобы создать новый контрол, нужно  имплементировать интерфейс FormValueControl. На первый взгляд он сильно проще, но надо еще попробовать на сложном контроле. В базе выглядит как-то так:&lt;br /&gt;&lt;/p&gt;
  &lt;pre id=&quot;Ppn9&quot; data-lang=&quot;typescript&quot;&gt;@Component({
  selector: &amp;#x27;app-custom&amp;#x27;,
  imports: [Control],
  template: &amp;#x60;
    &amp;lt;div&amp;gt;
      &amp;lt;label&amp;gt;Last name: &amp;lt;/label&amp;gt;
      &amp;lt;input #i [value]=&amp;quot;value()&amp;quot; (input)=&amp;quot;value.set(i.value)&amp;quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;#x60;
})
export class Custom implements FormValueControl&amp;lt;string&amp;gt; {
  value = model&amp;lt;string&amp;gt;(&amp;#x27;&amp;#x27;);
}&lt;/pre&gt;
  &lt;p id=&quot;aZPA&quot;&gt;Всё, что нужно — сигнал &lt;code&gt;model()&lt;/code&gt;, представляющий значение.&lt;br /&gt;Ни registerOnChange, ни writeValue, ни setDisabledState — просто реактивная модель.&lt;/p&gt;
  &lt;p id=&quot;2eq8&quot;&gt;По ощущениям это действительно новая эпоха форм в Angular, похожая на то, как signals изменили работу с состоянием.&lt;/p&gt;
  &lt;p id=&quot;eMhO&quot;&gt;Попробовать сигнальные формы можно самостоятельно, например на &lt;a href=&quot;https://stackblitz.com/edit/stackblitz-starters-2tq7duke?file=src%252Fmain.ts&quot; target=&quot;_blank&quot;&gt;StackBlitz&lt;/a&gt;&lt;/p&gt;

</content></entry><entry><id>osinpaul:el6NSH2UbIk</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/el6NSH2UbIk?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>WeakMap</title><published>2025-07-22T16:57:22.407Z</published><updated>2025-07-22T16:57:22.407Z</updated><category term="java-script" label="JavaScript 📜"></category><summary type="html">WeakMap — это структура, которая не удерживает объект от удаления GC.</summary><content type="html">
  &lt;p id=&quot;Qo0U&quot;&gt;&lt;code&gt;WeakMap&lt;/code&gt; — это структура, которая &lt;strong&gt;не удерживает объект от удаления GC&lt;/strong&gt;.&lt;/p&gt;
  &lt;ul id=&quot;i7hu&quot;&gt;
    &lt;li id=&quot;72r1&quot;&gt;Ключи — &lt;strong&gt;только объекты&lt;/strong&gt;.&lt;/li&gt;
    &lt;li id=&quot;8hYC&quot;&gt;Если на ключ больше &lt;strong&gt;нет других ссылок&lt;/strong&gt; кроме &lt;code&gt;WeakMap&lt;/code&gt; — объект считается мусором и будет удалён.&lt;/li&gt;
    &lt;li id=&quot;tdOG&quot;&gt;&lt;code&gt;WeakMap&lt;/code&gt; &lt;strong&gt;не итерируемый&lt;/strong&gt;, ты не можешь получить &lt;code&gt;.keys()&lt;/code&gt; или &lt;code&gt;.size()&lt;/code&gt; — потому что эти объекты могут в любой момент &amp;quot;исчезнуть&amp;quot;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;eEoP&quot;&gt;&lt;/p&gt;
  &lt;pre id=&quot;NXXR&quot; data-lang=&quot;javascript&quot;&gt;const privateData = new WeakMap();

class User {
  constructor(name) {
    privateData.set(this, { name });
  }

  get name() {
    return privateData.get(this).name;
  }
}
&lt;/pre&gt;
  &lt;p id=&quot;t8CU&quot;&gt;📌 Когда экземпляр &lt;code&gt;User&lt;/code&gt; больше нигде не используется — он удаляется, и все его &amp;quot;приватные данные&amp;quot; в &lt;code&gt;WeakMap&lt;/code&gt; тоже.&lt;/p&gt;

</content></entry><entry><id>osinpaul:61Qva_TMKnu</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/61Qva_TMKnu?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>RxJS share() в Angular</title><published>2025-07-20T11:56:13.645Z</published><updated>2025-07-20T11:56:13.645Z</updated><summary type="html">Многие Observable в RxJS являются холодными. Это означает, что при каждой подписке создаётся новый поток, заново выполняются все операторы и возможные побочные эффекты — например, HTTP-запросы, таймеры, логирование и т.д.</summary><content type="html">
  &lt;p id=&quot;RkgQ&quot;&gt;Многие Observable в RxJS являются &lt;strong&gt;холодными&lt;/strong&gt;. Это означает, что при каждой подписке создаётся &lt;strong&gt;новый поток&lt;/strong&gt;, заново выполняются все операторы и возможные побочные эффекты — например, HTTP-запросы, таймеры, логирование и т.д.&lt;/p&gt;
  &lt;p id=&quot;HwEg&quot;&gt;Чтобы избежать повторных вычислений и &lt;strong&gt;разделить один источник данных между несколькими подписчиками&lt;/strong&gt;, используется оператор &lt;code&gt;share()&lt;/code&gt;. Он превращает Observable в &lt;strong&gt;горячий&lt;/strong&gt; и включает режим &lt;strong&gt;мультикастинга&lt;/strong&gt; — подписчики получают данные из общего потока, а не инициируют новые.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;zwu8&quot;&gt;Пример холодного Observable&lt;/h2&gt;
  &lt;p id=&quot;fTvO&quot;&gt;Допустим, у нас есть сервис &lt;code&gt;DataService&lt;/code&gt;, который возвращает поток данных:&lt;/p&gt;
  &lt;pre id=&quot;ITcr&quot;&gt;import { Injectable } from &amp;#x27;@angular/core&amp;#x27;;  
import { Observable, of, delay, tap } from &amp;#x27;rxjs&amp;#x27;;  
  
@Injectable({ providedIn: &amp;#x27;root&amp;#x27; })  
export class DataService {  
  private callCount = 0;

  // Холодный Observable — каждый вызов создаёт новый поток
  getData(): Observable&amp;lt;string&amp;gt; {  
    this.callCount++;  
    return of(&amp;#x60;Ответ #${this.callCount}&amp;#x60;).pipe(  
      delay(1000), // имитируем задержку  
      tap(value =&amp;gt; console.log(&amp;#x27;🔥 Выполнен запрос!&amp;#x27;, value))  
    );  
  }
}
&lt;/pre&gt;
  &lt;p id=&quot;qDDr&quot;&gt;И у нас есть компонент, который подписывается на этот Observable дважды:&lt;/p&gt;
  &lt;pre id=&quot;qnGX&quot;&gt;import { Component, OnInit } from &amp;#x27;@angular/core&amp;#x27;;  
import { DataService } from &amp;#x27;../../data.service&amp;#x27;;  
  
@Component({  
  selector: &amp;#x27;app-cold&amp;#x27;,  
  template: &amp;#x60;
    &amp;lt;h2&amp;gt;Cold Observable (без share)&amp;lt;/h2&amp;gt;
    &amp;lt;div *ngIf=&amp;quot;value1&amp;quot;&amp;gt;Подписка 1: {{ value1 }}&amp;lt;/div&amp;gt;
    &amp;lt;div *ngIf=&amp;quot;value2&amp;quot;&amp;gt;Подписка 2: {{ value2 }}&amp;lt;/div&amp;gt;
  &amp;#x60;  
})  
export class ColdComponent implements OnInit {  
  value1?: string;  
  value2?: string;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    const obs$ = this.dataService.getData();  
  
    obs$.subscribe(value =&amp;gt; (this.value1 = value));  
    obs$.subscribe(value =&amp;gt; (this.value2 = value));  
  }
}
&lt;/pre&gt;
  &lt;p id=&quot;MPVz&quot;&gt;&lt;strong&gt;Результат:&lt;/strong&gt; в консоли мы увидим:&lt;/p&gt;
  &lt;pre id=&quot;ARTE&quot;&gt;🔥 Выполнен запрос! Ответ #1  
🔥 Выполнен запрос! Ответ #2  
&lt;/pre&gt;
  &lt;p id=&quot;IgzA&quot;&gt;Каждая подписка вызывает Observable заново. Это может быть нежелательно, если Observable делает сетевой запрос или дорогостоящую операцию.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;H7wN&quot;&gt;🔥 Преобразуем в горячий Observable с &lt;code&gt;share()&lt;/code&gt;&lt;/h2&gt;
  &lt;p id=&quot;D4Q7&quot;&gt;Теперь давайте применим &lt;code&gt;share()&lt;/code&gt; и сделаем один общий поток:&lt;/p&gt;
  &lt;pre id=&quot;IUPV&quot;&gt;import { Component, OnInit } from &amp;#x27;@angular/core&amp;#x27;;  
import { DataService } from &amp;#x27;../../data.service&amp;#x27;;  
import { share } from &amp;#x27;rxjs/operators&amp;#x27;;

@Component({  
  selector: &amp;#x27;app-shared&amp;#x27;,  
  template: &amp;#x60;
    &amp;lt;h2&amp;gt;Shared Observable (с share)&amp;lt;/h2&amp;gt;
    &amp;lt;div *ngIf=&amp;quot;value1&amp;quot;&amp;gt;Подписка 1: {{ value1 }}&amp;lt;/div&amp;gt;
    &amp;lt;div *ngIf=&amp;quot;value2&amp;quot;&amp;gt;Подписка 2: {{ value2 }}&amp;lt;/div&amp;gt;
  &amp;#x60;  
})  
export class SharedComponent implements OnInit {  
  value1?: string;  
  value2?: string;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    const shared$ = this.dataService.getData().pipe(share());

    shared$.subscribe(value =&amp;gt; (this.value1 = value));  
    shared$.subscribe(value =&amp;gt; (this.value2 = value));  
  }
}
&lt;/pre&gt;
  &lt;p id=&quot;5ifC&quot;&gt;&lt;strong&gt;Результат:&lt;/strong&gt;&lt;/p&gt;
  &lt;pre id=&quot;7yDc&quot;&gt;🔥 Выполнен запрос! Ответ #3
&lt;/pre&gt;
  &lt;p id=&quot;DOcV&quot;&gt;Теперь обе подписки получают &lt;strong&gt;одни и те же данные&lt;/strong&gt; из одного источника. Это и есть эффект &lt;strong&gt;мультикастинга&lt;/strong&gt;.&lt;/p&gt;

</content></entry><entry><id>osinpaul:gjCt2Hd8EbD</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/gjCt2Hd8EbD?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>Запуск Chrome в режиме бeз CORS</title><published>2025-07-01T10:29:20.498Z</published><updated>2025-11-01T07:21:52.359Z</updated><summary type="html">Во время локальной разработки часто возникает необходимость тестирования API-запросов к другим доменам. Браузер блокирует такие запросы из-за политики CORS, что затрудняет отладку. Для упрощения тестирования можно запустить Chrome в режиме без проверки CORS.</summary><content type="html">
  &lt;p id=&quot;G7H3&quot;&gt;Во время локальной разработки часто возникает необходимость тестирования API-запросов к другим доменам. Браузер блокирует такие запросы из-за политики CORS, что затрудняет отладку. Для упрощения тестирования можно запустить Chrome в режиме без проверки CORS.&lt;/p&gt;
  &lt;blockquote id=&quot;KkzH&quot;&gt;⚠️ &lt;strong&gt;Внимание:&lt;/strong&gt; запуск в таком режиме снижает безопасность. Используется &lt;strong&gt;только для разработки&lt;/strong&gt;.&lt;/blockquote&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;8MLG&quot;&gt;Инструкция по запуску&lt;/h2&gt;
  &lt;h3 id=&quot;UiBD&quot;&gt;Windows&lt;/h3&gt;
  &lt;ol id=&quot;iWBj&quot;&gt;
    &lt;li id=&quot;k4aq&quot;&gt;Закрыть все окна Chrome.&lt;/li&gt;
    &lt;li id=&quot;jSgx&quot;&gt;Открыть &lt;strong&gt;Командную строку&lt;/strong&gt; или &lt;strong&gt;PowerShell&lt;/strong&gt;.&lt;/li&gt;
    &lt;li id=&quot;4PBS&quot;&gt;Выполнить команду:&lt;/li&gt;
  &lt;/ol&gt;
  &lt;pre id=&quot;CmuJ&quot;&gt;start chrome --disable-web-security --user-data-dir=&amp;quot;C:/chrome-dev-disable-cors&amp;quot;
&lt;/pre&gt;
  &lt;p id=&quot;yiBQ&quot;&gt;&lt;strong&gt;Пояснения:&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;T3ZU&quot;&gt;
    &lt;li id=&quot;sVyV&quot;&gt;&lt;code&gt;--disable-web-security&lt;/code&gt; отключает проверку политики безопасности, включая CORS.&lt;/li&gt;
    &lt;li id=&quot;81lW&quot;&gt;&lt;code&gt;--user-data-dir=&amp;quot;C:/chrome-dev-disable-cors&amp;quot;&lt;/code&gt; создаёт отдельный профиль для разработки, чтобы избежать конфликта с основным профилем.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;hr /&gt;
  &lt;h3 id=&quot;RFeR&quot;&gt;macOS&lt;/h3&gt;
  &lt;ol id=&quot;43gV&quot;&gt;
    &lt;li id=&quot;WlCa&quot;&gt;Закрыть все окна Chrome.&lt;/li&gt;
    &lt;li id=&quot;qUBT&quot;&gt;Открыть &lt;strong&gt;Terminal&lt;/strong&gt;.&lt;/li&gt;
    &lt;li id=&quot;TW6b&quot;&gt;Выполнить команду:&lt;/li&gt;
  &lt;/ol&gt;
  &lt;pre id=&quot;38Rl&quot;&gt;open -na &amp;quot;Google Chrome&amp;quot; --args --disable-web-security --user-data-dir=&amp;quot;/tmp/chrome-dev-disable-cors&amp;quot;
&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;h3 id=&quot;I7X3&quot;&gt;Linux&lt;/h3&gt;
  &lt;ol id=&quot;rh09&quot;&gt;
    &lt;li id=&quot;YajM&quot;&gt;Закрыть все окна Chrome.&lt;/li&gt;
    &lt;li id=&quot;DwAI&quot;&gt;Выполнить в терминале:&lt;/li&gt;
  &lt;/ol&gt;
  &lt;pre id=&quot;fxsE&quot;&gt;google-chrome --disable-web-security --user-data-dir=&amp;quot;/tmp/chrome-dev-disable-cors&amp;quot;
&lt;/pre&gt;
  &lt;h2 id=&quot;oAkT&quot;&gt;&lt;/h2&gt;
  &lt;h3 id=&quot;UAOU&quot;&gt;А если без командной строки?&lt;br /&gt;&lt;/h3&gt;
  &lt;p id=&quot;wp8n&quot;&gt;Можно воспользоваться расширениями для браузера. Например, для &lt;a href=&quot;https://chromewebstore.google.com/detail/cors-unblock/hadoojkfknbjgoppkecpgamiajljiief?hl=ru&amp;utm_source=ext_sidebar&quot; target=&quot;_blank&quot;&gt;chrome&lt;/a&gt;:  или &lt;a href=&quot;https://mybrowseraddon.com/access-control-allow-origin.html&quot; target=&quot;_blank&quot;&gt;firefox. &lt;/a&gt;Но, по моему опыту, поведение с ними иногда непредсказуемо. Лично ловил в хроме упавшие запросы по корсам, когда они вроде бы должны быть отключены.&lt;/p&gt;

</content></entry><entry><id>osinpaul:MIpELP9Fj3v</id><link rel="alternate" type="text/html" href="https://osinpaul.ru/MIpELP9Fj3v?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=osinpaul"></link><title>REST vs GraphQL</title><published>2025-04-01T10:27:19.542Z</published><updated>2025-04-01T10:27:19.542Z</updated><category term="teoriya" label="Теория 📔"></category><summary type="html">В мире веб-разработки REST и GraphQL являются двумя наиболее популярными подходами к организации взаимодействия между клиентом и сервером. Несмотря на то, что оба подхода решают схожие задачи — передача данных через API — у них есть ключевые различия, которые могут повлиять на выбор в зависимости от потребностей проекта.</summary><content type="html">
  &lt;p id=&quot;Qt7S&quot;&gt;В мире веб-разработки REST и GraphQL являются двумя наиболее популярными подходами к организации взаимодействия между клиентом и сервером. Несмотря на то, что оба подхода решают схожие задачи — передача данных через API — у них есть ключевые различия, которые могут повлиять на выбор в зависимости от потребностей проекта.&lt;/p&gt;
  &lt;h3 id=&quot;S8ZQ&quot;&gt;&lt;strong&gt;Что такое REST?&lt;/strong&gt;&lt;/h3&gt;
  &lt;p id=&quot;RSXX&quot;&gt;REST (Representational State Transfer) — это архитектурный стиль, который описывает взаимодействие между клиентом и сервером с использованием стандартных HTTP-методов (GET, POST, PUT, DELETE) и структурированных URL. RESTful API обычно строится вокруг ресурсов (например, пользователей, постов, товаров), и каждое действие над ресурсом выполняется через соответствующий HTTP-метод.&lt;/p&gt;
  &lt;p id=&quot;4uHp&quot;&gt;&lt;strong&gt;Пример запроса в REST:&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;Or5x&quot;&gt;
    &lt;li id=&quot;lJyS&quot;&gt;Получение списка пользователей:&lt;br /&gt; &lt;code&gt;GET /users&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;KK0f&quot;&gt;Получение одного пользователя:&lt;br /&gt; &lt;code&gt;GET /users/{id}&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;lGx0&quot;&gt;Создание нового пользователя:&lt;br /&gt; &lt;code&gt;POST /users&lt;/code&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3 id=&quot;ISYo&quot;&gt;&lt;strong&gt;Что такое GraphQL?&lt;/strong&gt;&lt;/h3&gt;
  &lt;p id=&quot;t9kV&quot;&gt;GraphQL — это язык запросов для API, который позволяет клиентам запрашивать только те данные, которые им действительно нужны. В отличие от REST, который подразумевает использование множества разных конечных точек, GraphQL имеет одну точку входа, через которую можно получить все необходимые данные.&lt;/p&gt;
  &lt;p id=&quot;NI8w&quot;&gt;&lt;strong&gt;Пример запроса в GraphQL:&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;FnfT&quot;&gt;Запрос на получение информации о пользователе и его постах:&lt;/p&gt;
  &lt;pre id=&quot;WUq7&quot; data-lang=&quot;graphql&quot;&gt;{
  user(id: &amp;quot;1&amp;quot;) {
    name
    posts {
      title
      content
    }
  }
}&lt;/pre&gt;
  &lt;p id=&quot;u4Vd&quot;&gt;Этот запрос возвращает только нужные данные, без излишней информации.&lt;/p&gt;
  &lt;h3 id=&quot;tnRQ&quot;&gt;&lt;strong&gt;Основные различия:&lt;/strong&gt;&lt;/h3&gt;
  &lt;ol id=&quot;SIaY&quot;&gt;
    &lt;li id=&quot;Ta5X&quot;&gt;&lt;strong&gt;Количество запросов:&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;7tYb&quot;&gt;
      &lt;li id=&quot;t1BV&quot;&gt;&lt;strong&gt;REST:&lt;/strong&gt; Для получения данных с нескольких ресурсов потребуется несколько запросов. Например, если нужно получить пользователя и его посты, потребуется два запроса: один для получения пользователя, второй — для получения постов этого пользователя.&lt;/li&gt;
      &lt;li id=&quot;UYbf&quot;&gt;&lt;strong&gt;GraphQL:&lt;/strong&gt; С помощью одного запроса можно получить все необходимые данные, независимо от количества связанных сущностей. В нашем примере достаточно одного запроса для получения и пользователя, и его постов.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;NX8y&quot;&gt;&lt;strong&gt;Гибкость запросов:&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;1zjx&quot;&gt;
      &lt;li id=&quot;LMKA&quot;&gt;&lt;strong&gt;REST:&lt;/strong&gt; Сервер определяет, какие данные и в каком формате клиент получает в ответ. Это может привести к избыточным данным или, наоборот, к необходимости делать несколько запросов для получения нужной информации.&lt;/li&gt;
      &lt;li id=&quot;bl3J&quot;&gt;&lt;strong&gt;GraphQL:&lt;/strong&gt; Клиент сам определяет, какие именно данные ему нужны. Это позволяет сократить объем передаваемых данных и повысить эффективность взаимодействия.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;g7g4&quot;&gt;&lt;strong&gt;Структура данных:&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;cKfc&quot;&gt;
      &lt;li id=&quot;N6so&quot;&gt;&lt;strong&gt;REST:&lt;/strong&gt; Каждый ресурс имеет свой собственный URL и обычно возвращает фиксированную структуру данных.&lt;/li&gt;
      &lt;li id=&quot;3K8y&quot;&gt;&lt;strong&gt;GraphQL:&lt;/strong&gt; Все запросы выполняются через один URL, а структура ответа зависит от запроса клиента. Это даёт более высокую степень кастомизации.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;SF8m&quot;&gt;&lt;strong&gt;Обработка ошибок:&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;2n3N&quot;&gt;
      &lt;li id=&quot;sfM5&quot;&gt;&lt;strong&gt;REST:&lt;/strong&gt; Ошибки обрабатываются через статус-коды HTTP (например, 404 для &amp;quot;не найдено&amp;quot;, 500 для &amp;quot;ошибка сервера&amp;quot;).&lt;/li&gt;
      &lt;li id=&quot;KGH5&quot;&gt;&lt;strong&gt;GraphQL:&lt;/strong&gt; Ошибки можно передавать в теле ответа. Они не блокируют успешное получение данных — даже если часть запроса не выполнена, остальные данные могут быть возвращены.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;2XcC&quot;&gt;&lt;strong&gt;Сложность реализации:&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;jYFR&quot;&gt;
      &lt;li id=&quot;tqEK&quot;&gt;&lt;strong&gt;REST:&lt;/strong&gt; Более прост в реализации, особенно для небольших приложений. В большинстве случаев используется стандартная структура URL и методы.&lt;/li&gt;
      &lt;li id=&quot;KCrA&quot;&gt;&lt;strong&gt;GraphQL:&lt;/strong&gt; Сложнее в настройке, но даёт более мощные инструменты для работы с данными. Требует более тщательного планирования схемы API.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/ol&gt;
  &lt;h3 id=&quot;BZDt&quot;&gt;&lt;strong&gt;Сходства:&lt;/strong&gt;&lt;/h3&gt;
  &lt;ol id=&quot;5Tth&quot;&gt;
    &lt;li id=&quot;54GK&quot;&gt;&lt;strong&gt;Обе технологии используют HTTP:&lt;/strong&gt; Несмотря на разные подходы, REST и GraphQL работают через HTTP-протокол и могут использовать схожие механизмы аутентификации, например, с использованием токенов.&lt;/li&gt;
    &lt;li id=&quot;CGYt&quot;&gt;&lt;strong&gt;Обе предлагают механизмы фильтрации данных:&lt;/strong&gt; В REST это обычно происходит через параметры запроса (например, &lt;code&gt;?page=1&amp;amp;limit=10&lt;/code&gt;), а в GraphQL — через поля запроса.&lt;/li&gt;
    &lt;li id=&quot;sYm1&quot;&gt;&lt;strong&gt;Обе позволяют работать с различными типами данных:&lt;/strong&gt; и REST, и GraphQL могут работать с текстовыми данными, изображениями, файлами и т.д.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;h3 id=&quot;zs3l&quot;&gt;&lt;strong&gt;Когда использовать REST, а когда GraphQL?&lt;/strong&gt;&lt;/h3&gt;
  &lt;ul id=&quot;hhN4&quot;&gt;
    &lt;li id=&quot;0h4V&quot;&gt;&lt;strong&gt;REST:&lt;/strong&gt; Подходит для простых приложений, где структура данных не меняется часто, и нет сложных зависимостей между сущностями. Это классический выбор для большинства CRUD-приложений.&lt;/li&gt;
    &lt;li id=&quot;0O1R&quot;&gt;&lt;strong&gt;GraphQL:&lt;/strong&gt; Идеален для сложных приложений с множеством взаимосвязанных данных, где важно минимизировать количество запросов и оптимизировать трафик. Особенно полезен в случае, когда структура данных может изменяться, и нужно обеспечить гибкость запросов.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3 id=&quot;M3Nf&quot;&gt;&lt;/h3&gt;
  &lt;p id=&quot;aYwD&quot;&gt;REST и GraphQL — это два подхода с разной философией. REST ориентирован на ресурсы и стандартные HTTP-методы, в то время как GraphQL даёт клиентам полную гибкость в запросах. Выбор между ними зависит от требований к проекту: если важна простота и стандартность, лучше использовать REST; если нужна гибкость и эффективность, GraphQL может быть лучшим вариантом.&lt;/p&gt;

</content></entry></feed>