tgindex

Виталий и Платформа

описание

📍 О том, как всё устроено в веб-платформе и что происходит в индустрии фронтенда ⭐️ Новости, полезные выжимки, находки и напоминания 👨‍💻 Вопросы и предложения @web_platform_support 🔗 juwain.github.io/web-platform

1 826
подписчиков
Охват к подписчикам
84,8%
ERR
Реакции к просмотрам
1,31%
443 на 25 постов
Пересылки к просмотрам
1,18%
400
Постов в день
0,1
всего 25

Где отзываются чаще

доля реакций к просмотрам
  • 14 авг.Мой воркфлоу с ИИшками сейчас У меня счас несколько подписок: • антропик 20$ (для разработки) • опенай кодекс 20$ (для разработки) • и российская hub.neuraldeep.ru (в основном не для разработки) Пользую через claude code консольный, через него можно пользовать и модели сlaude нативно, и любую другую модель подцеплять. Для этого можно задать/отредактировать в энвах переменные ANTHROPIC_BASE_URL, ANTHROPIC_MODEL. Либо если хочется вариант поинтереснее, то есть инструмент ccs, который через поднятие локального прокси CLIProxy позволяет прокидывать запросы в формате claude code и конвертировать его в формат нужного провайдера (OpenAI/Gemini/Codex/Kimi…), а заодно и авторизовываться по OAuth. Какой профит все модели гонять через харнесс Claude Code? В едином месте собрана вся история моих взаимодействий с иишками, папка .claude становится как бы средой, накапливающей память обо всех проектах (менеджер сессий), процессах, что я делаю по работе и нет. Отсюда из этой среды можно черпать инфу для статей, идей, вспоминать какие-то моменты и возвращаться, то есть по сути это хранилище ВСЕГО контекста взаимодействия меня/ИИ. Кстати, сам CC именно как менеджер сессий не оч, и когда сессий становится много, ориентироваться начинает становиться сложно, для этого есть спец тула codbash, которая позволяет менеджерить все свои cc, codex и прочие ии-сессии на компутере и удобно к ним возвращаться (кстати, с недавних пор эта тулза стала уметь запускать сессии прямо внутри себя). Важный момент: сессии по умолчанию в СС подчищаются/ротируются раз в месяц, это регулируется настройкой ~/.claude/settings.json (можно выставить большое значение, чтобы старые сесии не пропадали + бэкапить с помощью codbash-а, там есть спец команда). Например, сейчас с начала года у меня накопилось 2363 сессий (по работе и не по работе). Все сессии запускаю в режиме --dangerously-skip-permissions, чтобы не просило лишний раз разрешений, а просто делало работу. Для защиты от случайных деструктивных действий использую защиту. В качестве редактора использую zed, иишные штуки там в основном не пользую, только для редактирования кода и просмотра диффов. Раньше для управления терминалами использовал отдельное приложение superset + iterm2, теперь перешёл на мультиплексор herdr + iterm2 (плюс в том, что это не приложение на electron и не ест много памяти). Есть ли разница между антропиком за 20$ и кодексом за 20$ в плане решения ежедневных задач? Я их использую обычно вместе, типа опус 4.8/5 делает задачу, гпт 5.5/sol/luna ревьюит или наоборот. Плюс запускать разные модели в одном харнессе: они имеют лёгкий доступ к сессиям друг друга (буквально можно указать id сессии и модель разберётся, как туда попасть). В харнессе codex недавно появилась возможность запуска сессий/общения меж сессиями не вручную, а самим агентом, в CC пока нет, ожидаю. Насчёт именно ревью кодексом опуса есть спец средство для этого — плагин codex для cc, которое позволяет запустить ревью кода кодексом прям внутри сессии клода /codex: review, запустится субпроцесс. Также есть /codex:codex-rescue для того, чтобы просто "спросить совета" у codex. Скорее всего эта инфа устареет уже через месяц и полностью превратится в тыкву через год, но всё же решил зафисировать текущее ссотояние дел, может кому пригодится. #Лаборатория_веб_платформы @web_platform | Поддержать блог 💙5,23%
  • 1 мар.End-to-end разработчик Деление на фронтенд и бэкенд никогда не было про технику. Это было больше про бизнес-процессы и найм: в индустрии удобнее искать заменяемых специалистов на узкий скоуп. Фронтендер имплементит "клиентскую" логику, бэкендер — API и "серверную" логику. Но по сути это единое целое и деление, если посмотреть свежим взглядом, весьма условное. Я прекрасно помню времена, когда фреймворки не затачивались конкретно на фронт/бэк, а были про веб-разработку проекта целиком. И сейчас это деление начинает терять смысл. ИИшки привносят собой инфляцию ценности навыков (и стоимости строк кода). Уметь писать код стоит меньше, чем год назад. И будет стоить ещё меньше через год. Причём стоит меньше и в деньгах, и в самой ценности навыка: знания становятся все более и более общедоступны и легкодоступны, и само понятие конкретного навыка — это теперь не что-то труднодостигаемое и "выстрадываемое" потом и кровью. Приходит эпоха полнофункционального разработчика (олды тут? привет, вебмастера). Это как бы «фуллстек» в старом понимании (например, в связке React + Node) + одновременно, который понимает систему целиком от и до, может и обозревать верхнеуровнево, и "зумиться" до детальной имплементации, настраивает инфраструктуру, валидирует, что и как система делает, а главное — следит, как система соотносится с бизнесом, насколько качественно решает его проблемы. End-to-end-разработчик. Карьерный путь становится короче и интенсивнее. "Junior" в привычном смысле исчезает — вместо него "junior senior": человек приходит уже с наработанным опытом через пет-проекты, симуляционные окружения. С образованием вообще всё просто небывало круто. Как никогда раньше обучение может быть безостановочным процессом и не привязанным к конкретному обучающему вендору — селф-обучение выходит на первый план, а бизнесы напрямую предоставляют свою инфру для специализации в нужном контексте. Реагировать на происходящее можно по-разному. Отрицать, игнорировать, плыть по течению или против. Но если хочется сохранить привычные заработки и скорость их роста, в обозримом будущем все пути ведут к тому, чтобы брать на себя бо́льшую ответственность. Для разработчика это или уходить в пипл-менеджмент, или же расширять и углублять технику. Я за свою карьеру два раза проходит цикл "разраб -> тимлид -> разраб" и понял, что не хочу копать в менеджмент дальше. Поэтому сейчас отзываясь на вызовы нового времени выбираю второе. Так что буду уделять больше своего времени частям веб-разработки и платформы, которые выходят за пределы интерфейса в браузере: инфраструктуре, "серверу", инструментам сборки и доставки, проектированию систем. И вникать в зарождающуюся роль e2e-разработчика — теперь больше "пчеловода", чем "собирателя". Думаю, это также отразится и на содержании блога. Всем послушных пчёл! 👁️ #Фикшн_веб_платформы @web_platform | Поддержать платформу2,52%
  • 3 апр.Не теряемся Раньше я составлял и публиковал дайжесты #Пульс_веб_платформы. Дело это было довольно муторное и меня хватило на выпуск где-то примерно 80 еженедельных дайджестов. В процессе я каждый четверг пропускал через себя несколько других дайджестов, около сотни тг-каналов и примерно столько же отдельных RSS-блогов. С момент последнего выпуска прошло около 10 месяцев, одно время я совсем бросил что-то читать, но потом интерес вернулся. Попробовал собрать несколько дайджестов, но мозг сразу сказал, что заниматься написанием дайджестов больше не будет. Но читать дайджесты при этом хочется! И я решил построить автоматизированный пайплайн сборки дайджестов (прежде всего для себя, но может быть и вам зайдёт). В общем, дождался блокировки телеги и вынес дайджест в отдельного бота @web_platform_pulse_bot. В нём ежедневно утром бот публикует примерно 10 ссылок с самым интересным, что произошло в индустрии за прошлые сутки. Аннотации пишу уже не я, а модель, поэтому в душевности дайджест, конечно, потерял. Но в информативности, пожалуй, даже прибавил, так как посты стали ежедневными вместо еженедельных, и покрывают большее число материалов. Если хочется за пару минут понять, что там произошло в целом, достаточно краткой сводки утром по диагонали вместо чтения объёмных рассылок. Я умышленно отделил авторский контент блога от генерёжки дайджеста, поэтому он будет именно в отдельном боте @web_platform_pulse_bot. Помимо бота дайджест публикуется на сайт https://juwain.github.io/web-platform/pulse/ и в RSS https://juwain.github.io/web-platform/pulse/rss.xml. Основной веблог тоже перевёз на gh pages https://juwain.github.io/web-platform/blog/ и в RSS https://juwain.github.io/web-platform/rss.xml (на очередную оплату домена webplatform.tech блог, увы, не заработал). Отдельно настроил публикацию в зеркало на статику яндексовского sourcecraft. Там ресурсы доступны даже на парковке, так как Яндекс в белых списках. Вот зеркало дайджеста https://juwain.sourcecraft.site/web-platform/pulse/ и его RSS https://juwain.sourcecraft.site/web-platform/pulse/rss.xml. А также зеркало основного блога https://juwain.sourcecraft.site/web-platform/blog/ и его RSS https://juwain.sourcecraft.site/web-platform/rss.xml. Если интересны технические детали реализации, поставьте реакций, я тогда позже напишу отдельный пост, как эта фиговина работает в более-менее автономном режиме и в лучшем случае требует от меня поревьюить подготовленные тексты через интерфейс TG. В комментах приложу пару примеров дайджестов за прошлые дни. #Лаборатория_веб_платформы @web_platform | Поддержать платформу 🌟2,00%
  • 4 февр.Что нового Baseline Widely Available, часть 1 В прошлом году (+ в январе 2026) довольно много платформенных фич достигли статуса Baseline Widely Available — это значит, что они поддерживаются во всех основных браузерах уже 30 месяцев! А это повод бескомпромиссно тащить их в прод проверить аналитику по пользовательским устройствам и браузерам в вашем проекте (скорее всего ваши пользователи уже обзавелись браузером, которые эти фичи поддерживают). Собрал списком все фичи с кратким описанием и моими комментами к их использованию. CSS Container Queries — применение стилей на основе размеров родительского контейнера, а не viewport. Позволяет создавать адаптивные компоненты, которые реагируют на свой контейнер. .card { container-type: inline-size; } @container (min-width: 300px) { .card { background-color: lightblue; } } Мнение: 5 из 5, но требует перестроить майндсет в целом при работе с UI (то есть не только вам как разработчику, но ещё из продукту и дизайнеру, часто думающим "мобилками"). Хорошо ложится на UI-киты и разработку изолированных компонентов. — Individual Transform Properties — отдельные свойства translate, scale, rotate вместо одного transform. Упрощает анимации и улучшает читаемость кода. .element { translate: 20px 10px; scale: 0.8; rotate: 90deg; } Мнение: 3 из 5, приятный сахар, помогает сократить повторящийся код при написании трансформов, легко внедрить. — Motion Path — анимация элементов вдоль заданного пути с помощью offset-path. .element { offset-path: path('M10 10 L 100 100'); animation: move 2s linear infinite; } Мнение: 2 из 5, специфичная, но прикольная фича, но если появится подходящая задача и вовремя вспомнить, можно избежать использования JS для анимации движения элемента по траектории. — Grid Animation — анимация свойств grid-template-columns и grid-template-rows. .grid { display: grid; grid-template-columns: 1fr 1fr; transition: grid-template-columns 0.3s; } .grid:hover { grid-template-columns: 1fr 2fr; } Мнение: 2 из 5, в целом прикольная фича для разъезжающихся динамических лейаутов, но применимость нечастая. — animation-composition — выбор способа комбинирования анимаций, влияющих на одно свойство. .element { animation-composition: add; /* replace, add, accumulate */ } Мнение: 2 из 5, если делаете руками CSS-анимации, может пригодиться, иначе не будет полезно. — color-mix() — смешивание двух цветов в заданном цветовом пространстве. Полезно для создания светлых/тёмных оттенков. .element { background: color-mix(in srgb, red 50%, white 50%); } Мнение: 4 из 5, чрезвычайно мощная штука, если настраиваете собственную дизайн-систему, стайлгайд или UI-кит в проекте. — color() — выбор цвета из указанного цветового пространства. Поддерживает wide-gamut пространства вроде display-p3 для более насыщенных цветов. .element { color: color(display-p3 0.5 0.2 0.8); } Мнение: 4 из 5, аналогично мнению про color-mix(). — Lab и LCH — цветовые пространства с перцептивной равномерностью. Lab использует прямоугольные координаты, LCH — полярные. Oklab и OkLCh — цветовые пространства, разработанные для лучшего соответствия человеческому восприятию цвета. OkLCh — полярный вариант Oklab. .element { color: lab(50% 20 -30); background: lch(60% 50 150); color: oklab(0.5 0.2 -0.1); background: oklch(0.6 0.5 120); } Мнение: 4 из 5, формат oklch может также пригодиться для гибкой настройки цветовой палитры и её производных в проекте. — Small, Large, Dynamic Viewport Units — единицы dvh, svh, lvh для учёта UI мобильных браузеров. sv* — минимальный размер, lv* — максимальный, dv* — динамический. .element { height: 100svh; /* 100% минимального viewport height */ width: 100dvw; /* 100% динамического viewport width */ } Мнение: 4 из 5, если вы когда-нибудь боролись с fullscreen-высотой в вёрстке на мобилках, то в следующий раз присмотритесь к этим значениям. продолжение следует... #Пульс_веб_платформы @web_platform | Поддержать платформу ⭐️1,87%
  • 5 февр.Что нового Baseline Widely Available, часть 2 ...продолжение CSS Two-value display property — свойство display теперь принимает два значения для явного указания внешнего и внутреннего режима layouts. Например, inline flex или block flow. .card { display: inline flex; } .container { display: block flow; } Мнение: 2 из 5, любопытно, но не более. — overflow: clip — обрезка содержимого без создания скроллов, с возможностью обрезки только по одной стороне .nav-bar { overflow-x: clip; overflow-y: visible; } Мнение: 4 из 5, полезно, если нужно включить скролл по одной стороне + также это значение overflow, в котором не создаётся новый formatting context. — overscroll-behavior — управление поведением при прокрутке за границы. .container { overscroll-behavior: contain; } Мнение: 3 из 5, очень узкоспециализированная фича, но может быть крайне полезна для управлением мульти-скроллом. — outline — свойства для стилизации линии вокруг элемента вне border, не влияющие на layout. .element { outline: 2px dashed red; outline-offset: 5px; } Мнение: 3 из 5, фича-то старая, но был баг в Safari, который починили в 03.2023. — :nth-child() of <selector> — более точный выбор элементов с фильтром по селектору. li:nth-child(odd of .active) { background: yellow; } Мнение: 4 из 5, прикольная штука для условной стилизации, полезно вспомнить перед решением реализовать такое на JS. — Media Query Range Syntax — новый синтаксис для медиавыражений с диапазонами. @media (400px <= width <= 800px) { /* стили */ } Мнение: 4 из 5, синтаксический сахар, нравится, что наконец не нужно каждый раз вспоминать и перебирать варианты min/max-width. — Resolution Media Query — медиавыражение для применения стилей на основе плотности пикселей устройства. @media (min-resolution: 2dppx) { /* стили для high-DPI экранов */ } Мнение: 2 из 5, узкоспециализированная фича, но может пригодиться для детальной стилизации для разных мобильных устройств. — Trigonometric Functions — sin(), cos(), tan() в CSS для математических вычислений. .element { transform: rotate(calc(sin(45deg) * 1turn)); } Мнение: 2 из 5, любопытно, но слишком хардкорно, малоприменимо в реальной жизни. — calc() Keywords — математические константы e, pi, infinity, NaN в CSS math функциях. .element { font-size: calc(var(--base-size) * pi); height: calc(100vh * sin(pi / 2)); } Мнение: 1 из 5, не могу придумать, где это применить. — font-palette — выбор цветовой палитры из шрифта, с возможностью переопределения отдельных цветов через @font-palette-values. .element { font-palette: alternate; } Мнение: 1 из 5, малополезная фича, применимая только для "цветных" шрифтов, не стоит внимания — Style Containment и Inline-size Containment — изоляция стилей элемента. style ограничивает расчёт счётчиков и кавычек внутри элемента, а inline-size предотвращает установку inline-размеров элемента его содержимым, ускоряя рендеринг. .widget { contain: style; } .container { contain: inline-size; } Мнение: 3 из 5, мелкие малополезные фичи, style может пригодиться разве что в нестандартных нумерованных списках; inline-size, могу только в теории представить, что может быть полезно в разного рода wysiwyg-ах (за исключением контейнерных запросов, которые без этой шутки не заработают). — HTML / DOM inert — атрибут для отключения элементов от взаимодействия и accessibility tree. Элементы не получают фокус и не реагируют на клики. <div inert> <button>Неактивная кнопка</button> </div> Мнение: 4 из 5, мощная и простая фича, хороший юзкейс для ограничения пределов фокуса при показе модальных окон: модалка работает, body под ней за inert-ом. продолжение следует... #Пульс_веб_платформы @web_platform | Поддержать платформу ⭐️1,76%
  • 13 февр.Что нового Baseline Widely Available, часть 4 ...продолжение Web APIs Offscreen Canvas — рендеринг canvas в отдельном потоке без зависимостей от DOM. const offscreen = canvas.transferControlToOffscreen(); worker.postMessage({ canvas: offscreen }, [offscreen]); Мнение: 4 из 5, по идее довольно мощная возможность для оптимизации, опять же в связке с web worker-ом, куда можно кинуть "внеэкранную канву" для рендера чего-либо тяжёлого. — Screen Orientation — API для управления ориентацией экрана. await screen.orientation.lock('landscape'); Мнение: 4 из 5, мастхэв для работы с мобилками, пригождалось. — Server Timing — API для получения информации о таймингах запросов с сервера. const entries = performance.getEntriesByType('navigation'); const serverTiming = entries[0].serverTiming; Мнение: 4 из 5, это мощное дополнение к Web Performance API, правда требует настройки и бэка/сервера, чтобы отправлялись нужные заголовки. — Push Messages — API для push-уведомлений. const subscription = await pushManager.subscribe(); Мнение: 5 из 5, о, думаю, появлению этого API на iOS для PWA очень порадовались банки и другие выпиленные из сторов компании; всему, что убирает монополию вендоров в пользу веб-платформы ставим пятюню. — MathML — разметка математических формул без картинок и JS-библиотек. <p> The fraction <math> <mfrac> <mn>1</mn> <mn>3</mn> </mfrac> </math> is not a decimal number. </p> Мнение: 4 из 5, вот это очень хоть и узкоспециализировано, но на мой взгляд тоже круто, так как позволяет latex-у и подобным инструментам, избавиться от доп кода для рендера формул. #Пульс_веб_платформы @web_platform | Поддержать платформу ⭐️1,63%
  • 14 янв.А вы заметили, что со всеми этими ии-движухами как-то морально проще стало экспериментировать с новым софтом и подходами в работе. Раньше ну узнаешь, что какая-то там либа вышла, ну подумаешь, что надо когда-нибудь попробовать, и всё на том. Ну максимум на свежем проекте или новом компе что-то свежее ставишь, пробуешь. А теперь как-то реально попроще со всем этим стало: узнаёшь, сразу пробуешь, профит (наверное это можно назвать таблеткой от FOMO, правда хоть это и купирует приступы, но провоцирует повторные 😁). Возможно на такой волне стоит присмотреться и к остальной экосистеме. Например, вот в pnpm подвели итоги прошлого года. В v10 много всего интересного. Самое важное — pnpm перестал доверять всем пакетам. Раньше pnpm install давал любому пакету в дереве зависимостей запускать произвольный код (preinstall, postinstall). Это само собой создавало вектор атак через supply chain. В v10 lifecycle scripts блокируются по умолчанию. Никакого preinstall/postinstall без явного разрешения. allowBuilds: esbuild: true nx@21.6.4: true Ещё одна фишка pnpm — content-addressable store (дедупликация файлов). В v10.12 сделали ещё шаг дальше — глобальное виртуальное хранилище. pnpm-workspace.yaml enableGlobalVirtualStore: true Раньше каждый проект имел свой node_modules. Теперь общие зависимости линкуются один раз глобально. А это: 1. Экономия диска — одинаковые графы зависимостей шарятся между проектами. 2. Быстрые установки — если 10 проектов используют react@19, pnpm линкует его один раз из глобального хранилища на машине. Для монорепозиториев и сложных настроек есть конфиги зависимостей. Это позволяет шарить конфиги pnpm (хуки, патчи, разрешения на билды) между проектами. Конфиги зависимостей устанавливаются в node_modules/.pnpm-config до основного графа зависимостей. # pnpm-workspace.yaml configDependencies: pnpm-plugin-my-company: "1.0.0+sha512-..." Это помогает шарить .pnpmfile.cjs хуки между репозиториями, централизовать патчи для patchedDependencies, поддерживать список пакетов, которым разрешено запускать build скрипты. Кроме того, pnpm давно умеет переключать версию Node.js для рантайма (не нужен nvm). В 2025 добавили Deno и Bun. // package.json { "devEngines": { "runtime": { "name": "node", "version": "24.6.0" } } } Pnpm сам скачает и будет использовать указанную версию для скриптов. Поддержка JSR — тоже прикольно, но пока что не особо актуально, как я вижу. Кроме того, ещё от себя добавлю, что в pnpm есть catalogs-алиасы (чтобы один раз в конфиге объявить набор зависимостей), workspaces (для организации монореп), возможность патчить зависимости (pnpm patch) и проверять их на актуальность (pnpm outdated) #Пульс_веб_платформы @web_platform | Поддержать платформу 🌀1,62%
  • 22 мар.Скриншот-тестирование компонентов в Docker В нескольких проектах, где я работал, пробовали внедрить скриншот-тестирование компонентов. Принцип работы такой: 1) меняем компонент 2) делаем эталонный скриншот, сохраняем в репе 3) повторяем на всех компонентах 4) при повторных изменениях запускаем тесты, в которых сравниваются эталонные скриншоты и текущее отображение компонентов 5) если все расхождения запланированные, меняем эталонные скрины; если есть незапланированные расхождения, поздравляю, тесты нашли регресс! Такие тесты хорошо подходят для теста вёрстки элементов ui-kit-а (многочисленные состояниях ховера, фокуса, разные режимы компонента) или же каких-то критичных компонентов (форма логина, чекаут), которые никак нельзя случайно поломать. Снепшотные тесты, юниты, e2e тут не помогут, нужно именно сравнение скринов. Звучит хорошо, но есть одно НО. Во всех случаях на моей практике дело упиралось в то, что скриншоты снимаются разными разработчиками на разных машинах с разными ОС. И в зависимости от этого скриншоты начинают расходиться. Шрифты другие. Субпиксельный рендеринг другой. Антиалиасинг другой. Также отличаются элементы форм. И тест падает, хотя визуально всё ок. Вместо помощи тесты начинают мешать и бесить. Логично было бы предположить делать скриншоты на одной машине, например, вынести этот процесс на CI/CD или на отдельный сервер, но обычно с этим в компаниях сложно, нужно всё согласовывать, заводить, что есть заморочь. И тут изучая Docker открыл для себя идеально подходящую под кейс фичу! Через Docker можно связать файловую систему хоста (ваша машина) с файловой системой запущенного контейнера. То есть храним скрины по прежнему в репо (развёрнутом у каждого разработчика), монтируем папку с ними внутрь Docker-контейнера, снимаем скриншоты, сравниваем, при необходимости обновляем. Внутри контейнера один и тот же образ с фиксированной ОС, фиксированной версией браузера, фиксированными шрифтами. И соотвественно таким же макаром запускается в CI. Собрал пруф оф концепт. Что там вкратце, происходит: Storybook изолирует компоненты, Playwright делает скриншоты, Docker обеспечивает одинаковый рендеринг. Оркестрация через docker-compose.yml (именно там указывается, какие локальные папки монтируются в контейнер): services: visual-tests: build: . volumes: # Persist screenshots - accessible from host - ./__screenshots__:/app/__screenshots__ # HTML report for visual diff - ./html-report:/app/html-report # Test results (traces, screenshots on failure) - ./test-results:/app/test-results # Source files for quick iteration (read-only) - ./src:/app/src:ro environment: - CI=true command: sh -c "npm run storybook -- --host 0.0.0.0 & sleep 15 && npx playwright test --project=visual" Docker-образ с Playwright официальный от Microsoft уже содержит браузеры и все зависимости. FROM mcr.microsoft.com/playwright:v1.48.0-noble Каждый визуальный тест — это три действия: открыть стори, дождаться рендера, сравнить скриншот. Storybook рендерит в статическом режиме каждый отдельный компонент по адресу /iframe.html?id=${storyId}&viewMode=story, ждём пока загрузятся картинки и шрифты, можно снимать скриншот Playwright-ом и сравнивать тоже им. Пример теста кнопки: test.describe('Button', () => { test('hover', async ({ page }) => { const component = await gotoStory(page, 'design-system-button--primary'); const button = component.locator('button.button').first(); await button.hover(); await expect(component).toHaveScreenshot(); }); }); Особенности настройки Playwright: можно оставить небольшой задел на случайные расхождения, выключить анимации, спрятать каретку в инпутах. toHaveScreenshot: { maxDiffPixels: 100, animations: "disabled", caret: "hide", }, После прогона тестов формируется HTML-репорт: интерактивный diff, где можно посмотреть, что именно изменилось (приложил скрин, как это выглядит в комментах). #Лаборатория_веб_платформы @web_platform | Поддержать платформу ⭐️1,62%
  • 11 февр.Что нового Baseline Widely Available, часть 3 ...продолжение JavaScript Array findLast() и findLastIndex() — поиск в массиве в обратном порядке. const array = [5, 12, 8, 130, 44]; const isLargeNumber = (element) => element > 100; console.log(array.findLast(isLargeNumber)); // 130 console.log(array.findLastIndex(isLargeNumber)); // 3 Мнение: 3 из 5, хорошо, когда удаётся утилиты заменять на встроенные методы, так что мелочь, а приятно. — Array by Copy — методы toSorted(), toReversed(), toSpliced(), возвращающие новый массив без изменения оригинала. const arr = [1, 2, 3]; const sorted = arr.toSorted(); const reversed = arr.toReversed(); Мнение: 3 из 5, это вот из разряда "теперь надо запомнить, что так делать можно", а то по привычке спредишь массив перед любой мутацией — Import Maps — декларативное управление импортами через JSON. <script type="importmap"> { "imports": { "lodash": "/node_modules/lodash-es/lodash.js", "utils": "/src/utils.js" } } </script> Мнение: 4 из 5, вот это, кстати, довольно важная непроходная фича, по сути позволяющая на ES-модулях построить проект без особой сборки, чтобы само собиралось в рантайме (но пригодно, на мой взгляд, в небольших проектах). — JavaScript Modules in Workers — использование ES-модулей в Web Workers с { type: "module" }. // worker.js import { processData } from './utils.js'; self.onmessage = (e) => { const result = processData(e.data); self.postMessage(result); }; Мнение: 2 из 5, с одной стороны, да, появилась возможность импортировать ES-модули, с другой — в этом режиме они импортируются под CORS-ом, а в классических воркерах доступен и `no-cors`-режим; в общем годная тема, ещё больше можно вынести в воркер. — requestAnimationFrame() in Workers — анимации в Web Workers. // worker.js function animate() { // логика анимации requestAnimationFrame(animate); } animate(); Мнение: 2 из 5, тоже плюсом к предыдущему пункту, если работаете с воркерами, то можно более плавно встраивать результат вычисления обратно в основной поток. — Compression Streams — API для сжатия/распаковки данных в формате gzip или deflate. const compressed = readableStream .pipeThrough(new CompressionStream('gzip')); Мнение: 3 из 5, может быть полезно для сжатия пользовательских файлов или распаковки полученных сжатых файлов, приятно, что заехало прям в платформу, можно не использовать доп либы. — Constructed Stylesheets — программное создание стилей через CSSStyleSheet и их совместное использование через adoptedStyleSheets. const sheet = new CSSStyleSheet(); sheet.replaceSync('.my-class { color: blue; }'); document.adoptedStyleSheets = [sheet]; Мнение: 2 из 5, более навороченный способ добавлять динамически новые стили, хз, может будет полезно авторам runtime-стилизационных библиотек. продолжение следует... #Пульс_веб_платформы @web_platform | Поддержать платформу ⭐️1,53%
  • 16 янв.react-best-practices Тут Vercel подогнали мозгов для ваших frontend-агентов набор бестпрактисов и антипаттернов по React, можно установить в виде скиллов в ваш любимый агент или просто почитать. Темы, рассматриваемые внутри: устранение вотерфоллов, оптимизация размера бандла, SSR, дата-фетчинг на клиенте (жаль, что без tanstack, а только swr), оптимизация ререндеров, перфоманс рендеринга и JS, а также ещё немного мелочей. #Пульс_веб_платформы @web_platform | Поддержать платформу 🌀1,52%
  • 3 дек.Про микрофронты на коленке. Iframe Мне в работе попадалась реализация микрофронтов на основе iframe. В целом, это вариант, когда надо «дёшево и сердито». Обычно такое используется, когда хост-приложение не одно и в каждом внутри своеобразная сборка, то есть федерацией модулей или монорепой всех проблем не решить. Как это работает: по определённому адресу хостится мини-приложение, например, по урлу example.com/header грузится общее приложение хедера для всех возможных хост-сред. Во все хост-приложения хедер встраивается через iframe: <iframe src="/header"></iframe> Важный момент: чтобы не возникало проблем с CORS, при таком подходе приложения должны находиться на одном домене, а также полностью должны совпадать порт и протокол. Редко когда такое мини-приложение живёт само по себе. Обычно нужно, чтобы оно как-то общалось со внешним миром. Для этого есть система двунаправленных событий, которые отправляются с помощью метода postMessage: window.parent.postMessage — отправляет сообщение изнутри фрейма наружу, в родителя. iframe.contentWindow.postMessage — отправляет сообщение снаружи, от родителя, внутрь фрейма. Получаются отправленные события подпиской на message (как изнутри iframe, так и в родительском приложении снаружи): parent.html: <iframe id="myFrame" src="/child"></iframe> <script> const iframe = document.getElementById('myFrame'); // Отправка сообщения во фрейм iframe.addEventListener('load', () => { iframe.contentWindow.postMessage({ type: 'fromParent', payload: 'Привет!' }, '*'); }); // Получение сообщения от фрейма window.addEventListener('message', (event) => { if (event.data.type === 'fromChild') { console.log('Получено от фрейма:', event.data.payload); } }); </script> child.html: <script> // Отправка сообщения родителю window.parent.postMessage({ type: 'fromChild', payload: 'Ответ!' }, '*'); // Получение сообщения от родителя window.addEventListener('message', (event) => { if (event.data.type === 'fromParent') { console.log('Получено от родителя:', event.data.payload); } }); </script> Эту идею можно раскрутить и дальше. Например, если одни и те же данные с сервера нужны в нескольких микрофронтах, то это может привести к дублированию запросов. Один из выходов — перенести этот дублирующийся запрос в хост-приложение и шарить его с фреймами. Ещё один вариант — сделать отдельный iframe, в котором визуально ничего нет (его можно даже скрыть), но есть данные. Данные этот стор-iframe может шарить наружу всем желающим, тоже через postMessage. Также помимо общения через postMessage шаринг данных между контекстами можно построить на Broadcast Channel API или Channel Messaging API. Broadcast Channel обеспечивает широковещательную связь между всеми контекстами одного origin, а Channel Messaging создает секьюрный двусторонний канал связи между двумя конкретными контекстами. То есть этот самый «стор с шаренными данными» при получении данных может в зависимости от цели пулять данные или на всех, или точечно в конкретное место. Работают оба способа тоже на похожих postMessage. В случае с Broadcast Channel надо как-то пошарить между потребителями созданный объект самого канала: const bc = new BroadcastChannel("test_channel"); bc.postMessage("This is a test message."); bc.onmessage = (event) => { console.log(event); }; В случае с Channel Messaging параметры для связи двух контекстов передаются в доп параметрах postMessage: const channel = new MessageChannel(); const port1 = channel.port1; port1.onmessage = onMessage; port1.postMessage("value"); Бонус: у iframe есть атрибут loading=lazy, чтобы не подгружать те фреймы, которые находятся вне вьюпорта. И ещё: в хромиум-браузерах все iframe на одном домене будут обрабатываться в одном потоке, то есть могут потенциально блокировать друг друга, даже если не блокируют родителя (лайфхак: если нет нужды держать их на одном домене, то выносом на другой домен можно «распараллелить потоки»). #Лаборатория_веб_платформы @web_platform | Поддержать платформу 🌟1,47%
  • 18 февр.npm lifecycle scripts TIL, что в npm есть lifecycle scripts. Перед и после стандартных команд типа dev, build, test есть "хуки", на которые можно подцепить нужные дополнительные команды. npm run dev | predev → dev → postdev npm run build | prebuild → build → postbuild npm test | pretest → test → posttest Добавляете в package.json скрипт с префиксом pre или post — npm запустит их с соответствующем порядке: { "scripts": { "build:client": "node esbuild.client.mjs", "predev": "npm run build:client" "dev": "tsx src/server/index.ts", } } Сначала predev соберёт клиент, dev запустит сервер. Полезно, когда перед запуском надо что-то подготовить — собрать ассеты, проверить типы, создать папки. Что можно с этим сделать: - prepublishOnly — проверки перед публикацией пакета в npm - prebuild — генерация кода или очистка папки dist - postinstall — автоматические действия после npm install Дока #Лаборатория_веб_платформы @web_platform | Поддержать платформу 💕1,17%