Русский
Русский
English
Статистика
Реклама

Vue

Дайджест свежих материалов из мира фронтенда за последнюю неделю 459 (15 21 марта 2021)

22.03.2021 00:17:09 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast Новости 512 от CSSSR: История React API, доклады и воркшопы с Я <3 фронтенд, нюансы this, релизы браузеров и V8, semver
podcast Подкаст Сделайте мне красиво 57 Если всё работает, то и тесты не нужны
podcast Подкаст Веб-стандарты 273. Старый Edge, Safari, React Native и Preact, MDN по-русски, печать, EditorConfig, ховеры, шрифты

Веб-разработка


habr Очередной гайд по HTML-элементам, которые можно использовать в каждом проекте
en Объяснение принципа работы Webpacks Hot Module Replacement Feature
en Как имитировать мобильные устройства в режиме Device Mode в Chrome
en Русская локаль на MDN разморожена и можно снова участвовать в переводе лучших материалов для веб-разработчиков. Желающие присоединиться к проекту могут отписаться в соответствующем issue




CSS


en Официальная вложенность в CSS последний кусочек головоломки
en Знаете ли вы о CSS селекторе :has?
en Три важных вещи, которые вы должны знать о :is() в CSS
en 100 анимаций подчеркивания/наложения | The ultimate CSS collection
en CSS генераторы
en Fluid Space Calculator
en Лучшие техники переноса строки для длинных URL-адресов
en Новости платформы: Prefers Contrast, MathML, :is(), и CSS Background InitialValues
en Что я делаю с размерами шрифтов
en Да, вот лучший фреймворк CSS в 2021 году

JavaScript


Удобный доступ кбуферу обмена сClipboard API
habr JavaScript prototype pollution: практика поиска и эксплуатации
en Обработка пользовательских разрешений в JavaScript
en Совет 1 по ознакомлению с новыми кодовыми базами JavaScript
en Что такое JavaScript Internationalization API (I18n)?







Браузеры


Из Firefox намерены убрать компактный режим отображения панелей
В ночных и бета сборках Firefox включена по умолчанию поддержка HTTP/3
Chrome получил функцию Live Captions и теперь может автоматически создавать субтитры для видео и аудио с речью


Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 460 (22 28 марта 2021)

29.03.2021 00:13:50 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast UnderJS Podcast #30 Billing и бухгалтерия инженерная сторона с Виталием Слободиным.
podcast Новости 512 от CSSSR: Firefox 87, Grid на примерах, смерть Node.js-процессов, проблема JS-библиотек, Compat2021
podcast Подкаст Фронтенд Юность #178: Супер-выпуск. Массивы против объектов. Раунд 1
podcast Подкаст Веб-стандарты 274. Новинки V8, бета Chrome 90 и веб-бандлы, ненастоящие PWA и нативный CSS Nesting

Веб-разработка


habr 14 полезных инструментов, ускоряющих и упрощающих веб-разработку
en Глубокое погружение в Eleventy Static Site Generator
en Под капотом Emoji
en Конец AMP
en Интерактивные веб-компоненты проще, чем вы думаете
en Bootstrap 5 Beta 3, Понимание леайута в Bootstrap 5
en 5 различных инструментов для мониторинга фронтенда
en Headless: сценарии использования и для чего он нужен




CSS


video Цветовые функции в CSS. Мечты сбываются: смешивание, прозрачность, контраст
en Compat2021: устранение пяти основных проблем совместимости в вебе
en Приручение режимов наложения: `difference` и`exclusion`
en 4 лучших альтернативы Tailwind CSS на 2021 год
en Обработка текста, размещенного поверх изображений в CSS
en Нетипичное использование горизонтальных разделителей
en Анимация подчеркивания
en Как улучшить недостаточно проработанные элементы select
en Эффект фрагментации изображения с помощью масок CSS и кастомных свойств

JavaScript


habr Как создатель node.js сам разочаровался в нем
en Понятный человеку JavaScript: история двух экспертов
en Основные тренды JavaScript, за которыми стоит следить в 2021 году
en JavaScript SEO: лучшие практики и инструменты отладки
en Самый проклятый JavaScript
en Новые функции ES2021, которые вы могли пропустить







Браузеры


habr Google удалил расширение ClearURLs из Chrome Web Store
habr IETF официально прекратил поддержку протоколов TLS 1.0 и 1.1
Релиз Firefox 87
Microsoft, Google и другие объединяют усилия для улучшения совместимости браузеров
В Chrome 90 утверждено использование HTTPS по умолчанию в адресной строке

Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 461 (29 марта 4 апреля 2021)

05.04.2021 00:18:36 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры|Занимательное


Медиа


podcast Новости 512 от CSSSR: Next.js 10.1, Socket.io 4, апрельские security-релизы Node.js, анонс Deno Company, Sublime Text 4
podcast Новости 512 от CSSSR: SvelteKit beta, Bootstrap 5 beta-3, локализация MDN RU, прототип Container Query, атака на PHP
podcast Подкаст Сделайте мне красиво 59 Чем программист отличется от светофора?
podcast Подкаст Фронтенд Юность #180: Конфетти-токены на самописном React-е
podcast Подкаст Веб-стандарты 275. Firefox 87, Container Queries, Compat 2021, SvelteKit, эмоджи, лонгрид и дорога к доступности
video Хард скиллы верстальщика: интервью с Людмилой Мжачих из PayDay, Mail.ru Group
podcast video Подкаст Да как-так то?. Пилотный выпуск


Веб-разработка


habr 7 полезных HTML-атрибутов, о которых вы, возможно, не знаете
habr Вы можете создавать эти элементы, не используя JavaScript
en Как я проанализировал все лицензии в NPM зависимостях
en Использование Python для фронтенда
en Новости платформы: скругленные outlines, GPU-Accelerated анимации в SVG, как определяются CSS переменные





CSS


habr Крутые трюки с переменными CSS
habr Font size бесполезен, давайте это исправим
video CSS Container Queries, или Адаптация по-новому: контейнер вместо вьюпорта
en Container Queries уже на подходе
en Адаптивный веб-дизайн для нового поколения раскладывающихся устройств
en Темный режим за 5 минут, с инвертированными переменными яркости
en Встречайте Utopia: проектирование и разработка с использованием резиновых размеров и масштабирующихся отступов
en Портреты в виде кляксы: развлекаемся с функцией CSS path()
en Несовершенная разработка: стили CSS с перспективой на будущее
en Давайте создадим эффект всплывающего изображения с помощью SVG Clip Path
en Создание волшебной 3D-кнопки
en Совет: стилизуйте псевдоэлементы с помощью Javascript, используя кастомные свойства CSS
en Инструменты для аудита CSS
en Reseter.css современный CSS Reset/Normalizer

JavaScript


habr Экосистема JavaScript: тренды в 2021 году. Всё ли так однозначно?
habr JavaScript: Стек вызовов и магия его размера
habr Умная квартира на JavaScript. От светодиода до распознавания лица в камере домофона
Лучший JavaScript-фреймворк 2021: React илиVue?
en Использование GPU для повышения производительности JavaScript
en [V8 Deep Dives] Случайные мысли о Math.random()






Браузеры


Яндекс.Браузер научился переводить текст на картинках
Браузер Opera получил нативную поддержку ARM-процессора Apple M1


Занимательное


Фальшивая версия плагина jQuery Migrate заразила множество сайтов
Злоумышленники использовали GitHub для криптомайнинга, но сервис до сих пор не закрыл уязвимость
Эксперимент: как запросы в поиске и просмотры на YouTube влияют на ленту от Google с новостями
Офис остался на первом месте: у Amazon и Google не будет полной удалёнки, а сотрудников постепенно вернут в кампусы
Подборка первоапрельских шуток 2021 года
О чём пишут 1 апреля в ИТ-прессе. Лучшие шутки
en Google vs Baidu: ключевые различия в стратегии SEO

Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 462 (5 11 апреля 2021)

12.04.2021 00:19:09 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast Новости 512 от CSSSR: TypeScript 4.3 beta, воркшоп по TDD c React, Husky 6, сравнение Hyperapp с React, RGB и HSL
podcast Новости 512 от CSSSR: Prototype pollution, Tailwind CSS 2.1, Ruby 3.0.1, Cypress 7.0, security-обновления Node.js
podcast Подкаст Веб-стандарты 276. Safari, static в V8, Deno, Sublime Text, HSL и LCH, F1, WordPress, SPA, гидрация и Next.js
podcast Подкаст Фронтенд Юность #181 Эйблизм открытого ПО

Веб-разработка


habr Веб-империя правительства UK: все во имя человека, для блага человека
habr DevTools для чайников
PWA для SSR приложения в 5 строк на Workbox 6.
en Руководство по SEO в Jamstack
en HTML атрибут ping для якорных элементов (ссылок)
en Обновление архитектуры DevTools: перенос DevTools на TypeScript
en Создание переключателя с поддержкой доступности
en Эффект перехода на полноразмерную страницу по клику на превью

CSS


habr 5 плохих CSS практик
en Псевдоклассы CSS :where и :is
en Устранение проблем с темным режимом Gmail с помощью CSS Blend Modes
en Современные обновления CSS для улучшения доступности
en Инспектирование элементов как способ удовлетворить свое любопытство
en Возвращение к CSS Pie таймеру

JavaScript


Спецификация ECMAScript 2021 для JavaScript приближается к финишу
en Шпаргалка по переходу с jQuery на ванильный JavaScript
en Сниппеты кода на ванильном JavaScript
en Создание умной панели навигации на ванильном JavaScript
en Лечебная сила JavaScript
en 5 способов предотвратить внедрение кода в JavaScript и Node.js
en Замена let на const
en JS классы это не просто синтаксический сахар







Браузеры


habr Шпион, выйди вон: что делают браузеры после установки?
Google Chrome заблокировал HTTP, HTTPS и FTP-доступы на порте 10080 из-за хакерской угрозы
10 малоизвестных возможностей браузера Google Chrome

Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 463 (12 18 апреля 2021)

19.04.2021 00:19:54 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast Новости 512 от CSSSR: Chrome 90, Deno 1.9, анализ производительности JS, сборщики, верстка писем, pnpm 6, ESLint 7.24.0
podcast Подкаст proConf #92: GatsbyConf 2021
podcast Подкаст Сделайте мне красиво 60 Единственный фронтендер, который откладывает яйца
podcast Подкаст Фронтенд Юность #183: Путь от идеи до популярного OpenSource проекта
podcast Подкаст Да как так-то?. Выпуск 2: Тимлиды, проектные менеджеры, тестировщики кто все эти люди?

Веб-разработка


habr Малоизвестные, но крутые атрибуты в HTML
habr Микрофронтенды: разделяй и властвуй
en Полное руководство по созданию шаблонов HTML-писем
en Практическая доступность, часть 2: дайте имя (почти) всему
en Новости платформы: Использование :focus-visible, новый шрифт BBC, Declarative Shadow DOMs, A11Y иплейсхолдеры
en Медленно и осторожно: конвертация всего интерфейса Sentry на TypeScript
en Напряжение между Wix и WordPress растет




CSS


habr Нестандартные шрифты: как подключить и оптимизировать
habr Какие CSS-генераторы можно использовать в 2021 году
habr Пользовательские CSS-переменные, инверсия светлоты цветов и создание тёмной темы за 5 минут
habr CSS: работа с текстом на изображениях
Tailwind CSS: to use, или not to use?
en Tailwind UI: теперь с поддержкой React + Vue
en Проблемы с Overflow в CSS
en Как подружить стили с Fullscreen API
en Скажите привет CSS Container Queries
en CSS это строго типизированный язык
en Руководство для новичков по новым утилитам в Bootstrap 5
en Используйте Reseter.css вместо Normalize и Reset.css. Чтобы улучшить кроссбраузерность.


JavaScript


habr Типобезопасность в JavaScript: Flow и TypeScript
habr Работа с датой и часовыми поясами в JavaScript
en Изменение размера изображения в зависимости от контета с помощью JavaScript
en Работа со строками в современном JavaScript
en Генераторы JavaScript: превосходный async/await
en Другой подход к архитектуре фронтенда





Браузеры


habr Вышел Chrome 90
Включение поддержки HTTP/3 в Firefox намечено на конец мая
В Firefox 90 будет удалён код, обеспечивающий поддержку FTP
Разработчики Vivaldi и Brave отказались использовать FLoC от Google, призванный заменить сторонние cookie
В Microsoft Edge появился специальный детский режим
en В Firefox Nightly и Beta появилась поддержка QUIC и HTTP / 3
en WebKit: Представляем CSS Grid Inspector

Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 464 (19 25 апреля 2021)

26.04.2021 00:06:32 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры|Занимательное|


Медиа


podcast Callback Hell записи аудио-стримов о технологиях и не только от CSSSR
podcast Подкаст Фронтенд Юность #182: Не нужно платить разработчикам 200-300к
podcast Новости 512 от CSSSR: релиз Node.js 16 и Firefox 88, проектирование приложения с TypeScript и ООП, взгляд на Container Query, доклады с Я.Субботника.
podcast Новости 512 от CSSSR: Микрофронтенды в Delivery Club, JS-классы, состояние JS-фреймворков и стейт-менеджеров, минусы Dart
video IT-дебаты: JavaScript-программист vs фронтенд-разработчик
habr video Frontend Meetup 20/04
video Я.Субботник по разработке интерфейсов 2021


Веб-разработка


habr CORS для чайников: история возникновения, как устроен и оптимальные методы работы
habr Как создают и поддерживают веб-страницы tinkoff.ru
habr Трёхпроходный алгоритм рефакторинга Front End
en Что нового в DevTools (Chrome 91)
en Независимые компоненты: новые строительные блоки для веба
en Как я создал свой блог



CSS


habr Полное руководство по CSS Flex + опыт использования
CSS-нестинг больше, чем сахар
en Руководство по новым современным псевдо-селекторам CSS
en Создание (и потенциальные преимущества) CSS-шрифта
en Как добавить двойную границу к SVGShapes
en Начало работы с CSS Custom Properties
en TailwindCSS: добавляет сложности, ничего не делает.
en Работа на ошибками гибкой типографии, базирующихся на вьюпорте в Safari


JavaScript


habr Основы управления памятью в JavaScript: как это работает и какие проблемы могут возникнуть
Выпуск серверной JavaScript-платформы Node.js 16.0
en Чудесный мир Javascript бандлеров
en Улучшите управление состоянием в вашем фронтенде с помощью view models
en Шаблон для свойства отложенной загрузки в JavaScript
en Полное руководство по инкрементной статической регенерации (ISR) с Next.js
en Топ-5 самых популярных вопросов о JavaScript на Stack Overflow
en Руководство по MobX
en Понимание Array Reduce в JavaScript





Браузеры


В Firefox 88 молча удалён пункт контекстного меню Page Info
Apple, Microsoft, Opera и другие разработчики не горят желанием поддерживать технологию Google FLoC
Релиз Firefox 88
В Microsoft Edge тестируется новый режим производительности с иным принципом работы спящих вкладок


Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 465 (26 2 мая 2021)

02.05.2021 22:08:33 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript


Медиа


podcast Подкаст proConf #94: GraphQL Galaxy
podcast Новости 512 от CSSSR: Chrome 91 Beta, postcss-easy-z, tree-shakeable библиотеки, гайды по кастомным CSS-свойствам и CLS
podcast Подкаст Callback Hell: Падение последнего оплота Dart, визуальные ЯП на примере Enso, Lucy DSL для стейт-машин
podcast Подкаст Фронтенд Юность#184: Матрица для мешка с картошкой


Веб-разработка


История фронтенда: JavaScript как отражение новой эпохи
habr HTMHell адовая разметка
habr HTML-теги и атрибуты, о которых вы, возможно, не знали
habr Адаптивный дизайн как антипаттерн
en Скромный элемент img и Core Web Vitals
en Как реализовать выбор дейстий для выделенного текста с помощью SelectionAPI




CSS


habr Примеры применения переменных CSS на практике
habr Контейнерные запросы в CSS
habr Как обеспечить глассморфизм с помощью HTML и CSS
VDS (value definition syntax)
en fit-content и fit-content()
en Полное руководство по Custom Properties
en Первый взгляд на CQFill, полифилл для CSS Container Queries
en Изучение color-contrast() в первый раз
en GPT-3 и CSS-фреймворки
en Понимание easing-функций для анимации и переходов в CSS
en Новые возможности WebKit в Safari 14.1 (Flexbox Gap, Date & Time Inputs, CSS Individual Transform Properties)

JavaScript


habr Целительная сила JavaScript
habr Человеко-читаемый JavaScript: история о двух экспертах
Принцип мозаики, или Как мы сделали JavaScript по-настоящему модульным
en Fower утилитарная CSS in JS библиотека для быстрой разработки интерфейсов.
en Клиентские шаблоны API, о которых должен знать каждый разработчик фронтенда








Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 466 (3 9 мая 2021)

10.05.2021 00:07:48 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript


Медиа


podcast Подкаст 'Callback Hell': Убийцы вебпака, Microsoft + Bytecode Alliance, удалёнка
podcast Новости 512 от CSSSR: Bootstrap 5, V8 9.1, дженерики в TypeScript, RxJS в Angular, e2e-тесты с Cypress, баг в Safari 14.1
podcast Новости 512 от CSSSR: История фронтенда ч.2, Safari 14.1, CORS, Cookie Store API, Next.js 10.2, RxJS 7, Google I/O 2021
podcast Подкаст Веб-стандарты 280. Safari 14.1, гэпы во флексах, история JS, мозаичный JS, кому нужны алгоритмы
video Видеокаст Front-end. Вопросы на собеседовании #1
podcast video Подкаст Pro Conf #95: HollyJS Moscow 2020

Веб-разработка


habr Вышел Bootstrap 5: оцениваем 7 главных нововведений
habr Почему стоит использовать тег <picture> вместо <img>
habr Базовая структура HTML-документа с объяснением каждой строчки
habr HTML трюки
en Эволюция Jamstack
en Как мы используем веб-компоненты на GitHub
en Аудит дизайн-систем на предмет доступности
en Ускорение процесса разработки с помощью Bootstrap 5
en Как мы ускорили трассировку стека Chrome DevTools в 10 раз




CSS


en Состояние кроссбраузерной разработки CSS
en Container Queries: разъяснения и предложения
en Два варианта использования кастомных свойств
en Полное руководство по веб-шрифтам в шаблонах писем
en Является ли CSS языком программирования?
en CSS Hell Сборник распространенных ошибок в CSS и способы их исправления
en Текст размером 16 пикселей или больше предотвращает масштабирование формы в iOS
en Fluid typography Создавайте текст, масштабируемый в соответствии с размером окна, чтобы заголовки отлично смотрелись на любом экране.
en Вендорные префиксы мертвы?
en Компиляция CSS по запросу с помощью последней версии компилятора Tailwind


JavaScript


habr Как я написал браузерный 3D FPS шутер на Three.js, Vue и Blender
Кастомные типы данных в TypeScript: валидация на этапе компиляции
en Возможны ли 0kb JavaScript в вашем будущем?
en Vue Composition API против React Hooks основная разница
en Создайте трекер спутников с нуля 30-ю строками JavaScript кода







Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 467 (10 16 мая 2021)

17.05.2021 00:12:32 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast Подкаст Веб-стандарты 281. SpiderMonkey 25 лет, Safari TP, Bootstrap 5, Гитхаб, префиксы, монорепы и свой git в Яндексе
podcast Подкаст Фронтенд Юность #186. Утюжить веб. В гостях создатель и главный редактор Smashing Magazine Виталий Фридман.
video Видеокаст Front-end. Вопросы на собеседовании #2
video Нужен ли джуну идеальный код: интервью с Вадимом Макеевым
podcast video Подкаст Да как так-то?. Выпуск 4: из филолога-япониста во фронтенд на фрилансе

Веб-разработка


Солидные фронтенды: мониторинг
en Регистрация обработчика протокола URL для PWA
en Различия между WebSockets и Socket.IO
habr Переход к Meta GSAP: поиски идеальной бесконечной прокрутки




CSS


habr Выявление устройств с сенсорными экранами на чистом CSS
habr Венец эволюции CSS-in-JS уже здесь: полностью типизированные стили без рантайма в vanilla-extract
habr Сравнение производительности CSS и CSS-in-JS в реальном мире
habr Инструменты для аудита CSS
Родительский селектор :has() в реальность!


en Дизайн для чтения: советы по оптимизации контента для режимов чтения и приложений-читалок
en Продвинутая CSS-анимация с использованием cubic-bezier()
en aspect-ratio и grid
en Создание Stylesheet Feature Flags с помощью Sass!default
en Плавная прокрутка Sticky ScrollSpy Navigation с фиксированным фоном на CSS
en Взгляд на CSS Tailwind

JavaScript


habr Отслеживание и визуализация положения МКС с помощью 30 строк JavaScript-кода
habr Шпаргалка по JS-методам для работы с DOM
habr Паттерны отложенной инициализации свойств объектов в JavaScript
habr Я выпустил Grafar JS-библиотеку для визуализации
en 7 шагов для безопасного JavaScript в 2021 году
en Современный Javascript: все, что вы пропустили за последние 10 лет (ECMAScript 2020)
en Создайте тетрис с помощью современного JavaScript








Браузеры


en Использование обработчиков пользовательских протоколов для кросс-браузерного отслеживания в Tor, Safari, Chrome и Firefox
Идентификация через анализ внешних обработчиков протоколов в браузере

Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 468 (17 23 мая 2021)

24.05.2021 00:09:13 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast Подкаст Веб-стандарты #282: Rome, CloudFront Functions, кроссбраузерность, has() и другой современный CSS, мониторинг, GDE
podcast Подкаст Фронтенд Юность #187: Bootstrap круче чем все сраные фреймворки
podcast Подкаст Callback Hell: Производительность CSS-in-JS, языки логического программирования, ООП в современном фронтенде
podcast Новости 512 от CSSSR: Angular 12, Deno 1.10, мониторинг, тестирование UI, :has(), курс по git, Rome + $, TypeScript 4.3 RC
podcast Подкаст Callback Hell Поддержка нескольких мажорных версий, венчурный капитал в Open Source и возвращение тонкого клиента
podcast Подкаст proConf #96: DeveloperWeek 2020
podcast video Подкаст Цинковый Прод #113: Сайт сына маминой подруги

Веб-разработка


W3C представил черновой вариант стандарта WebGPU
en Google AMP мертв! AMP-страницы больше не пользуются приоритетом в поиске Google
en Incremental Static Regeneration: создавайте статические сайты понемногу
en Тестирование фронтенд-приложений что, где, как?





CSS


habr Трюки CSS, которые сделают из вас ниндзя верстки
habr Взгляд на Tailwind CSS
en Новая отзывчивость: веб-дизайн в мире компонентов
en Нет, утилитарные классы это не то же самое, что инлайн стили
en Как создать неоновый текст с помощью CSS
en Как стилизовать любое поле ввода советы и методы
en 82% разработчиков неправильно проходят этот трехстрочный тест по CSS
en Learn CSS Постоянно обновляемый курс CSS и справочник для повышения вашего уровня знаний в области стилизации веба
en aspect-ratio

JavaScript


habr Швейцарский нож отладки JavaScript
habr Трасси что? Доклад Яндекса
en DOM Events изучение системы событий DOM с помощью визуального исследования
en ES12 сделает вашу жизнь проще
en Справочник по массивам JavaScript методы работы с JS-массивами с примерами
en Двухмерные оптические демки в Javascript
en JavaScript API для распознавания людей и ботов в Chrome







Браузеры


habr Microsoft прекратит поддержку приложения Internet Explorer 11 в Windows 10 с июня 2022 года
habr Кросс-браузерный трекинг на основе перебора обработчика внешних протоколов
В Chrome экспериментируют с поддержкой RSS, чисткой User-Agent и автосменой паролей
Компания Mozilla представила режим строгой изоляции сайтов для Firefox
Выпуск перенастраиваемого web-браузера Nyxt 2.0.0

Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 469 (24 30 мая 2021)

31.05.2021 00:23:47 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast Подкаст Веб-стандарты 83. Sublime Text 4, Sass, Svelte после React, Container Queries, Learn CSS, Google I/O, новые GDE
podcast Подкаст proConf #97: JavaScript for WordPress 2020
podcast Подкаст Callback Hell: Sublime Text 4 и другие редакторы, проблемы написания читаемого кода, завершение эпохи IE
podcast Новости 512 от CSSSR: Chrome 91, TypeScript 4.3, Server-Sent Events API, logux и logux/state, postTask, Parcel 2 beta 3
podcast Новости 512 от CSSSR: Sublime Text 4, PostCSS 8.3, ненадежность TypeScript, Angular DevTools, WebContainers, Google I/O 21
podcast Пилотный выпуск подкаста Goose & Duck: Babel, деньги, два гуся

Веб-разработка


habr Самая серьёзная проблема HTML? Разработчики, разработчики, разработчики
habr Использование веб-компонентов при работе над GitHub
habr Наиболее полное руководство по практическому использованию Web Speech API
en Эволюция и новое определение Jamstack
en 10 вариантов клиентских хранилищ и когда их использовать
en Нарушаете ли вы патент, публикуя PWA?
en Создайте эффект плавного наведения с помощью GSAP и SVG



CSS


habr HTML и CSS ошибки, ухудшающие UX
en Тщательный анализ CSS-in-JS
en CSS Container Queries для дизайнеров
en 25 лет CSS
en CSS Container Queries: примеры использования и стратегии миграции
en Новый способ уменьшить влияние загрузки шрифтов: дескрипторы шрифтов в CSS

JavaScript


habr Карманная книга по TypeScript. Часть 1. Основы, Часть 2. Типы на каждый день
habr 3 способа визуального извлечения данных с помощью JavaScript
en Sparkplug неоптимизирующий компилятор JavaScript
en Новые стандарты доступа к оборудованию устройств с использованием JavaScript
en 7 инструментов, трансформирующих JavaScript-разработку
en Введение в Clio lang: несложная реализация производительного critical js






Браузеры


habr Mozilla примет Manifest v3 для дополнений Firefox, но без мер против блокировщиков рекламы
Релиз Chrome 91
en Призрак Google Reader находит свой путь в новой сборке Chrome Canary

Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Проблемы рендера 7-и тысяч элементов на Vuetify

03.06.2021 08:11:45 | Автор: admin

Предисловие

На момент написания статьи я готовился к диплому и писал дипломный проект для нужд Московского Политеха. Моя задача - перенести существующий функционал из PHP-таблицы во что-то современное с кучей проверок, после чего дополнить данный функционал. Движок - Nuxt, материал-фреймворк: Vuetify.

После написания первичного кода, я, довольный, окинул взглядом свою таблицу и пошел спать. На следующий день мне предстояло импортировать 150+ проектов заказчика в свою таблицу. После импорта, я удивился тому, что браузер повис. Ну, бывает, просто переоткрою вкладку. Не помогло. Я впервые столкнулся с проблемой, что я рендерю слишком много, как для движка, так и для самого браузера. Пришлось начать думать.

Первые попытки

Что делает разработчик, когда сталкивается с проблемой? Идет гуглить. Это было первое, что я сделал. Как оказалось, проблема медленного рендера таблицы Vuetify встречается с куда меньшим числом элементов, чем у меня. Что советуют:

  • Рендерить элементы по частям через setInterval

  • Ставить условие, чтобы не рендерить элементы, пока не сработает хук жизненного цикла mounted()

  • Использовать v-lazy для последовательной отрисовки

При этом было предложение использовать компонент Virtual Scroller, позволяющий отрисовывать элементы по мере скролла, а предыдущие разрендеривать. Но этот компонент Vuetify не работает с таблицами Vuetify -_-

С "радостью" прочитав, что в Vuetify 3 (релиз через ~полгода) производительность улучшится на 50%, я стал пробовать решения. Рендер элементов по частям ничего не дал, так как на условном тысячном элементе отрисовка начинала лагать, а к семи тысячам снова всё висло. Рендер элементов на mounted не дал вообще ничего, всё зависало, но зато после того, как страница загрузится (эээ, ура?). v-lazy хоть и рендерился быстрее, но рендерить 14 тысяч компонентов (Vuetify и Transition от Vue) тоже грустное занятие.

Даже после того, как элементы отрендериваются, скроллить по таблице невыносимо. Элементов много, логики много, браузер просто не выдерживает такого. Сдача всего этого стояла на следующий день. Нужно было выкручиваться, учитывая опыт StackOverflow, который хоть мне и не помог, но натолкнул на то, что делать на следующем шаге.

ТаблицаТаблица

Решение 1. Intersection Observer

Итак, что мы имеем. v-lazy отрисовывать невозможно, это 14 тысяч компонентов. Vuetify Virtual Scroller не поддерживается в Vuetify Data Table из-за его структуры. Выходит, нужно писать свою реализацию. Кто умеет определять, докрутил ли пользователь до элемента? Intersection Observer. Internet Explorer нам не нужен, так что можем приступать.

Первая логичная попытка: использовать директиву v-intersect от самого Vuetify. И 7 тысяч директив также привели к длительному рендеру страницы =(. Значит, выбора нет и придется работать руками.

mounted() {  //Цепляем его на таблицу с overflow: auto  //Требуемая видимость элемента для триггера: 10%this.observer = new IntersectionObserver(this.handleObserve, { root: this.$refs.table as any, threshold: 0.1 });//Почему нельзя повесить observe на все нужные элементы? Ну ладноfor (const element of Array.from(document.querySelectorAll('#intersectionElement'))) {this.observer.observe(element);}},

Теперь взглянем на сам handleObserve:

async handleObserve(entries: IntersectionObserverEntry[]) {const parsedEntries = entries.map(entry => {const target = entry.target as HTMLElement;  //Предварительно задали data-атрибутыconst project = +(target.dataset.projectId || '0');const speciality = +(target.dataset.specialityId || '0');return {          isIntersecting: entry.isIntersecting,          project,          speciality,};    });//Чтобы точно было реактивно    this.$set(this, 'observing', [      //Не добавляем дубликаты      ...parsedEntries.filter(x => x.isIntersecting && !this.observing.some(y => y.project === x.project && y.speciality === x.speciality)),      //Убираем пропавшие      ...this.observing.filter(entry => !parsedEntries.some(x => !x.isIntersecting && x.project === entry.project && x.speciality === entry.speciality)),     ]);//Иначе функция стриггерится несколько раз     Array.from(document.querySelectorAll('#intersectionElement')).forEach((target) => this.observer?.unobserve(target));     //Даем Vuetify перерендериться await this.$nextTick(); //Ждем 300мс, чтобы не триггернуть лишний раз, отрисовка тоже грузит браузер     await new Promise((resolve) => setTimeout(resolve, 500));     //Вновь обсервим элементы     Array.from(document.querySelectorAll('#intersectionElement'))          .forEach((target) => this.observer?.observe(target));},

Итак, мы имеем 7 тысяч элементов, на которых смотрит наш Intersection Observer. Есть переменная observing, в которой содержатся все элементы с projectId и specialityId, по которым мы можем определять, нужно ли показывать нужный элемент в таблице. Осталось всего-лишь повесить v-if на нужный нам элемент и отрисовывать вместо него какую-нибудь заглушку. Ура!

 <template #[`item.speciality-${speciality.id}`]="{item, headers}" v-for="speciality in getSpecialities()"><div id="intersectionElement" :data-project-id="item.id" :data-speciality-id="speciality.id"><ranking-projects-table-itemv-if="observing.some(x => x.project === item.id && x.speciality === speciality.id)":speciality="speciality":project="item"/><template v-else>Загрузка...</template></div></template>

А на саму таблицу вешаем v-once. Таблице будет запрещено менять свой рендер без $forceUpdate. Не очень красивое решение, но Vuetify непонятно чем занимается при скролле, запретим ему это делать.

<v-data-table v-bind="getTableSettings()"   v-once   :items="projects"   @update:expanded="$forceUpdate()">

Итоги:

  • Рендер занимает около секунды

  • Элементы разрендериваются вне их зоны видимости

  • Идет ререндер на каждое действие внутри таблицы

Но при этом работать с большой таблицей невыносимо. 7 тысяч событий на каждой клетке таблицы, бесконечный набор сообщений "Загрузка...". Таблица была создана, чтобы выполнять действия быстро, и с этим решением мы релизнулись.

Загрузка...Загрузка...

Однако было ясно (и заказчик это подчеркнул), что работать с таблицей тяжело. Нужно было что-то решать, но на момент тогда я зашел в тупик: Vuetify Data Table имеет ужасные проблемы с производительностью, которые признают даже разработчики фреймворка.

Не переписывать же мне их таблицу и создавать свой аналог?

Решение 2. Переписать таблицу и создать свой аналог

Решение, приведённое выше, может подойти для таблиц с куда меньшим количеством компонентов, содержимого и прочего. Но у нас с нашей логикой это позволило работать с таблицей, пренебрегая комфортом пользователей. Попробуем добавить немного комфорта.

Что мы поняли из первого решения:

  1. Таблицы Vuetify лагучий отстой

  2. Если показывать элементы только когда пользователь их увидит, рендер будет быстрее

  3. Нужно как-то сократить время на первичную отрисовку, чтобы пользователь не видел бесконечную "Загрузку"

  4. Можно заставить Intersection Observer реагировать чаще, чтобы рендерить элементы по мере скролла, а не когда скролл остановится или среагирует событие (300мс задержка)

Возвращаемся к Virtual Scroller. Он не работает в таблице Vuetify, так? А что если мы напишем свою таблицу с блекджеком и display: grid? Зачем-то же его придумали.

Что нужно для Virtual Scroller? Фиксированная высота каждого элемента. Что нужно для Grid'ов? Фиксированная ширина каждого элемента и информация о количестве элементов. Бахнем CSS-переменные для последующего использования в CSS:

<div class="ranking-table" :style="{    '--projects-count': getSettings().projectsCount,    '--specialities-count': getSettings().specialitiesCount,    '--first-column-width': `${getSettings().firstColumnWidth}px`,    '--others-columns-width': `${getSettings().othersColumnsWidth}px`,    '--cell-width': `${getSettings().firstColumnWidth + getSettings().othersColumnsWidth * getSettings().specialitiesCount}px`,    '--item-height': `${getSettings().itemHeight}px`  }">

Потом пишем гриды по типу аля

display: grid;grid-template-columns:var(--first-column-width)repeat(var(--specialities-count), var(--others-columns-width));

И так далее для элементов внутри. Класс! А еще мы, зная ширину и количество элементов, можем задать ширину нашему Virtual Scroller (он сам по умолчанию туповат), а заодно и всем нашим элементам, чтобы не дай боже кто-то вышел за доступные ему границы

.ranking-table_v2__scroll::v-deep {.v-virtual-scroll {&__container, &__item, .ranking-table_v2__project {    width: var(--cell-width);}}}

Примечание: если вы сделаете просто <style>без scoped и решите, что будет хорошей идеей редактировать глобальные стили вне окружения компонента, то у меня для вас плохие новости: лучше так не делать вне каких-то App.vue, и стоит ознакомиться с тем, что это за v-deep.

Поехали: добавляем Virtual Scroller, пихаем в него проекты, после чего выводим последние. Сразу скажу: поддержки Expandable Items у нас тут нет, я вынес информацию о проекте во всплывающее окно. Жаль, конечно, что нельзя это отображать прямо в таблице, как делал Vuetify, но тогда придется помучаться с их скроллером, а он и так не особо хорошо работает. В общем, к делу:

<v-virtual-scroll   class="ranking-table_v2__scroll"   :height="getSettings().commonHeight":item-height="getSettings().itemHeight":items="projects"><template #default="{item}"><div class="ranking-table_v2__project" :key="item.id"><!-- ... -->

Итого: на страницу, допустим, помещается 6 проектов (высота же у всех максимальная по факту), итого рендерится 6 строк + шапка. Колонок 50. Итого рендерится около 300 сложных компонентов. А вот это уже задача не уровня мстителей, 300 мы рендерить умеем.

Вспоминаем про лучший инструмент всех времен и народов v-lazy: он позволяет отрендерить элемент один раз и потом не перерендеривать его. Раньше мы пытались отрендерить 14 тысяч компонентов, сейчас 600 простых. Ну и оборачиваем все наши колонки (кроме шапки) в v-lazy. При горизонтальном скролле элементы подгружаются, и остаются отрендеренными до тех пор, пока сама строка таблицы не пропадет из области видимости и не разрендерится.

 <v-lazy class="ranking-table_v2__item ranking-table_v2__item--speciality"v-for="(speciality, index) in specialities":key="speciality.id">   <!-- Колонка в строке таблицы --></v-lazy>

Видео с разницей, можно сравнить:

Плюсы такого решения:

  • Элементы перестали скакать как ненормальные

  • Нет нужды писать свою реализацию логики рендера/отрендера

  • Можем отказаться от v-once и всяких принудительных ререндеров аля $forceUpdate

  • Куда больше гибкости при верстке таблицы

Минусы:

  • Если используются выпадающие элементы (expand), нужно писать свою реализацию

  • Нужно верстать таблицу самому, без инструментов движка

  • Нет средств сортировки/группировки и прочего (мне было не нужно)

  • Для того, чтобы шапка всегда была отрендеренной, но скроллилась вместе со всеми, мне пришлось отслеживать событие Scroll и подгонять скролл шапки к скроллу основных элементов

  • Чтобы отображать таблицу в нужном мне проценте от высоты страницы, мне пришлось смотреть на window.innerHeight и применять его в CSS переменных и в значении высоты у VirtualScroll

Несмотря на то, что минусов визуально больше, значительное улучшение UX и скорости отрисовки значительно компенсирует затраты времени и изучения инструментов для реализации этой штуки.

Заключение

Я привел 2 варианта решения проблем с производительностью отрисовки таблиц в Vuetify. Оба решения достаточно сложные, просто сложные в разных аспектах. Оба так или иначе помогли мне решить мою проблему, и у обоих есть свои ограничения. Например, второй вариант мне нравится больше, но отсутствие средств Vuetify (сортировка/группировка и пр.) может оттолкнуть тех, кому это позарез нужно.

Зачем я это пишу? Я перегуглил половину интернета и не нашел простых решений своей проблемы. Наверное, как раз таки потому, что этих решений нет. Эти таблицы не предназначались для сложной логики, даже в условных гугловских таблицах элементов отрисовывается меньше, потому что там буквально один инпут, изредка с каким-то контентом, здесь же - гигантская куча логики.

И да: я пользовался дебагером производительности от Vue и смотрел, кто её потребляет. Зачастую там был буквально один-два компонента, и, заменив их на какой-то другой с похожей логикой, проблема не решалась - дело в их количестве, а не сложности (не считая таблицу Vuetify - там передается множество props'ов из компонента в компонент).

Надеюсь, что приведенные мной варианты натолкнут кого-то на решение его проблемы, а кто-то просто узнает что-то новое =). Будем вместе ждать стабильный Vue 3 со всей его экосистемой, как минимум Nuxt 3. Что-то обещают множество улучшений, может, часть костылей из этой статьи даже пропадет.

Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 470 (1 6 июня 2021)

07.06.2021 00:10:32 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast video Подкаст Goose&Duck #1 Ржавеющий JavaScript
podcast CSSSR Callback Hell: Rescript, мысли пьяного Senior-разработчика, слежка за сотрудниками
podcast Новости 512 от CSSSR: Server-Sent Events: ограничения, поддержка Node.js-проектов, плагины для VSCode, 12 лет Node.js
podcast Подкаст Фронтенд Юность #189 Рон-дом-дом

Веб-разработка


habr С помощью перехода на микросервис мы ускорили бизнес-процесс в 60 раз
en Создание нескольких прогрессивных веб-приложений в одном домене
en Тестирование фронтенда для всех
en Разрушение мифов: Jamstack не может обрабатывать динамический контент
en История веба: часть 1
en Некоторые из лучших пасхальных яиц, спрятанных на сайтах в Интернете





CSS


habr 25 лет CSS
Нативная валидация ввода в CSS
en CSS in SVG in CSS: добавление конфетти в дизайн-систему Stack Overflow
en Новые функциональные селекторы псевдоклассов CSS: is() и: where()
en Тригонометрия в CSS и JavaScript: Введение в тригонометрию
en Тригонометрия в CSS и JavaScript: творческий подход с помощью тригонометрических функций
en The CSS Layout Generator визуальный инструмент для создания компонентов лейаута на CSS Grid
en Inherit, initial, unset, revert
en Шестиугольники и не только: гибкие, отзывчивые сеточные шаблоны, без медиа-запросов

JavaScript


habr Управление зависимостями в Node.js
habr Как мы потерпели неудачу, а затем преуспели в переходе на TypeScript
habr Создание нейронной сети Хопфилда на JavaScript
ES12 сделает вашу жизнь проще!
en Обеспечение быстрой работы JavaScript в WebAssembly
en Еще одна альтернатива Javascript: ReScript
en Взгляд на компиляцию в JavaScript-фреймворках






Браузеры


habr Firefox 89 обновил интерфейс браузера
Релиз Firefox 89 с переработанным интерфейсом
Mozilla, Google, Apple и Microsoft объединили усилия в стандартизации платформы для браузерных дополнений
en Что нового в DevTools (Chrome 92)

Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 472 (7 13 июня 2021)

14.06.2021 00:15:08 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript|Браузеры


Медиа


podcast Новости 512 от CSSSR: Firefox 89, Safari 15 Beta, Jest 27, цикл статей о работе браузера, разработка базовых компонентов, обзорная статья о тестировании фронтенда и анонс WebExtensions Community Group.
podcast Подкаст Веб-стандарты #285: Бета Chrome92, Firefox89, якоря ирасширения, TeamCity, JSвнутри WASM, TypeScript4.3
podcast Подкаст Фронтенд Юность #190: Как подступиться к старому проекту и не сесть на кулак
podcast Новости 512 от CSSSR: React 18, Vue 3.1, анонс ESLint 8, курсы от CSSSR, :is(), where() и :has(), как прилёг Интернет
podcast Подкаст Callback Hell: Сервисы Google с плохими Web Vitals, шеринг логики между фронтом и бэком, документация на проектах


Веб-разработка


habr Будущее веба: станет ли рендеринг в <canvas> заменой DOM?
en Правильный тег для работы: почему следует использовать семантический HTML
en 5 проблем фронтенда, которые нельзя игнорировать





CSS


habr Выкладка нетрадиционной ориентации
en Полное руководство по CSS Grid с шпаргалкой
en Системные цвета CSS
en CSS определяет значения цвета, соответствующие системным настройкам.
en Media Queries во времена @container
en Давайте узнаем об Aspect Ratio в CSS
en CSS size-adjust для @font-face
en Равные столбцы с Flexbox: это сложнее, чем вы думаете
en Эксперимент с сортируемыми мультиколоночными таблицами
en Знакомьтесь с :has: нативный CSS селектор
en Рог изобилия ContainerQueries
en Создание правил для font-size CSS и создание Fluid Type Scale

JavaScript


habr Как я ускорил движок на 13%
habr Прогнозирование временных рядов на JS: анализ данных для самых маленьких фронтендеров
habr Sparkplug неоптимизирующий компилятор JavaScript в подробностях
en Как создать фулстек-приложение с помощью Supabase и Next.js
en Реализация приватных полей в JavaScript
en Forever Functional: Мемоизация промисов
en Как реализовать принципы SOLID в JavaScript
en Автоматизируйте форматирование и исправление JavaScript кода с помощью Prettier и ESLint
en Современный JavaScript
en Выходя за рамки ESLint: обзор статического анализа в JavaScript
en Доберенные типы API для безопасности JavaScript DOM
en Как создать NFT с помощью JavaScript
en Rust с точки зрения JavaScript





Браузеры


habr Vivaldi 4.0 Первое приближение
Google признал неудачным эксперимент с показом только домена в адресной строке Chrome
en Возможности WebKit в Safari, продемонстрированные на WWDC21


Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Дайджест свежих материалов из мира фронтенда за последнюю неделю 473 (14 20 июня 2021)

21.06.2021 00:15:47 | Автор: admin
Предлагаем вашему вниманию подборку с ссылками на новые материалы из области фронтенда и около него.


Медиа|Веб-разработка|CSS|JavaScript


Медиа


podcast Подкаст Веб-стандарты 286: Высокопроизводительное хранилище для вашего приложения: Storage Foundation API
podcast Подкаст Callback Hell: Микрофронтенды и Module Federation, почему компании боятся открывать свой код, игровая выставка E3
podcast Новости 512 от CSSSR: Canvas-рендеринг, Lighthouse 8, пропорции в CSS, PHP 8.1 alpha, Next.js 11, Линус и антипрививочник
podcast video Подкаст Ленивый фронтендер #2 Kaiwa Show | Как сохранить любовь к веб-разработке
podcast Подкаст Фронтенд Юность #191: HR'ы немножко осатанели


Веб-разработка


habr <img>. Доклад Яндекса
habr Темизация. История, причины, реализация
habr DIV должен уйти: улучшаем HTML
en Изучение Eleventy с нуля. Бесплатный курс, состоящий из 31 урока
en Как я использовал WAAPI для создания библиотеки анимации
en Десять лет веб-компонентам



CSS


video :has в CSS псевдокласс из будущего на примере карточки новости
en Использование свойства `outline` в качестве схлопывающейся границы
en Идеальные всплывающие подсказки с обрезкой и маскированием CSS
en Оптический размер, скрытая сверхспособность вариативных шрифтов
en Краткое руководство по логическим свойствам CSS
en Застенчивая кнопка стоимостью 8 миллионов долларов
en Создание таблиц с липким верхним и нижним колонтитулами стало немного проще

JavaScript


habr Скрываем номера курьеров и клиентов с помощью key-value хранилища
habr Юмористичный обзор Rust с перспективы JavaScript
en Управление состоянием: двусторонние биндинги и расширенные средства форматирования биндингов
en Что такое букмарклеты? Как использовать JavaScript для создания букмарклета в Chromium и Firefox
en Тестирование использования памяти в JavaScript
en Двойные кавычки против одинарных кавычек против обратных кавычек в JavaScript
en sorting-algos-visualizer Визуализация популярных алгоритмов сортировки: QuickSort, MergeSort, HeapSort, BubbleSort, InsertionSort







Дайджест за прошлую неделю.
Материал подготовили dersmoll и alekskorovin.
Подробнее..

Программа HolyJS нюансы DevTools, минусы GraphQL, инструменты a11y

01.04.2021 14:11:52 | Автор: admin


Осталось меньше месяца до конференции HolyJS (20-23 апреля, онлайн) пора рассказать, что именно там будет. Под катом описания докладов с разбивкой по тематическим блокам. А для начала несколько вопросов для затравки:


  • В чём недостатки GraphQL?
  • Зачем OCaml на фронтенде?
  • Чего вы не знаете о DevTools?
  • Как писать надёжные тесты для Vue?
  • Как сделать свой DSL-язык легко и непринуждённо?
  • Как добиться на дешёвом устройстве плавности дорогого?
  • Как отобразить 100500 метрик и не сойти с ума?
  • Как принести в JS типы ещё радикальнее, чем в TypeScript?

Ответы на всё это и многое другое в докладах.


Оглавление


Инструменты
Производительность
Фреймворки
Языки
Микрофронтенды
Основы
Визуал
Заключение



Инструменты


Всё, что вам нужно DevTools, Виталий Фридман


DevTools очень продвинутый и полезный набор инструментов, но также он может быть устрашающим и непосильным. Давайте исправим это. Виталий пройдёт через десятки полезных и скрытых фич DevTools, советов и приёмов, объяснит пугающий перформанс и сетевые панели, покажет навороченные экспериментальные фичи и процесс дебага для работы с CSS и JS.


Чем хорош спикер: Виталий человек-харизма, не нуждающийся в представлении: зрители предыдущих HolyJS хорошо помнят его доклады. Он известный перфекционист и адвокат современного веба, отличающийся вниманием к каждой детали.


Чем хороша тема: DevTools использует каждый, но там столько подводных камней, что без помощи не разобраться! Возможно, сверхсеньоры и не найдут чего-то прорывного, но даже им будет интересно увидеть flow работы Виталия.




Сделать сайт доступным за 60 секунд, Глафира Жур и Денис Бирюк


Все вокруг говорят о доступности: она нужна, важна и должна быть добавлена в проект на самом раннем этапе. Но никто не говорит, как рядовому фронтенд-разработчику, путающему a11y и 11ty, разобраться в вопросе. Какой пакет подключить в Webpack, чтобы проблемы решались на этапе сборки? Где в консоли найти отчёт об ошибках a11y? И как проверить результат своей работы после деплоя?


Чем хороши спикеры: Два спикера два мира. Глафира эксперт по интерфейсам, Денис любит прикручивать автоматизацию к фронтенд-проектам.


Чем хороша тема: Рассказывает про доступность с точки зрения инженера: что и как можно автоматизировать, какой тулинг позволяет проверять доступность, и всё подобное, что так любят программисты.


Кому будет полезно: Знаете про доступность, но не практикуете, потому что ну это же сложно? Тут расскажут, что конкретно добавить в ваш проект, чтобы пользователям было удобнее.




WebXR в реальной жизни, Роман Пономарев


Дополненная и виртуальная реальности отличные инструменты для демонстрации продукта. А браузер всегда под рукой и позволяет визуализировать что угодно без скачивания приложений. В докладе рассмотрим существующие технологии для работы с AR и VR в вебе, стандарты, лежащие в их основе, а также затронем состояние XR-индустрии в целом, увидим демки и узнаем, как можно продать это всё бизнесу.


Чем хорош спикер: У Романа есть практический опыт в теме. Некоторые зрители также могут знать его по Фронтенд Юности.


Чем хороша тема: Даже в доковидные времена Virtual/Augmented Reality всё больше проникала в нашу жизнь, а уже теперь и подавно. Тем временем спецификация WebXR взрослеет и позволяет делать всё больше.




Make your authentication flow perfect, Anton Nemtsev


Есть очень много сайтов, где нужно зайти, ввести email, указать телефон, подтвердить это все через ввод кода из SMS. Авторизация и регистрация постоянно балансирует между удобно и секьюрно, причем чаще в сторону секьюрно.
Как сделать так, чтобы было и удобно, и секьюрно в докладе Антона Немцева.


Чем хороша тема: Актуальный доклад, который покажет не только как сделать хорошо, но и как это продать. У нас уже были доклады о теоретической, секьюрной части авторизации, мы увидели фидбек о том, что нужно больше практичности, и следуем ему.


Кому будет полезно: Если вы работали с формами авторизации и регистрации, либо вам интересно, как можно сделать их удобными




Воркшоп. GitLab + CI/CD + JavaScript = любовь, Виталий Слободин


Стать передовиком производства можно при помощи настроенных процессов. Это знают все. Но как настроить процессы вопрос сложный, лучше всего начинать с машин, потому что они податливее, чем люди. Первым шагом для настройки непрерывного цикла разработки может стать CI/CD. Цель Виталия показать вам трюки и рецепты для создания CI/CD, чтобы все инженеры были довольны и счастливы.


Чем хорош спикер: Ранее на HolyJS Виталий рассказывал про headless browsers, и доклад приняли отлично: глубоко знать тему ему помог личный опыт разработки PhantomJS. А теперь он работает в GitLab так что снова поговорит о том, к чему сам причастен.


Чем хороша тема: В результате развития подхода DevOps сейчас JS-разработчикам самим приходится думать про то, как их код будет тестироваться и собираться. Виталий обещал дать набор готовых рецептов.




Serverless и Edge Computing на практике, Алексей Тактаров


Две основные проблемы при построении веб-сервисов повысить пропускную способность и снизить время ответа от сервера. Это проблемы, которые обычно решают с помощью наращивания мощностей, горизонтального масштабирования и разнесения дата-центров по континентам. Такой подход надежен, но дорог и требует обслуживания. Лямбда-вычисления и Smart CDN относительно новые и дешевые технологии, призванные решить обе задачи.


В докладе Алексей рассмотрит практическое применение лямбд от Vercel и Cloudflare: проектирование фронтенд-микросервисов, их авторизацию, работу с CDN и особенности кеширования на пограничных (edge) серверах. Будут затронуты архитектурные приемы для построения легко масштабируемых сервисов на примере задач: рендеринг PDF/DOCX-документов, генерация above-the-fold CSS для страниц на лету в Cloudflare Workers, отрисовка OG-картинок.


Чем хорош спикер: Алексей очень опытный спикер и специалист очень широкого спектра: отлично разбирается во фронтенд-разработке, дизайне, бекенде, инфраструктуре, безопасности, оптимизации и много другом. А благодаря стартаперскому бэкграунду понимает, что полезно бизнесу.


Чем хороша тема: Достаточно высокая практическая польза. Много примеров и будет показан реальный опыт. Применение этих знаний можно легко использовать для улучшения архитектуры и скорости проектов многих зрителей. Доклад охватывает достаточно широкую аудиторию подходит практически для любого уровня.




Гиперавтоматизированный пайплайн, или Почему матрица должна победить, Алексей Золотых


Больше всего ошибок происходит по вине человека. Это люди случайно пушат в мастер всякие непотребства, это они в код с пробелами вставляют табы и это они забывают писать ченжлоги.


Но настало время машин. Алексей расскажет о том, как быстро захватить власть в проекте на JavaScript или TypeScript. Как унижать и угнетать разработчиков так, чтобы они при этом оставались эффективными и счастливыми. И как не допустить появления Нео!


Чем хорош спикер: Пишет на JavaScript c 2007 года, давно участвует в HolyJS в качестве участника программного комитета.


Чем хороша тема: В наше девопсовое время вопросы автоматизации пайплайнов всё актуальнее.




Воркшоп: Знакомство с MobX, Назим Гафаров


MobX это простая и эффективная библиотека, но найти хорошие материалы по ней невероятно трудно. Видеоуроки и статьи, да и сама документация MobX слишком перегружены.


В этом воркшопе мы напишем приложение на React и TypeScript, используя MobX для управления состоянием. Начнем с фундаментальных понятий, таких как наблюдаемые объекты, действия и реакции. Вместе с основами глубже рассмотрим варианты решения проблемы окрашивания асинхронных функций. В конце рассмотрим полезные архитектурные паттерны вроде композиции классов и способов внедрения модели.



Производительность


Быстрый и красивый веб на дешевом девайсе с голосовым, пультовым и тач-управлением, Павел Ремизов


Вот реальная инженерная задача: есть SberBox за 3 000 рублей и SberPortal за 30 000. Производительность разная, но написанный на веб-технологиях интерфейс должен работать одинаково плавно. Павел Ремизов расскажет, как решали эту задачу, как ускоряли анимации и как использовали React, как кэшировали статику на девайсах и уменьшали размеры бандла, как отлаживали и измеряли производительность.


Чем хорош спикер: Имеет очень хорошую экспертизу в производительности и в реальных аспектах ее оценки


Чем хороша тема: Реальные устройства, юзкейсы, пользователи никаких коней в вакууме! Происходят реальные эксперименты на в боевых условиях, и их результаты не всегда совпадают с общепринятым мнением.




Оптимизация производительности высоконагруженного поиска на стороне фронтенда, Даниил Гоник, Ян Штефанец


Даниил Гоник и Ян Штефанец работают над редактором наподобие Google Docs.


Они расскажут, как правильно частично отрисовывать контент, что делать, если пользователь быстро скроллит, как подсвечивать нужные области при поиске, и как это применять не только в редакторах, но и на обычных сайтах.


Чем хороши спикеры: Докладчики глубоко и много работали с созданием редактора в вебе, поэтому экспертизы и знания нюансов много.


Кому будет полезно: Если вы реализовывали подсветку элементов на сайте по тексту, если вы работали когда-либо с WYSIWYG




Стабильность React Native-приложения с круглосуточным up time, Евгений Гейманен


Евгений Гейманен расскажет поучительную историю про проблемы производительности одного React Native-приложения. Из нее вы узнаете:


  • Как оптимизировать перерисовку виртуального React DOM;
  • Как правильно мутировать redux-store, коннектить к нему компоненты и описывать селекторы;
  • Как находить проблемные места в Android-версии React Native-приложения;
  • Какими инструментами стоит обзавестись для того, чтобы держать руку на пульсе приложения и начать думать как приложение.

Чем хорош спикер: Евгений Lead Developer в Bolt и ему есть что рассказать как с точки зрения технологий, так и с точки зрения истории


Чем хороша тема: Мониторинг это одна боль. Мониторинг приложений установленных на куче девайсов другая. Это реальный опыт решения некоторого количества проблем производительности на примере React Native приложения




Производительность в полевых условиях, Александр Шушунов


Вы уже прочитали все статьи Эдди Османи. Выучили, как расшифровываются аббревиатуры RAIL, PRPL, LCP и прочие. Свежий перформанс-гайд Фридмана второй месяц висит в открытой вкладке. Но как все это применить к текущему проекту? Как улучшить производительность здесь и сейчас? Александр не знает и не будет давать общие советы зато расскажет, что сделал он. И надеется, что его опыт борьбы за секунды и байты покажется вам интересным.


Чем хороша тема: Сугубо практический взгляд на производительность от человека, повидавшего виды. Будет отличным дополнением к докладу Виталия Фридмана. Причем зачастую хороших результатов можно достичь малой кровью


Кому будет полезно: Если вам интересна тема производительности клиентских приложений. Особенно, если вы никогда специально не работали в этом направлении, и хотите посмотреть на свой текущий проект под другим углом




Фреймворки


А нужен ли нам GraphQL?, Павел Черторогов


Это далеко не первый доклад Павла про GraphQL, так что он уже глубоко в теме, но у него нет слепого фанатизма. Он понимает, что GraphQL это инструмент, а не серебряная пуля. И каждый инструмент удобен под свою категорию задач. Молотком шуруп не закрутишь, отверткой гвоздь не забьешь это практически нереально.


И в этом докладе речь пойдёт как раз о том, каких ситуациях GraphQL нужен (например, когда модели постоянно развиваются и количество полей растет), в каких не нужен (например, когда ведется работа с файлами или другими бинарными данными), и что делать, если ситуация спорная (попадаем в обе категории сразу). Будут разобраны сильные и слабые стороны и найдены разумные компромиссы.




How we built our custom React SSR Engine, Erzhan Torokulov


Эржан Торокулов расскажет, как несколько лет назад в Toptal сделали React Rendering Engine: он уже тогда умел делать ряд вещей, которые теперь знакомы по инструментам вроде Gatsby и Next.js. Компания по-прежнему использует этот инструмент для своего сайта и продолжает его развивать.


Чем хорош спикер: Эржан имеет глубокий опыт не просто применения существующих решений, но и написания больших инструментов для компании.


Чем хороша тема: Никого не удивить сайтом на React, Vue или даже Svelte, и доклады про React стали очередными. Индустрия перестала строить велосипеды и догадалась положить рельсы. Но иногда хочется большего и это такой случай.




Как в GitLab @vue/test-utils обновляли, Илья Климов


За последний год Илья дважды обновлял @vue/test-utils в GitLab и оба раза это заняло огромное количество времени. Для того, чтобы это сделать, ему пришлось отправить более десятка pull-request'ов во @vue/test-utils, обсудить в RFC внутри GitLab возможность создания собственного форка, глубоко разобраться в механизмах реактивности Vue и нещадно воевать за качество кода и тестов как в GitLab, так и в самом @vue/test-utils.


Теперь Илья со зрителями HolyJS будет разбираться в следующих вопросах:


  • где спрятана сложность в тестировании подобных систем;
  • какие ошибки были допущены разработчиками @vue/test-utils и можно ли было их избежать;
  • как магия реактивности усложняет построение надежной системы и как с этим бороться;
  • как писать надежные тесты для Vue.

Доклад будет интересен не только тем, кто использует Vue.js в повседневной разработке, но и всем, кто верит в unit-тестирование как ключ к управляемости любого проекта.





Языки


Свой язык с поддержкой sourcemaps за полчаса, Дмитрий Карловский


DSL (Предметно-ориентированный язык) техника для высокоуровневого описания чего-либо (конфигурации, UI, баз-данных). С помощью DSL можно решить множество проблем бизнеса (и множество создать). Дмитрий поговорит об этой теме, раскроет нюансы sourcemaps и расскажет, как в два счёта реализовать свой язык с их поддержкой. А также как легко и просто трансформировать AST, даже если вы никогда с ним не работали и ничего не знаете про sourcemaps.


Чем хорош спикер: Является автором собственного инструмента для работы с DSL.


Чем хороша тема: DSL недооцененная техника: во многом как раз из-за сложности написания инструментов для исполнения этих самых языков. В данном докладе, будет показан инструмент, который упрощает жизнь.




Зачем OCaml на фронтенде, Дмитрий Коваленко


Возможно, вы слышали о таких технологиях, как ReasonML/Resсript и bucklescript. Всё это OCaml на фронтенде. Но как всё это работает? Как OCaml компилируется в JS? Почему это круто? Всё это вы узнаете в данном докладе.


Чем хорош спикер: Дмитрий один из немногих людей, использующих OCaml на практике.


Чем хороша тема: Про Ocaml много кто слышал, но мало кто представляет, что это такое и чем он замечателен. Если вы хотите разнообразить свои знания мира языков транспилируемых в JS и узнать (а возможно и подсесть на) что-то новое вам сюда.




Strict Types in JavaScript, Виктор Вершанский


Говорят, что JavaScript динамически типизированный. А если хочешь другой типизации, надо использовать что-то вроде TypeScript, который не заставляет писать без ошибок типов в runtime.


Но что, если это не совсем так? Что, если можно у чистого JS заставить его runtime работать с типами по всей строгости и получать связанные с этим ошибки? Если вы думаете, что это невозможно нам есть о чём поговорить.


Чем хороша тема: Такое решение еще никто не представлял, это самый эксклюзивный доклад из всех возможных.


Кому будет полезно: Всем, кто:


  • задумывался про проблему строгой типизации в рантайме
  • устал ловить TypeError в рантайме
  • хочет поломать мозг неординарными решениями



How to outsmart time: Building futuristic JavaScript applications using Temporal, Ujjwal Sharma


Почти 25 лет JS-разработчики страдали каждый раз, когда нужно было что-то сделать со временем и датой. Было сломано много костылей, сорвано много подорожников, и прожжено много стульев. И вот, наконец, будущее наступило: появилось долгожданное Temporal API, которое позволяет работать с временем и датой, как все давно мечтали. Уджвал Шарма покажет, как это всё работает.


Чем хорош спикер: Данный спикер помогает разрабатывать этот стандарт (ТС39 делегат) и внедряет его в браузеры.


Чем хороша тема: Поскольку текущий Date API не слишком хорош, разработчики подключают внешние библиотеки для работы с датами вроде date-fns, moment, luxon и тд. Temporal предложение для самого языка, это будущий стандарт работы с датами.





Микрофронтенды


Микросервисы, которые делаем мы, Олег Сметанин


Олег представит обзор способов проектирования микросервисов, API, использования шаблонов и паттернов взаимодействия; DevOps и тестирования на основе обобщения опыта проектирования и аудирования приложений с микросервисной архитектурой для разных индустрий: финансы, ритейл, ресурсы. Вы узнаете, как использовать TypeScript/NestJS для реализации микросервисов под NodeJS для cloud-native-приложений и как применять TypeScript/React для фронта.


Чем хорош спикер: У спикера большой опыт в построении больших систем с высокой нагрузкой.


Чем хороша тема: Доклад по построению больших микросервисных систем. Обосновываются причины использования тех или иных инструментов. Обзор технической, функциональной, DevOps архитектуры, построение карты микросервисов, организация доставки приложения на примере нескольких крупных проектов.




Микрофронтенды на модулях и веб-компонентах, Юрий Караджов


Микросервисная архитектура постепенно добирается и до фронтенда. В докладе представлен один из способов реализации этой архитектуры с помощью модулей и веб-компонентов, рассмотрены проблемы и преимущества такого подхода.


Чем хорош спикер: У докладчика есть интересное мнение, отчасти затрагивающее вопрос, поднятый Павлом Чертороговым на прошлой HolyJS. Умеет и готов обсуждать архитектурный паттерн с разных неожиданных сторон.


Чем хороша тема: Доклад про лёгкий и прозрачный способ внедрения микрофронтендов. Анализ преимуществ и недостатков. Практическая применимость и подводные камни для популярных фреймворков.




One logic to rule them all, from clot to wire, Владимир Минкин


Презентация концепции Strings API с библиотекой Wire для создания веб и мобильных приложений, которые повторно используют бизнес-логику в языке Dart. Цель этого доклада: продвинуть идеи создания программных систем, состоящих из плагинов отдельных компонентов и блоков, которые можно тестировать индивидуально, визуально и в модульных тестах.


Разделение ответственности это не новая идея и того же можно добиться с помощью уже существующих инструментов. Разница с Wire заключается в том, что он предоставляет простой и семантически красивый API с четким разделением задач на уровне API-библиотеки: общение в одном месте, данные в другом. В докладе будет уделено внимание архитектуре систем визуального ПО и тому, что всё это MVC, а также архитектуре Flux.


Чем хорош спикер: Владимир обладает хорошим опытов разработки приложений в ООП стиле. Автор библиотек. Имеет опыт боевой опыт разработки на Dart и Haxe. Он может помочь зрителю посмотреть на разработку под другим углом.


Чем хороша тема: Эта тема напрямую касается практических аспектов разработки больших приложений. Это очень полезно нашим зрителям, так как выходит за рамки документации и учить принимать иненерсные решения. В докладе описан подход, который перекликается с первоначальным представлением об ООП





Основы


Напомни через минуту, или Как считать время в браузере, Никита Дубко


Пришла как-то к разработчику Мефодию задача: показывать через заданное пользователем время попап с напоминалкой, что время прошло. Написал Мефодий setTimeout, запушил, тикет закрыл. А пользователь так и не дождался своей напоминалки.


В докладе поговорим о том, как браузеры работают со временем, что такое время вообще и как всё-таки показать напоминалку в правильный момент.


Чем хорош спикер: Обладает огромным опытом выступлений и предоставил очень интересную тему


Чем хороша тема: Не так много докладов есть вообще, которые бы покрывали тему устройства API для замера времени. Данная тема интересна как сама по себе, так и с точки зрения формирования практик измерений производительности приложения исходя из данных знаний.




Оптимизация синхронной асинхронности, Дмитрий Махнев


Дмитрий Махнёв хочет показать проблему обманчивости простоты async/await на реальном кейсе и приблизительные пути решения и профиты от этого.
Что вас ждет в докладе:
очевидное нахождение проблемы синхронной асинхронности в реальной задаче (индексе сайта);
удивительно неправильная попытка решения;
героическое ускорение примерно на порядок без переписывания на Rust;
неловкая ситуация с unhandledRejection, пролетающей сквозь try/catch;
пара полезных абстракций.


Чем хорош спикер: Дмитрий ранее состоял в программном комитете HolyJS, работает в JetBrains. Увлеченный инженер, выступающий за фундаментальный подход ко всему.


Чем хороша тема: async/await, которого боялись несколько лет назад, стал чем-то обыденным, повседневным, повсеместным. Раньше у нас был callback hell, then hell. Нет, await hell не выглядит чем-то страшным для читабельности кода. Он в определённых ситуациях критически влияет на производительность. Дима на собственном примере покажет, как правильно работать с async-await.




Machine Learning and JavaScript. Unleashing the power of sentiment analysis, Artur Nizamutdinov


Если вы работали с ML на Python и хотите перенести часть опыта в браузер (или не имели дела с МL, но очень хотите попробовать) приходите на доклад Артура Низамутдинова. Он покажет на живом примере, для каких задач можно использовать машинное обучение в браузере, и разберёт дообучение моделей.


Чем хорош спикер: У Артура сильная экспертиза. А еще он сильно переработал доклад конкретно для нашей аудитории. Так что тут будут не просто знания, а знания, поданные именно для слушателей конференции


Чем хороша тема: ML сейчас почти так же моден, как JavaScript. И этот доклад совмещает в себе эти два мира, показывая на практическом примере, для каких задач мы можем использовать ML в браузере.





Визуальное


Анализ больших географически распределенных данных через визуализацию на карте, Никита Швыряев


Доклад основан на опыте построения трёх систем для бизнес-анализа географически распределенных данных. В основном доклад будет построен вокруг использования библиотеки Mapbox GL JS. Рассмотрим востребованную функциональность, осветим проблемы и решения во многих задачах (от загрузки данных до кластеризации). Целевая аудитория JS-разработчики, которые работают или планируют работать с визуализацией на карте.


Чем хорош спикер: Докладчик имеет большой опыт работы с визуализациями на картах, организацией взаимодействия с бэкэндом.


Чем хороша тема: В докладе рассматриваются практические примеры построения архитектуры для анализа больших географически распределённых данных через визуализации на карте. Рассматриваются паттерны, которые не стоит использовать при работе с картами, кратко вводится необходимая терминология.




Как отобразить 100500 метрик распределенной системы и не сойти с ума, Андрей Гончаров


Представьте распределённый кластер из 100 машин. Каждая собирает по 100 разных метрик. А теперь представьте график со всеми метриками сразу. Получилось? Вот и у Hazelcast не получилось. Поговорим, что делать, когда хочется показать много и сразу в условиях ограниченного экранного пространства и конечного числа глаз пользователей. Вспомним, что такое интеграл. Вместе посмотрим, чем он может быть лучше арифметического среднего и медианы.


Чем хорош спикер: Андрей работает в компании, которая оперирует огромным количеством машин и метрик на них. Задача правильного и, что самое главное, доверительного отображения метрик первостепенная.


Чем хороша тема: Любой бизнес любит метрики продуктовые, технические. Почти любой разработчик знает что такое медиана, 75, 90, 95 перцентиль. Правда, не всегда понятно пора бежать чинить, мы горим. Андрей расскажет, как можно обрабатывать метрики, чтобы они лучше отображали существующую картину.




Браузерный игровой движок как pet-проект, Михаил Реммеле


Михаил расскажет о своем опыте работы над pet-проектом. Игровой движок штука достаточно сложная, это огромный источник всевозможных задач и очень нетипичный опыт. Писать его можно бесконечно долго. Интересующие темы могут меняться, но в таком проекте всегда можно подобрать подходящую для исследований задачу. Технологии в данном случае: JavaScript, WebGL, Webpack.


Чем хорош спикер: Опыт работы в геймдеве, крайне редкий среди спикеров JS-конференций. Опыт разработки собственного игрового движка под браузер.


Чем хороша тема: Не распространённый, а возможно и редкий опыт в мире фронтенда. В докладе будет рассказано как автор с нуля написал свой игровой движок и с какими архитектурными сложностями столкнулся.





Заключение


Доклады это хорошо. Но напомним, что HolyJS не сводится только к тому, чтобы сидеть и слушать:


Спикерам можно будет задавать вопросы в чате во время доклада и в Zoom-созвоне после него.
Можно общаться с другими зрителями в чате конференции и на нашей виртуальной площадке.
На виртуальных стендах партнёров можно будет узнать больше о самих компаниях, послушать мини-доклады и поучаствовать в активностях.


Напоминаем, HolyJS пройдет с 20 по 23 апреля в онлайне. Вся информация и билеты на сайте.

Подробнее..

VueUse обязательная библиотека для Vue 3

27.04.2021 22:20:31 | Автор: admin

Для тех, кто незнаком с этой библиотекой, советую попробовать, так она может де-факто стать стандартом для использования в проектах на Vue 3, как стала, например, библиотека lodash для почти любых проектов на js.

Остальные наверное уже успели заценить весь обширный функционал, который она предоставляет. Некоторые уже использовали ее на Vue 2, но далеко не все новые функции поддерживают старую версию. Арсенал библиотеки впечатляет, тут и простые утилиты вроде клика вне элемента, и различные интеграции с Firebase, Axios, Cookies, QR, локальным хранилищем, браузером, RxJS, анимации, геолокации, расширения для стандартных Vue-хуков, медиа-плеер и многое другое. Среди спонсоров отмечен сам Эван Ю, что как бы намекает. Библиотека регулярно получает обновления, баги закрываются, а сообщество растет. В общем у нее есть все для успеха.

В этой статье я расскажу только о 3 фичах, но, безусловно, и все остальные нуждаются во внимании.

onClickOutside клики вне элемента

С установкой библиотеки вы справитесь, я уверен, поэтому перейдем сразу к интересным вещам. Для разогрева рассмотрим простой хук, который отслеживает клики вне заданного элемента onClickOutside. Существует множество пакетов, которые предоставляют данный функционал, не считая бесчисленные велосипеды, которые писал наверное каждый. Обычно его реализуют через добавление пользовательской Vue-директивы к нужному элементу, например v-clickOutside, а вот использование хука необычно.

Я использовал этот хук в своей тудушке, в компоненте ToDoItem.vue:

<template>    <li ref="todoItem">      <input type="checkbox" />      <span        v-if="!editable"        @click="editable = !editable"      >        {{ todo.text ? todo.text : "Click to edit Todo" }}      </span>      <input        v-else        type="text"        :value="todo.text"        @keyup.enter="editable = !editable"      />    </li></template><script lang="ts">  import { defineComponent, PropType, ref } from "vue"  import ToDo from "@/models/ToDoModel"  import { onClickOutside } from "@vueuse/core"  export default defineComponent({    name: "TodoItem",    props: {      todo: {        type: Object as PropType<ToDo>,        required: true      }    },    setup() {      const todoItem = ref(null)      const editable = ref(false)      onClickOutside(todoItem, () => {        editable.value = false      })      return { todoItem, editable }    }  })</script>

Я удалил лишний код, чтобы не отвлекал, но все еще компонент достаточно большой. Обратите внимание на код, который находится внутри хука setup, сначала мы создаем пустую ссылку todoItem, которую вешаем на нужный элемент в шаблоне, а потом передаем первым параметром в хук onClickOutside, а вторым параметром коллбэк с нужными нам действиями. При клике на тег span, он заменится на тег input, а если кликнуть вне тега li с атрибутом ref="todoItem", то input сменится тегом span.

useStorage реактивное локальное хранилище

Следующая функция, о которой я расскажу, это useStorage. Эта функция создает реактивное локальное хранилище. Это очень удобно, так как никаких дополнительные манипуляции с хранилищем больше не потребуются. Данные будут сохранятся, обновляться и удаляться автоматически. Я применил эту функцию в Vuex для хранения записей со списком дел.

// @/store/index.tsimport { createStore } from 'vuex'import Note from '@/models/NoteModel'import { useStorage } from '@vueuse/core'const localStorageNotes: unknown = useStorage('my-notes', [] as Note[])export default createStore({  state: {    notes: localStorageNotes as Note[]  },  mutations: {                  addNote(state) {      state.notes.push(note)    },   // mutations  },  actions: {    // actions  },  getters: {    // getters  },  strict: true})

Первым параметром функция useStorage принимает ключ, под которым она будет сохранять ваши данные, а вторым их начальное значение. Полученную переменную localStorageNotes передаем в state.notes. Массив notes можно использовать как обычно, например добавлять новую запись в мутациях. Теперь записи будут сохраняться даже после перегрузки страницы и самого браузера.

При написании этого кода TypeScript начал ругаться на отсутствие типа у переменной localStorageNotes. Этого следовала ожидать, так как эта переменная создается с помощью ref и не создана для использования вне хука setup. Я не нашел другого решения, кроме как присвоить ей значение any или unknown. Код работает, но выглядит не очень. Если кто знает лучшее решение, подскажите в комментариях. Будем надеяться, что авторы библиотеки создадут лучшую интеграцию с Vuex, где эта функциональность так необходима.

Для сравнения также полезно ознакомиться с примером использования useStorage от авторов. Разница в том, что в setup работать с реактивным хранилищем нужно не напрямую, а через его свойство value. В html-шаблоне же, все как обычно.\

useRefHistory история изменений

useRefHistory хук который позволит записывать историю изменений данных и предоставляет undo/redo функциональность. Я использовал ее для создания кнопок Undo и Redo на странице создания и редактирования записи со списком дел. Так как переменная currentNote, которая отвечает за хранение редактируемой записи, тоже находится во Vuex-хранилище. Я так же использовал ее именно там и так же получил ошибку типизации. Рассмотрим код получше:

import { createStore } from 'vuex'import Note from '@/models/NoteModel'import ToDo from "@/models/ToDoModel"import { useRefHistory } from '@vueuse/core'import { ref } from 'vue'const note: any = ref({  title: "",  todos: [] as ToDo[]})const { history, undo, redo, canUndo, canRedo, clear } = useRefHistory(note, {  deep: true})export default createStore({  state: {    currentNote: note as Note,    currentId: 0  },  mutations: {    // mutations    clearHistory() {      clear()    },    undoChanges() {      undo()    },    redoChanges() {      redo()    }  },  actions: {      },  getters: {    canUndo() {      return canUndo.value    },    canRedo() {      return canRedo.value    }  },  strict: true})

Создаем реактивную переменную с помощью ref, передаем ее в хук useRefHistory, в параметрах хука обозначаем deep: true, для вложенных объектов. С помощью деструктурирующего присваивания из useRefHistory получаем history, undo, redo, canUndo,canRedo и clear. Функции undo и redo, необходимо применять только в мутациях, чтобы Vuex не ругался. Свойства canUndo и canRedo можно передать через геттеры, которые потом повесить на атрибуты disabled в кнопках. clear необходима для очистки истории после окончания редактирования записей. Хук useManualRefHistory делает практически тоже самое, но сохранение в историю происходит только по вызову команды commit().


Я рассказал всего про 3 функции из большого арсенала инструментов VueUse для разработки на Vue 3. Для более глубокого изучения советую посетить сайт этой замечательной библиотеки. Документация все еще далека от совершенства, но она регулярно обновляется как и сама библиотека.

Полный код моего полигона, где я испытывал эту библиотеку, можно посмотреть здесь или здесь. О начале работы над этим проектом я уже рассказывал в этой статье.

Подробнее..

Как я написал браузерный 3D FPS шутер на Three.js, Vue и Blender

07.05.2021 02:13:27 | Автор: admin
Стартовый экран игрыСтартовый экран игры

Мотивация

На пути каждого коммерческого разработчика (не только кодеров, но, знаю, у дизайнеров, например, также) рано или поздно встречаются топкие-болотистые участки, унылые мрачные места, блуждая по которым можно вообще забрести в мертвую пустыню профессионального выгорания и/или даже к психотерапевту на прием за таблетками. Работодатели-бизнес очевидно задействует ваши наиболее развитые скилы, выжимая по максимуму, стек большинства вакансий оккупирован одними и теми же энтерпрайз-инструментами, кажется, не для всех случаев самыми удачными, удобными и интересными, и вы понимаете что вам придется именно усугублять разгребать тонну такого легаси Часто отношения в команде складываются для вас не лучшим образом, и вы не получаете настоящего понимания и отдачи, драйва от коллег Умение тащить себя по-мюнхаузеновски за волосы, снова влюбляться в технологии, увлекаться чем-то новым [вообще и/или для себя, может быть смежной областью], имхо, не просто является важным качеством профессионала, но, на самом деле, помогает разработчику выжить в капитализме, оставаясь не только внешне востребованным, конкурентоспособным с наступающей на пятки молодежи, но, прежде всего, давая энергию и движение изнутри. Иногда приходится слышать что-нибудь вроде: а вот мой бывший говорил, что если бы можно было не кодить, он бы не кодил!. Да и нынешняя молодежь осознала что в сегодняшней ситуации честно и нормально зарабатывать можно только в айти, и уже стоят толпою на пороге HR-отдела... Не знаю, мне нравилось кодить с детства, а кодить хочется что-нибудь если не полезное, то хотя бы интересное. Короче, я далеко не геймер, но в моей жизни было несколько коротких периодов когда я позорно загамывал. Да само увлечение компьютерами в детстве началось, конечно же, с игр. Я помню как в девяностые в город завезли Спектрумы. Есть тогда было часто практически нечего, но отец все-таки взял последние деньги из заначки, пошел, отстоял невиданно огромную очередь и приобрел нам с братом нашу первую чудо-машину. Мы подключали его через шнур с разъемами СГ-5 к черно-белому телевизору Рекорд, картинка тряслась и моргала, игры нужно было терпеливо загружать в оперативную память со старенького кассетного магнитофона [до сих пор слышу ядовитые звуки загрузки], часто переживая неудачи... Несмотря на то что ранние программисты и дизайнеры умудрялись помещать с помощью своего кода в 48 килобайт оперативной памяти целые миры с потрясающим геймплеем, мне быстро надоело играть и я увлекся программированием на Бейсике)), рисовал спрайтовую графику (и векторная трехмерная тогда тоже уже была, мы даже купили сложную книжку), писал простую музыку в редакторе... Так вот, некоторое время назад мне опять все надоело, была пандемийная зима и на велике не покататься, рок-группа не репетировала Я почитал форумы и установил себе несколько более-менее свежих популярных игр, сделанных на Unity или Unreal Engine, очевидно. Мне нравятся РПГ-открытые миры-выживалки, вот это все... После работы я стал каждый вечер погружаться в виртуальные миры и рубиться-качаться, но хватило меня ненадолго. Игры все похожи по механикам, однообразный геймплей размазан по небольшому сюжету на кучу похожих заданий с бесконечными боями Но, самое смешное, это реально безбожно лагает в важных механиках. Лагают коммерческие продукты которые продают за деньги А любой баг, имхо, это сильное разочарование он мгновенно выносит из виртуальной среды, цифровой сказки в реальный мир Конечно, отличная графика, очень круто нарисовано. Но, утрируя, я понял что все эти поделки на энтерпрайзных движках, по сути даже не кодят. Их собирают менеджеры и дизайнеры, просто играясь с цветом кубиков, но сами кубики, при этом практически не меняются... Вообщем, когда стало совсем скучно, я подумал что а я ведь тоже так могу, да прямо в браузере на богомерзком непредназначенным для экономии памяти серьезного программирования джаваскрипте. Решил наконец полностью соответствовать тому что все время с умным видом повторяю сыну: уметь делать игры, намного интереснее чем в них играть. Одним словом, я задался целью написать свой кастомный браузерный FPS-шутер на открытых технологиях.

Итак, на данный момент, первый результат по этой долгоиграющей таски на самого себя можно тестить: http://robot-game.ru/

Стек и архитектура

Вполне может быть, что я не вкурсе чего-то (ммм на ум приходит что-нибудь вроде quakejs и WebAssembly), но, с основной технологией было, походу, особо без вариантов. Библиотека Three.js давно привлекала мое внимание. Кроме того, в реальной коммерческой практике, несколько раз, но уже приходилось сталкиваться с заказами на разработку с ее использованием. На ней я сделал собственно саму игру.

Очевидно, что нужно что-то вокруг для простого интерфейса пользователя: шкал, текстовых сообщений, инструкций, контролов настроек, вот этого всего. Я решил поленился, не усложнять себе жизнь и использовать любимый фреймворк Vue 2, хотя, надо было, конечно, писать на свежем, похожем по дизайну и еще более прогрессивном по сути молниеносном Svelte. Но так как хорошенько разобраться предстояло, прежде всего, с Three, думаю, это было правильное решение. Хорошо знакомый и предсказуемый, лаконичный, изящный, удобный и эффективный Vue, позволил практически не тратить время на внешний пользовательский интерфейс.

Когда-то давно я работал дизайнером на винде и достаточно бойко рисовал 2D в Иллюстраторе, но навыков 3D у меня никаких не было. А вот в процессе создания шутера пришлось пойти, скачать и установить одним кликом на свой нынешний Linux Blender. Я быстро научился рисовать с помощью примитивов мир, отдельные объекты, и даже научился делать UV-развертки на них. Но! В целях простоты, скорости работы и оптимизации объема ассетов в моей нынешней реализации не используются текстурные развертки. Я просто подгружаю чистые легковесные бинарные glTF: .glb-файлы и натягиваю на них всего несколько вариантов нескольких текстур уже в джаваскрипте. Это приводит к тому что текстуры на объектах искажаются в разных плоскостях, но на основном бетоне для стен, смотрится даже прикольно, такой разный, рваный ритм. Кроме того, сейчас персонажи не анимируются пока не было времени изучить скелетную анимацию. Одной из основных целей написания этой статьи является желание найти (по знакомым не получилось) специалиста который поможет довести проект до красоты (очень хочется) и согласится добавить совсем немного анимаций на мои .glb (об условиях договоримся). Тогда враги, будут погружаться в виде glTF со встраиванием: .gltf-файлов со встроенными текстурами и анимациями. Сейчас уже есть два вида врагов: ползающие-прыгающие наземные дроны-пауки и их летающая версия. Первых нужно научить шевелить лапками при движении и подбирать их в прыжке, а вторым добавить вращение лопастей.

Модель дрона-паука в BlenderМодель дрона-паука в Blender

Для того чтобы игру нельзя было тупо-легко прочитить через браузерное хранилище я добавил простенький бэкенд на Express с облачной MongoDB. Он хранит в базе данные о прогрессе пользователя по токену, который на фронте записывается в хранилище. Хотелось сделать не просто FPS-шутер, а привнести в геймплей элементы РПГ. Например, в нынешней реализации мир делиться на пять больших уровней-локаций между которыми можно перемещаться через перезагрузку. При желании локации можно быстро дорисовывать из уже имеющихся и добавлять в игру, указывая только двери входа и выхода, стартовую и конечную координату, хорошее направление камеры для них (при переходе живого персонажа через дверь текущее направление сохраняется-переносится). На каждом уровне есть только одна формальная цель найти и подобрать пропуск к двери на следующий уровень. Пропуски не теряются при проигрыше на локации (только при выборе перехода на стартовый уровень после выигрыша на последнем пятом). А вот враги и полезные предметы цветы и бутылки при переходе между локациями, проигрыше или перезагрузке страницы пока выставляются заново согласно основной glb-модели одновременно и схеме, и визуальной клетке локации об этом дальше. И тут вот первое важное про архитектуру: мой фронтенд это совсем примитивное SPA. Vue, например, ни для чего не нужен роутер. Вероятно, я получу негативную реакцию некоторых продвинутых читателей, после того, как сообщу что потратил кучу времени для того чтобы попробовать организовать перезагрузку-очистку сцены внутри системы и пока с самым провальным результатом. Вот к такой спорной мысли я пришел в процессе своих экспериментов: самый эффективный, простой, даже, в этой ситуации, правильный и при этом, конечно же, топорный подход, это нативный форс-релоад после того как мы сохраняем или обнуляем данные пользователя на бэкенде:

window.location.reload(true);

А потом просто дадада считываем их обратно )) и строим всю сцену заново, с чистого листа, так сказать. Тут, конечно, можно было бы улучшить прокидывать пользователя через хранилище вместо того чтобы ожидать разрешения запроса, но это не критично, в данном случае. Небольшое количество оптимизированных текстур (меньше полтора мегабайта сейчас), сильно компрессированного аудио (MP3, понятно: 44100Гц 16 бит, но с сильным сжатием 128 кбит/с меньше полтора мегабайта все вместе сейчас), основная модель-локация весящая около 100Кб и модели отдельных объектов каждая еще меньше... Я добился того что переход между локациями полная перезагрузка мира занимает вполне приемлемое время, судя по записи перфомансов примерно две с чем-то, три секунды. И это, кажется, меньше чем во всех шовных открытых мирах от энтерпрайза которые я видел. Продвинуто бесшовный я тоже один нашел и поиграл, но он лагал хуже всех, и когда сюжет наконец двинулся с мертвой точки вдруг перестали работать сейвы; тут я уже забил

Все использующиеся в игре текстурыВсе использующиеся в игре текстурыПерфомансПерфоманс

Хочется сразу сказать что техлиды и сеньоры с менторским тоном и заоблачной экспертизой в микробенчмаркинге в комментариях только приветствуются. Это же вообще самое забавное и интересное на Хабре когда лиды с сеньорами начинают рубиться в комментариях за стоимость операций в джаваскрипте и то, чей микробенчмаркинг заоблачнее! Остается только надеятся на то, что когда вы будете размазывать мой форсрелоад как дешёвое и сердитое средство изменения сцены вы обязательно продемонстрируете ваши работающие примеры в которых сцена Three с большим количеством разнообразных объектов на ней очищается и заново инициализируется через свои внутренние методы (например, без перезагрузки текстур и прочих ассетов, аудио). Я же не говорю что это невозможно, это очевидно дорого. Намного дороже чем просто сделать форсрелоад. Понятно что хороший проект это прежде всего кодовая база которая может и должна легко развиваться. Но невозможно прикрутить все фичи сразу, а использование дешевого релоада сейчас никак не блокирует добавление более сложного функционала в будущем. Да и кроме дешевизны более простой подход и идеологически привлекателен. Я убежден что хороший код это простой и понятный код, хороший подход простой подход, точно так же как и интерфейс который они предоставляют. Простое решение лучше сложного, особенно если мы только начинаем строить что-то.

Для того чтобы избежать лишних сложностей в моей реализации сцена практически неизменна. Она разворачивается, запускается и дальше функционирует в некотором постоянном виде [порождая и уничтожая только выстрелы и взрывы] пока не происходит переход в другую локацию (или проигрыш на этой). Конкретнее: cейчас я нигде кроме удаления не подлежащих внешнему учету выстрелов и взрывов не использую scene.remove(object.mesh) например при сборе героем полезных предметов, делая вместо этого:

// встроенное свойство на Object3D в Threeobject.mesh.visible = false;// кастомный флаг кастомного массива объектовobject.isPicked = true;

Поэтому мы, например, можем даже использовать свойство id: number mesh`ей вместо uuid: string для учета и идентификации объектов. Так как все подлежащие учету объекты всегда остаются на сцене мы можем быть уверены что Three не поменяет айдишники, сдвинув нумерацию под коробкой при удалении элемента (но если вы хотите все-таки удалять что-то такое просто опирайтесь на uuid при работе с этим).

Я нигде и не на чем не использую .dispose(), так как мне просто нечего удалять. В документации библиотеки сказано что лучший момент и повод для этого переход между уровнями, когда можно и нужно, например удалить ненужные текстуры. Как вы видите выше, текстур у нас совсем немного и они все всегда нужны.

Посмотрим на структуру проекта:

. /public // статические ресурсы   /audio // аудио     ...   /images // изображения     /favicons // дополнительные фавиконки для браузеров       ...     /modals // картинки для информационных панелей       /level1 // для уровня 1         ...       ...     /models       /Levels         /level0 // модель-схема Песочницы (скрытый уровень 0 - тестовая арена)           Scene.glb         ...       /Objects          Element.glb          ...     /textures        texture1.jpg        ...   favicon.ico // основная фавиконка 16 на 16   index.html // статичный индекс   manifest.json // файл манифеста   start.jpg // картинка для репозитория ) /src   /assets // ассеты сорцов     optical.png // у меня один такой )))   /components // компоненты, миксины и модули     /Layout // компоненты и миксины UI-обертки над игрой       Component1.vue // копонент 1       mixin1.js // миксин 1       ...     /Three // сама игра        /Modules // готовые полезные модули из библиотеки          ...        /Scene           /Enemies // модули врагов             Enemy1.js             ...           /Weapon // модули оружия             Explosions.js // взрывы             HeroWeapon.js // оружие персонажа             Shots.js // выстрелы врагов           /World // модули различных элементов мира             Element1.js             ...           Atmosphere.js // модуль с общими для всех уровней объектами (общий свет, небо, звук ветра) и проверками взаимодействия между другими модулями           AudioBus.js // аудио-шина           Enemies.js // модуль всех врагов           EventsBus.js // шина событий           Hero.js // модуль персонажа           Scene.vue // основной компонент игры           World.js // мир   /store // хранилище Vuex     ...   /styles // стилевая база препроцессора SCSS     ...   /utils // набор утилитарных js-модулей для различных функциональностей     api.js // интерфейс для связи с бэкендом     constants.js // вся конфигурация игры и тексты-переводы     i18n.js // конфигурация переводчика     screen-helper.js // модуль "экранный помощник"     storage.js // модуль для взаимодействия с браузерным хранилищем     utilities.js // набор полезных функций-атомов   App.vue // "главный" компонент   main.js // эндпоинт сорцов Vue ... // все остальное на верхнем уровне проекта, как обычно: конфиги, gitignore, README.md и прочее

То как я работаю со стилями и оформлением компонент в UI-обертке подробно описано в моем первом масштабном тексте для сообщества. Целью данной статьи не является рассмотрение отдельных разрозненных особенностей перечисленных выше технологий и инструментов. Хочется рассказать об опыте, проблемах и найденных возможных путях их решениях которые возникли в результате практической попытки совместить данные технологии именно для решения конкретной задачи создания кастомного браузерного шутера.

Сейчас игра в спокойном состоянии когда потревоженных врагов нет или совсем мало, на компьютере с поддержкой GPU выдает практически коммерческие 60FPS в Google Chrome (ну или Yandex Bro). В Firefox игра запускается, но показатель производительности не менее чем в 2-3 раза ниже. А когда начинается мясо, появляется много потревоженных врагов, выстрелов и взрывов в Лисе процесс начинает лагать и может вообще повиснуть. Моя экспертиза в микробенчмаркинге сейчас пока не позволяет с умным видом рассуждать о причинах этой разницы. Будем считать что дело в более слабой поддержке WebGL и вычислительных способностях, что-то такое))...

Легенда

Так как выбранный мною тип игры совсем обычный классический FPS, пиф-паф, ойойой, мне была нужна актуальная захватывающая легенда. Прообраз главного героя Робот-собутыльник появился из нашего с друзьями музыкального рок-творчества: существует пластинка его имени и забавный клип про него... В игре речь, видимо, идет о потомках пьющего робота

Земля, далекое будущее. Люди давным-давно перебили друг-друга в ядерных войнах, выясняя кто правый, кто левый, кто белый, а кто красный и прочее. На не затронутых бомбардировками атоллах в Тихом Океане размножилось несколько рас человекоподобных роботов. Например, более человекоподобные, имитирующие органику, двуполость и личные отношения Собутыльники, которые перерабатывают животных и растительность в жизненную силу и спецэффекты. Внутри них, по тонким крепким трубкам, течет специальный сброженный органический микс, схожий с человеческим вином, приводя их в движение. Или более машиноподбные однополые Кибер-Танцоры, проповедующие медитативный Дзинь-Нойз. На почве гендерных и религиозных разногласий между культурами, конечно же, понеслась жестокая война.

Пара молодых Собутыльников отправились в свадебное путешествие по океану. Но катер внезапно дал течь и они с трудом дотянули до ближайшего атолла. Прекрасный на первый взгляд остров оказался опасной ловушкой, так как был давно облюбован для медитаций Танцорами.

Робот-Собутыльник приходит в себя на полу пыточной камеры тюрьмы Однополых... Баки пусты... Его мучители, видимо, решили что он уже не жилец, и оставили подыхать... На стене висит портрет легендарного Последнего Президента идеологического предтечи и кумира Танцоров человека, когда-то развязавшего последнюю в истории человечества войну

Аллегории прозрачные, конечно. Но сама рамочная идея про конфликт однополых и двуполых машин в будущем, имхо, замечательная. Так и будет, точно, все предпосылки уже налицо. ))

В игре пока нет самих главных врагов, только их дроны (нужен специалист по скелетной анимации отзовись!), но уже присутствуют специфические объекты и отдельная механика позволяющая глубже раскрыть генезис Собутыльников, историю их борьбы против демократической диктатуры Танцоров. Это информационные панели которые включаются когда герой находится поблизости и важно в одном помещении с ними. Ниже об этом будет подробнее, когда будем разбирать постройку мира и взаимодействие модулей.

ДашбордДашборд

Если подойти к панели и нажать E открывается модаль с исторической справкой:

Рассказ о будущем внутриРассказ о будущем внутри

Это задел для дальнейшего развития в сторону РПГ. Например на таких дашбордах несложно организовать торговый ларек, в котором можно было бы менять поверженный и подобранный металлолом на полезные цветы и бутылки.

Геймплей

В моей игре любой выстрел сталкиваясь с препятствием вызывает взрыв который уронит и врагов и героя на величину зависящую от расстояния. Поражение врага вызывает взрыв повышенной мощности следует быть осторожным в ближнем бою.

Фишку боям с двумя видами дронов с помощью единственного пока оружия виномета с оптическим прицелом сейчас добавляют похожие на чупики и хорошо заметные издалека, подозреваю психотропные цветы, употребление которых восполняет шкалу здоровья и прокачивает способности персонажа, ну или замедляет мир в случае зеленого цветка топлива для машины времени.

Цветы и бутылкиЦветы и бутылки

Так как у нас почти РПГ и возможно собирать полезный стафф перемещаясь назад по локациям цветы и бутылки имеют определенный вес и герой не может носит с собой больше 25 его единиц. Вес вместе с количеством возвращаемого приемом цветка здоровья помогает сбалансировать всю эту механику: тупые цветы дающий неуязвимость красный и прокачивающий виномет фиолетовый весят больше, а здоровье дают меньше чем более интересные желтый и зеленый.

Сейчас уже есть три уровня сложности игры они влияют на срок действия цветов, дистанцию на которой героя обнаруживают враги (если персонаж ползет может подобраться в два раза ближе) и, конечно же силу урона от выстрелова и взрывов.

Уровни сложностиУровни сложности

Если я найду скелетного аниматора и смогу довести до ума то что уже есть и потом продолжить развивать проект дальше, можно будет подумать о следующих фичах:

  • Разное оружие. Виномет потеряет прицел, но станет работать как пулемет-автомат не нужно будет все время нажимать на левую кнопку мыши. С прицелом будет снайперская винтовка стреляющая стальными снарядами (которые, например, можно делать из собранных разрушенных врагов тут бы пригодился уже упомянутый торговый ларек на дашбордах).

  • Еще один вид бутылок с крепышом часовые мины: установил быстро отбегаешь. Будут полезны для разрушения Танков с огромным здоровьем или крупных скоплений любых врагов.

  • Новые типы врагов. Танки медленные, но очень живучие и с убойным выстрелом. Стационарные дроны-пушки умеющие стрелять не в горизонтальной плоскости навесом как делают дроны сейчас, а под разными углами и двойными зарядами. Рядовые бойцы Танцоры Роботы-Курицы мой барабанщик почему-то их именно так видит. В идеале они высаживаются как спезназ, приземляясь на челноке в центр третьей локации когда герой на нее заходит. В пятой локации может появиться босс: Робот-Блогер Финальный с ракетницей

  • Трубочных и двуполых Собутыльников нарисовать сложно, но в идеале было бы рассадить их по камерам Централа четвертой локации.

  • Можно добавить 2D-карту с врагами (внизу и по центру экрана)

Планов полно, но без скелетной анимации они бессмысленны, конечно

Но хватит лирики, перейдем к техническим решениям и собственно коду.

Конфигурация

Особенный кайф от написания кастомной игры в том, что после того как вы доставили новые фичи или любые изменения в код вам просто необходимо расслабиться и их честно искренне протестировать. Ручками. Сделать несколько каток, по любому. Тесты тут никак и ничем не помогут, даже, убежден, наоборот будут мешать прогрессу, особенно если вы не работаете по заранее известному плану, а постоянно экспериментируете. Браузерная игра на джаваскрипт это в принципе превосходный пример того, когда статическая типизация или разработка через тестирование будут только мешать добиться действительно качественного результата. (А на чем тут необходимо проверять типы, господа сеньоры? Я до сих пор в замешательстве от React c CSS Modules и просто Flow, а не TS даже в котором авторы маниакально проверяли что каждый, еще и передаваемый по цепочке компонент, класс модулей для оформления !!! это string А тут что будем маниакально типизировать, вектора?). И даже сам Роберт Мартин в Идеальном программисте делает несколько пассажей на тему бессмысленности TDD, когда говорит о рисках при разработке GUI. В моей игре можно сказать что и нет практически ничего кроме тонны двумерного и трехмерного GUI, ну и логики для него. Любая ошибка либо вызовет исключение, либо неправильное поведение во вьюхе и геймплее, которое может быть очень быстро обнаружено с помощью визуальной проверки, но очень сомнительно что вообще способно быть покрыто тестом.

На самом деле, создавая любую программу или даже просто разметку для программы вы всегда непреложно должны следовать некоторым общеизвестным простым принципам (но не всегда это TDD). Чтобы не происходило укороченный нереальный дедлайн, термоядерная перестрелка за окном вы должны писать свой код таким образом, чтобы его можно было легко отлаживать и развивать. Любое важное или повторяющиеся больше одного раза значение должно быть отражено в конфигурации. Влияющие на геймплей параметры должны быть локализованы в одном файле балансируя геймплей работать вы должны именно и практически только с ним одним.

Все настройки настройки и значения влияющие на геймплей и дизайн (константа DESIGN), а также весь текстовый контент-переводы у меня сосредоточены в constants.js.

Контрол

На сайте библиотеки Three представлено большое количество полезных примеров с демо-стендами, самых разных реализаций, функциональностей которые стоит изучить и по возможности к месту использовать. Я отталкивался в своих исследованиях, прежде всего, вот от этого примера. Это правильный, мягкий инерционный контрол от первого лица который математически обсчитывает столкновения с клеткой-миром gld-моделью с помощью октодерева. Проверять столкновения можно для капсулы (для героя или врагов) или обычных сферы Sphere и луча Ray от Three. Этого в принципе достаточно для чтобы сделать FPS-игру: сделать так чтобы герой и враги не сталкивались с миром и между собой, выстрелы взрывались при попадании в другие объекты и тд.

Для того чтобы понимать что происходит когда вы нажимаете кнопку Играть, игра запускается и курсор мыши пропадает вы должны знать о браузерной фиче Pointer_Lock_API. Мы добавляем такой контрол вместе со всей стандартной кухней Three в инициализации основного компонента-сцены, ищите:

// Controls// In First Person...

Но! Тут нюанс браузеры обязательно оставляют путь для панического отступления пользователю и резервируют клавишу Esc для того чтобы пользователь всегда мог разлочить указатель. Это касается нашего UI/UX в игре необходима клавиша P ставящая мир на паузу. Когда указатель залочен то бишь запущен игровой процесс нажатие на Esc, как уже сказано вызовет паузу. Но если мы попытаемся добавить обработку отпускания по 27ому коду даже только для режима паузы, все равно очень быстро увидим в консоли:

ОшибкаОшибка

Поэтому: забудьте про Esc. Пауза по клавише P. Есть еще одно ограничение и проблема связанная с созданием хорошего FPS-контрола: оружие. Я так понял что в энтерпрайзных реализациях руки-оружие это отдельный независимый план наложенный поверх мира. С Three, насколько я понимаю, сделать так не получится. Поэтому мой пока единственный в арсенале грозный виномет с оптическим прицелом это объект сцены который приделан к контролу. Я копирую вектор направления камеры на него. Но около зенита и надира в результате его начинает штормить он не может однозначно определить позицию. При взгляде совсем под ноги я его просто скрываю, а вот стрелять наверх нужно. Что делать с этим небольшим и не особо заметным багом я пока не придумал.

Оптический прицел винометаОптический прицел винометаВыстрел вверхВыстрел вверх

Пытаясь сделать скоростной задорный шутер на Three мы можем сразу забыть о тенях или дополнительных источниках освещения, особенно движущихся. Да, я пытался запилить качающиеся на ветру лампы для особенного мрачняка и криповости, движущиеся тени от них. Нет никак нельзя даже статичные точечные источники света сильно просаживают производительность (а нам еще врагов гонять). По поводу света я пришел к простому компромиссу: чтобы картинка не выглядела совсем сухо и скучно приделать мощный фонарик к контролу, герою. Фонарик можно выключать клавиша T.

Далее я просто пройдусь по основным модулям давая небольшие комментарии в интересных моментах.

Сцена

Основной компонент Scene.vue предоставляет:

  • всю стандартную кухню Three: Renderer, Scene и ее туман, Camera и Audio listener в ней, Controls

  • набор утилитарных переменных для использования в анимационных циклах низовых модулей

  • переменные для хранения коллекций примитивных дополнительных объектов превдоmesh`ей по которым работает кастинг

  • в том числе и через используемые миксины все необходимые ему самому или его низовым модулям геттеры и экшены стора Vuex

  • обрабатывает большинство (кроме тех, что удобно ловить в логике героя) событий клавиатуры, мыши и так далее

  • инициализирует Аудиошину, Шину Событий и Мир

  • анимирует Шину Событий, Героя и Мир

  • в наблюдателях значений важных геттеров добавляет игровой логики

Весь код тут простой, прямо очевидный, практически не требующий дополнительных пояснений. Что-то мы будем рассматривать дальше, например что за такие превдоmesh`и для кастинга. Но стоит только остановить внимание на такой простой базовой сущности как переменные. Дело в том что существует еще один действительно важный аспект который позволит нам вытянуть эту задачу с шутером это тут можно начинать смеяться экономия памяти в джаваскрипте (господа техлиды-сеньоры?). Да не надо ни при каких обстоятельствах создавать переменные в анимационных циклах и проверках низовых модулей, нужно использовать только те что уже есть в основном компоненте (ну или модуле), созданы заранее. Контекст основного компонента можно передавать в публичные методы низовых модулей-функций.

Стандартный модуль героя, врагов, предмета или специфического объекта вроде двери или информационной панели в общем виде выглядит так:

import * as Three from 'three';import { DESIGN } from '@/utils/constants';function Module() {  let variable; // локальная переменная - когда очень удобна или необходима при инициализации или во всей логике    // ...  // Инициализация  this.init = (    scope,    texture1,    material1,    // ...  ) => {    // variable = ...    // ...  };  // Функция анимационного цикла для этого модуля - опционально (предметы, например, не нужно анимировать)  this.animate = (scope) => {    // А вот тут и в остальной логике стараемся использовать уже только переменные Scene.vue:    scope.moduleObjectsSore.filter(object => object.mode === DESIGN.ENEMIES.mode.active).forEach((object) => {      // scope.number = ...      // scope.direction = new Three.Vector3(...);      // variable = ... - так, конечно, тоже можно, главное не let variableNew;      // ...    });  };}export default Module;

Стор

Хранилище Vuex поделено на 3 простых модуля. layout.js отвечает за основные параметры игрового процесса: паузы-геймоверы и тд, взаимодействует с API-бекенда. В hero.js большое количество полей и их геттеров, но всего два экшена/мутации. Этот модуль позволяет в максимально унифицированной форме распространять изменения значений отдельных параметров, шкал, флагов на герое с помощью setScale или может пакетно установить эти значения через setUser.

Третий модуль совсем примитивный preloader.js и целиком состоит из однотипных boolean-полей с false по дефолту. Пока его поле isGameLoaded единственное в состоянии модуля с геттером с false не получает true при запуске или перезагрузке приложения пользователь будет видеть лоадер. Каждое из остальных полей обозначает подгрузку определенного ассета: текстуры, модели, аудио или постройку определенного типа объектов.

Если нам нужно подгрузить, например, текстуру песка:

import * as Three from 'three';import { loaderDispatchHelper } from '@/utils/utilities';function Module() {  this.init = (    scope,    // ...  ) => {    const sandTexture = new Three.TextureLoader().load(      './images/textures/sand.jpg',      () => {        scope.render(); // нужно вызвать рендер если объекты использующию эту текстуру заметны "на первом экране"          loaderDispatchHelper(scope.$store, 'isSandLoaded');      },    );  };}export default Module;
// В @/utils/utilities.js:export const loaderDispatchHelper = (store, field) => {  store.dispatch('preloader/preloadOrBuilt', field).then(() => {    store.dispatch('preloader/isAllLoadedAndBuilt');  }).catch((error) => { console.log(error); });};

Когда отправка сообщения о том что элемент подгружен разрешается функция-помощник из набора атомов-утилит отправляет экшен проверяющий все ли готово?.

Согласен что решение по прелоадера не идеальное с точки зрения UI в том смысле что мы не демонстрируем общий прогресс по загрузке. Но на данном этапе это не кажется критически важным, особенно в свете озвученной выше концепции и даже факта быстрой перезагрузки локаций.

Аудиошина

Одна только необходимость воздействовать сразу на все звучащие аудио, например, при переходе в режим паузы или при включении машины времени диктует требование формирование общего микшера в системе аудиошины. Кроме того, такой подход максимально удобно унифицирует синтаксис однообразных похожих вызовов и избавляет от необходимости следить за очередностью подгрузки аудио на объекты (когда на одном объекте может звучать несколько) с помощью LoadingManager`ов.

Аудио бывают:

1) Звучащие на контроле-герое и PositionalAudio на объектах

2) Луп или сэмпл

Насколько я понял аудио-API Three полностью отражает API браузера. Меня как человека уже всю осознанную жизнь занимающемуся на досуге звукорежиссурой несколько напрягает необходимость станцевать с бубном в коде для того если захочется добавить простой фейд в начале или конце сэмпла, но что тут поделаешь. При желании мы как раз можем удобно добавить такую функциональность именно с помощью модуля аудиошины.

В Hero удобно записывать аудио в переменную чтобы можно было просто работать [в обход шины] с ними в специфической логике:

// В @/components/Three/Scene/Hero.js:import * as Three from "three";import {  DESIGN,  // ...} from '@/utils/constants';import {  loaderDispatchHelper,  // ...} from '@/utils/utilities';function Hero() {  const audioLoader = new Three.AudioLoader();  let steps;  let speed;  // ...  this.init = (    scope,    // ...  ) => {    audioLoader.load('./audio/steps.mp3', (buffer) => {      steps = scope.audio.addAudioToHero(scope, buffer, 'steps', DESIGN.VOLUME.hero.step, false);      loaderDispatchHelper(scope.$store, 'isStepsLoaded');    });  };  this.setHidden = (scope, isHidden) => {    if (isHidden) {      // ...      steps.setPlaybackRate(0.5);    } else {      // ...      steps.setPlaybackRate(1);    }  };  this.setRun = (scope, isRun) => {    if (isRun && scope.keyStates['KeyW']) {      steps.setVolume(DESIGN.VOLUME.hero.run);      steps.setPlaybackRate(2);    } else {      steps.setVolume(DESIGN.VOLUME.hero.step);      steps.setPlaybackRate(1);    }  };  // ...  this.animate = (scope) => {    if (scope.playerOnFloor) {      if (!scope.isPause) {        // ...        // Steps sound        if (steps) {          if (scope.keyStates['KeyW']            || scope.keyStates['KeyS']            || scope.keyStates['KeyA']            || scope.keyStates['KeyD']) {            if (!steps.isPlaying) {              speed = scope.isHidden ? 0.5 : scope.isRun ? 2 : 1;              steps.setPlaybackRate(speed);              steps.play();            }          }        }      } else {        if (steps && steps.isPlaying) steps.pause();        // ...      }    }  };}export default Module;

Казалось бы шаги нужно делать лупом? Ан нет, не получится. И, например, вешать еще один последний шаг на окончание движения не позволит само устройство инерционного контрола собирающего много быстрых событий. С ним даже не особо ясно когда это вообще происходит когда позиция героя перестаёт изменяться нам уже не надо. Найденное мною решение простое и хорошо работает, дает нужный эффект. Сэмпл шага это ровно два шага. Если появляется событие клавиатуры запускаем аудио. И оно всегда отыгрывает до конца. Если персонаж переходит на бег или начинает ползти просто меняем скорость.

Тут надо упомянуть только об одном обнаруженном мною нюансе. Если аудио не луп с ним возникают проблемы при постановке и снятии с паузы. При следующих запусках отыгранная в предыдущий раз часть трека съедается пока он вообще не перестает звучать. Фиксит это данная строчка в функции добавления аудио на шину:

if (!isLoop) audio.onEnded = () => audio.stop();

Имейте ввиду!

import * as Three from "three";import { DESIGN, OBJECTS } from '@/utils/constants';import { loaderDispatchHelper } from '@/utils/utilities';function Module() {  const audioLoader = new Three.AudioLoader();  // ...  let material = null;  const geometry = new Three.SphereBufferGeometry(0.5, 8, 8);  let explosion;  let explosionClone;  let boom;  this.init = (    scope,    fireMaterial,    // ...  ) => {    // Звук наземных врагов - загружаем в инициализации на объекты через шину    audioLoader.load('./audio/mechanism.mp3', (buffer) => {      loaderDispatchHelper(scope.$store, 'isMechanismLoaded');      scope.array = scope.enemies.filter(enemy => enemy.name !== OBJECTS.DRONES.name);      scope.audio.addAudioToObjects(scope, scope.array, buffer, 'mesh', 'mechanism', DESIGN.VOLUME.mechanism, true);     });    // Звук взрыва - то есть - "добавляемой и уничтожаемой" сущности - загружаем и записываем в переменную    material = fireMaterial;    explosion = new Three.Mesh(geometry, material);    audioLoader.load('./audio/explosion.mp3', (buffer) => {      loaderDispatchHelper(scope.$store, 'isExplosionLoaded');      boom = buffer;    });  };  // ...  // ... где-то в логике врагов:  this.moduleFunction = (scope, enemy) => {    scope.audio.startObjectSound(enemy.id, 'mechanism');    // ...    scope.audio.stopObjectSound(enemy.id, 'mechanism');    // ...  };  // При добавлении взрыва на шину взрывов:  this.addExplosionToBus = (    scope,    // ...  ) => {    explosionClone = explosion.clone();    // ..    scope.audio.playAudioOnObject(scope, explosionClone, boom, 'boom', DESIGN.VOLUME.explosion);    // ..  };}export default Module;

Попробуйте подобрать и принять зеленый цветок запускающий машину времени в игре, классно? ))

Шина событий и сообщения

Очевидно что кроме аудиомикшера игре необходима еще одна одна шина: делей для событий модуль который будет задерживать отправку изменений в контексте игрового времени. Например, если было показано сообщение, а потом система поставлена на паузу мы не можем использовать нигде обычные таймауты только обновляемые в анимационном цикле часы Clock Three. Модуль хранит актуальные записи о связанных событиях и в нужный момент вызывает переданный коллбэк.

В игре присутствует интерфейс текстовых сообщений которые показываются в правом верхнем углу экрана. Сообщения могут быть двух видов: постоянные и временные. Первые должны быть видимы, например, когда герой кастит предмет или дверь и игра сообщает об этом. Вторые сообщают о состоявшихся игровых событиях, нумеруются и должны показываться в виде очереди на короткий интервал времени каждое. Для них категорически необходима функциональность шины событий.

Мир

Модель первой локацииМодель первой локации

В инициализации модуля мира по порядку:

  1. Загружаются все переиспользуемые в остальных модулях текстуры и создаются все такие материалы и геометрии.

  2. Загружается и разбирается модель уровня. На ее основе формируются массивы данных обо всех игровых объектах в OBJECTS и рабочие массивы, списки псевдообъектов для сущностей с одинаковой функциональностью в основном контексте.

  3. На примитивы которые должны стать видимыми элементами, строительными блоками мира накладываются текстуры. Места-пустышки обозначающие места рождения врагов или нахождения полезных предметов после учета удаляются.

  4. Двери переносятся в отдельную группу из основной модели. Из этих двух групп очищенной сцены и все дверей создаются стартовые октодеревья.

  5. Инициализируются все остальные модули.

Я разбираю один файл glb и как совершенно необходимый такой игре редактор уровней и как готовую модель для построения стартовых октодеревьев мира, и, отдельно дверей в нем, и как почти готовую не текстурированную основу самого примитивного визуального мира. Различать примитивы можно с помощью специфических маркеров в их наименовании. Это не самое надежное соглашение, оно чревато ошибками, но они легко обнаруживаются визуально при ручном тестировании. Изменения можно вносить очень быстро. Тут уже все зависит от вашей фантазии и выдуманного с помощью нее дизайна и геймплея, ну и количества времени которые вы можете на это потратить. Например, я использую маркер Mandatory если хочу чтобы цветок или бутылка были обязательными, если его нет постройка зависит от рандома. Или для механики включения-выключения информационных панелей собирается отдельный массив с их комнатами параллелепипедами определяющими объем в котором панель реагирует на персонажа. Для геометрии такого объекта следует сделать при инициализации:

room.geometry.computeBoundingBox();

room.visible = false;

И теперь у нас может быть вот такой публичный метод в модуле панелей и его забегая вперед можно будет использовать в модуле Атмосферы при проверках отношения панелей к герою:

// В @/components/Three/Scene/World/Screens.js:this.isHeroInRoomWithScreen = (scope, screen) => {scope.box.copy(screen.room.geometry.boundingBox).applyMatrix4(screen.room.matrixWorld); if (scope.box.containsPoint(scope.camera.position)) return true;return false;};

При постройке дверей я использую примитив из основного файла как массив самой двери, добавляя дизайн уже в джаваскрипте текстуру, маркеры уровня доступа с обеих сторон и псевдоmesh. Для правильной работы дверей все равно нужен некоторый такой дополнительный псевдообъем и подобная комнатам панелей логика для того чтобы не дверь не закрывалась когда герой находится в ее проеме и отскакивала если зашел в него во время закрытия.

Псевдообъект-помощник для двериПсевдообъект-помощник для двериДверь не закрываетсяДверь не закрывается

Совершенно точно что тут можно улучшить сделать пустышки обозначающие место рождения врагов не планами один на один метр а большими планами для наземных юнитов и трехмерными коробками для воздушных чтобы они могли появляться в случайной точке некоторого прямоугольника на поверхности или трехмерного объема. Но пока лень. )

Анимационный цикл мира это просто список анимационных циклов всех модулей врагов, объектов и оружия которые должны быть анимированы кроме героя. Если включена машина времени то он вызывается через раз.

Кастинг

Вот мы и добрались до самого интересного: кастинг и столкновения. Как сделать так, чтобы предметы можно было собирать, а герой и враги не сталкивались с миром и друг-другом. Для обоих механик я использую дополнительные невидимые примитивы, псевдоmesh`и. Они инициализируются и записываются в абстрактные объекты которыми оперирует система вместе с основным видимым и всеми необходимыми им флагами-свойствами. Для движущихся врагов еще записывается коллайдер Sphere. Псевдомеши идут на кастинг (предметы) или построение и обновление октодеревьев (враги). А коллайдер для проверки столкновения с ними.

Псевдообъекты-помощники для предметовПсевдообъекты-помощники для предметов

Геометрия и материал готовиться в мире перед инициализацией всех вещей и надежнее сделать материал двусторонними так кастинг будет работать даже если герой оказался внутри псевдообъекта:

// В @/components/Three/Scene/World.js:const pseudoGeometry = new Three.SphereBufferGeometry(DESIGN.HERO.HEIGHT / 2,  4, 4); const pseudoMaterial = new Three.MeshStandardMaterial({ color: DESIGN.COLORS.white, side: Three.DoubleSide,});new Bottles().init(scope, pseudoGeometry, pseudoMaterial);

В модуле конкретной вещи:

// В @/components/Three/Scene/World/Thing.js:import * as Three from 'three';import { GLTFLoader } from '@/components/Three/Modules/Utils/GLTFLoader';import { OBJECTS } from '@/utils/constants';import { loaderDispatchHelper } from '@/utils/utilities';function Thing() {  let thingClone;  let thingGroup;  let thingPseudo;  let thingPseudoClone;  this.init = (    scope,    pseudoGeometry,    pseudoMaterial,  ) => {    thingPseudo = new Three.Mesh(pseudoGeometry, pseudoMaterial);    new GLTFLoader().load(      './images/models/Objects/Thing.glb',      (thing) => {        loaderDispatchHelper(scope.$store, 'isThingLoaded'); // загружена модель        for (let i = 0; i < OBJECTS.THINGS[scope.l].data.length; i++) {          // eslint-disable-next-line no-loop-func          thing.scene.traverse((child) => {            // ... - тут "покраска" материалами частей вещи          });          // Клонируем объект и псевдо          thingClone = thing.scene.clone();          thingPseudoClone = thingPseudo.clone();          // Псевдо нужно дать правильное имя чтобы мы могли различать его при кастинге          thingPseudoClone.name = OBJECTS.THINGS.name;          thingPseudoClone.position.y += 1.5; // корректируем немного позицию по высоте          thingPseudoClone.visible = false; // выключаем рендер          thingPseudoClone.updateMatrix(); // обновляем          thingPseudoClone.matrixAutoUpdate = false; // запрещаем автообновление          // Делаем из обхекта и псевдо удобную группу          thingGroup = new Three.Group();          thingGroup.add(thingClone);          thingGroup.add(thingPseudoClone);          // Выставляем координаты из собранных из модели уровня данных          thingGroup.position.set(            OBJECTS.THINGS[scope.l].data[i].x,            OBJECTS.THINGS[scope.l].data[i].y,            OBJECTS.THINGS[scope.l].data[i].z,          );          // Записываем в "рабочие объеты" - по ним будем кастить и прочее          scope.things.push({            id: thingPseudoClone.id,            group: thingGroup,          });          scope.objects.push(thingPseudoClone);          scope.scene.add(thingGroup); // добавляем на сцену        }        loaderDispatchHelper(scope.$store, 'isThingsBuilt'); // построено      },    );  };}export default Thing;

Теперь мы можем тыкать направленным вперед лучом из героя в анимационном цикле Hero.js:

// В @/components/Three/Scene/Hero.js:import { DESIGN, OBJECTS } from '@/utils/constants';function Hero() {  // ...  this.animate = (scope) => {    // ...    // Raycasting    // Forward ray    scope.direction = scope.camera.getWorldDirection(scope.direction);    scope.raycaster.set(scope.camera.getWorldPosition(scope.position), scope.direction);    scope.intersections = scope.raycaster.intersectObjects(scope.objects);    scope.onForward = scope.intersections.length > 0 ? scope.intersections[0].distance < DESIGN.HERO.CAST : false;    if (scope.onForward) {      scope.object = scope.intersections[0].object;      // Кастим предмет THINGS      if (scope.object.name.includes(OBJECTS.THINGS.name)) {        // ...      }    }    // ...  };}export default Hero;

Кастинг очень полезен и для усовершенствования ИИ врагов. С помощью него возможно проверять имеет ли смысл, есть ли возможность двигаться-прыгать вперед, лететь вниз, делать выстрел. В утилитах:

// В @/utils/utilities.js:// let arrowHelper;const fixNot = (value) => { if (!value) return Number.MAX_SAFE_INTEGER; return value;};export const isEnemyCanMoveForward = (scope, enemy) => { scope.ray = new Three.Ray(enemy.collider.center, enemy.mesh.getWorldDirection(scope.direction).normalize()); scope.result = scope.octree.rayIntersect(scope.ray); scope.resultDoors = scope.octreeDoors.rayIntersect(scope.ray); scope.resultEnemies = scope.octreeEnemies.rayIntersect(scope.ray); // arrowHelper = new Three.ArrowHelper(scope.direction, enemy.collider.center, 6, 0xffffff); // scope.scene.add(arrowHelper); if (scope.result || scope.resultDoors || scope.resultEnemies) {   scope.number = Math.min(fixNot(scope.result.distance), fixNot(scope.resultDoors.distance), fixNot(scope.resultEnemies.distance));   return scope.number > 6; } return true;};

Для наглядной визуальной отладки подобных механик очень полезен объект Three ArrowHelper. Если мы включим его добавление на сцену в функции выше:

Отладка с включенными стрелочными помощникамиОтладка с включенными стрелочными помощниками

С помощью подобной простой функции можно добиться того что враги не реагируют на героя через стены строим луч от центра коллайдера к камере и сравниваем дистанцию от камеры до обнаруженного столкновения:

// В @/utils/utilities.js:export const isToHeroRayIntersectWorld = (scope, collider) => { scope.direction.subVectors(collider.center, scope.camera.position).negate().normalize(); scope.ray = new Three.Ray(collider.center, scope.direction); scope.result = scope.octree.rayIntersect(scope.ray); scope.resultDoors = scope.octreeDoors.rayIntersect(scope.ray); if (scope.result || scope.resultDoors) {   scope.number = Math.min(fixNot(scope.result.distance), fixNot(scope.resultDoors.distance));   scope.dictance = scope.camera.position.distanceTo(collider.center);   return scope.number < scope.dictance; } return false;};

Враги

Кроме инициализации самих низовых модулей врагов, модуль Enemies.js содержит и всю общую логику обслуживающую жизненный цикл врагов. Сейчас у меня четыре этапа-режима жизни у врагов:

// В @/utils/constatnts.js:export const DESIGN = {  DIFFICULTY: {    civil: 'civil',    anarchist: 'anarchist',    communist: 'communist',  },  ENEMIES: {    mode: {      idle: 'idle',      active: 'active',      dies: 'dies',      dead: 'dead',    },    spider: {      // ...      decision: {        enjoy: 60,        rotate: 25,        shot: {          civil: 40,          anarchist: 30,          communist: 25,        },        jump: 50,        speed: 20,        bend: 30,      },    },    drone: {      // ...      decision: {        enjoy: 50,        rotate: 25,        shot: {          civil: 50,          anarchist: 40,          communist: 30,        },        fly: 40,        speed: 20,        bend: 25,      },    },  },  // ...};
// В @/components/Three/Scene/Enemies.js:import { DESIGN } from '@/utils/constants';import {  randomInteger,  isEnemyCanShot,  // ...} from "@/utils/utilities";function Enemies() {  // ...  const idle = (scope, enemy) => {    // ...  };  const active = (scope, enemy) => {    // ...    // Где-то в логике агрессивного режима: решение на выстрел (если отдыхает)    scope.decision = randomInteger(1, DESIGN.ENEMIES[enemy.name].decision.shot[scope.difficulty]) === 1;    if (scope.decision) {      if (isEnemyCanShot(scope, enemy)) {        scope.boolean = enemy.name === OBJECTS.DRONES.name;        scope.world.shots.addShotToBus(scope, enemy.mesh.position, scope.direction, scope.boolean);        scope.audio.replayObjectSound(enemy.id, 'shot');      }    }  };  const gravity = (scope, enemy) => {    // ...  };  this.animate = (scope) => {    scope.enemies.filter(enemy => enemy.mode !== DESIGN.ENEMIES.mode.dead).forEach((enemy) => {      switch (enemy.mode) {        case DESIGN.ENEMIES.mode.idle:          idle(scope, enemy);          break;        case DESIGN.ENEMIES.mode.active:          active(scope, enemy);          break;        case DESIGN.ENEMIES.mode.dies:          gravity(scope, enemy);          break;      }    });  };}export default Enemies;

В зависимости от вспомогательных проверочных функций которые мы затрагивали выше, своего состояния (на полу или в прыжке, например, если это наземный юнит) каждый живой враг может с некоторой частотой принимать определенные решения.

Но! Самое важное на что нужно обратить внимание: в idle спокойном режиме полноценно двигается некоторое случайное время только один выбранный случайным образом враг. Остальные поворачиваются на месте + может и должна быть запущена анимация. Такая оптимизация позволяет действительно полноценно разгрузить систему.

Столкновения

Октодеревом в данном тексте обозначается максимально упрощенная модель 3D-пространства которое занимает некоторая группа объектов с минимально необходимым и достаточным для обсчета количеством граней, рёбер и вершин.

Такие октодеревья, как вы уже наверняка поняли из примера полезной функции из конца раздела про кастинг помогают нам грамотно обсчитывать кастинг лучей или столкновения коллайдеров героя/врагов с миром и другими объектами. В случае если персонаж движется и мы обнаруживаем столкновение октодерева с коллайдером капсулой героя или сферой врага вектору ускорения этого объекта добавляется вектор выталкивающий обратно центр его коллайдера (и все остальное, соответственно).

В текущей реализации используются три октодерева: мир: 1) пол, бетонные блоки, трубы, стекла, а также 2) двери и 3) враги. Каждый из врагов обсчитывает свои столкновения с персональным октодеревом врагов собранным без него.

Мы можем и должны обновлять октодеревья. Если дверь была открыта-закрыта или враг передвинулся нам нужно пересобрать соответствующее октодерево. При открытии закрытии двери лучше всего сделать это два раза: когда дверь открылась или закрылась настолько что герой уже может или наоборот больше не может пройти через нее и когда процесс открытия/закрытия завершен.

В моей реализации на объекты врагов инициализируется по две псевдо-коробки. Это не оптимально, но сделано в целях балансировки визуальной составляющих двух механик: 1) столкновение героя с врагами 2) столкновения врагов с миром и между собой. Визуальное тестирование показало что для реализации первой механики нужна коробка намного более меньшего размера чем для второй.

Точность обсчета столкновений с октодеревьями напрямую зависит от качества оптимизации анимационного цикла и ее непосредственного результата фактической производительности системы. Если скорость объекта высокая, но система начинает захлебываться, то есть фрейм, дельта анимационного цикла удлиняется обсчет с октодеревом не успеет компенсировать или даже вообще зарегистрировать столкновение и пользователь может отхватить глюк: герой или враг может быть вытолкнут через стену-стекло. Но не будем о грустном. )

Мы не можем обновлять октодерево врагов героя или персональные октодеревья врагов каждый раз когда вызывается обсчет столкновений наша система просто сразу ляжет в таком случае. Но достаточно делать это, например, каждые полсекунды. Обновили октодерево всех врагов для героя запускаем таймер на полсекунды. Точно также и с персональными октодеревьями врагов.

// В @/utils/constatnts.js:export const DESIGN = {  OCTREE_UPDATE_TIMEOUT: 0.5,  // ...};
// В @/utils/utilities.js:// Обновить персональное октодерево врагов для одного врагаimport * as Three from "three";import { Octree } from "../components/Three/Modules/Math/Octree";export const updateEnemiesPersonalOctree = (scope, id) => {  scope.group = new Three.Group();  scope.enemies.filter(obj => obj.id !== id).forEach((enemy) => {    scope.group.add(enemy.pseudoLarge);  });  scope.octreeEnemies = new Octree();  scope.octreeEnemies.fromGraphNode(scope.group);  scope.scene.add(scope.group);};
// Столкновения враговconst enemyCollitions = (scope, enemy) => {  // Столкновения c миром - полом, стенами, стеклами и трубами  scope.result = scope.octree.sphereIntersect(enemy.collider);  enemy.isOnFloor = false;  if (scope.result) {    enemy.isOnFloor = scope.result.normal.y > 0;    // На полу?    if (!enemy.isOnFloor) {      enemy.velocity.addScaledVector(scope.result.normal, -scope.result.normal.dot(enemy.velocity));    } else {      // Подбитый враг становится совсем мертвым после падения на пол и тд      // ...    }    enemy.collider.translate(scope.result.normal.multiplyScalar(scope.result.depth));  }  // Столкновения c дверями  scope.resultDoors = scope.octreeDoors.sphereIntersect(enemy.collider);  if (scope.resultDoors) {    enemy.collider.translate(scope.resultDoors.normal.multiplyScalar(scope.resultDoors.depth));  }  // Делаем октодерево из всех врагов без этого, если давно не делали  if (scope.enemies.length > 1    && !enemy.updateClock.running) {    if (!enemy.updateClock.running) enemy.updateClock.start();    updateEnemiesPersonalOctree(scope, enemy.id);    scope.resultEnemies = scope.octreeEnemies.sphereIntersect(enemy.collider);    if (scope.resultEnemies) {      result = scope.resultEnemies.normal.multiplyScalar(scope.resultEnemies.depth);      result.y = 0;      enemy.collider.translate(result);    }  }  if (enemy.updateClock.running) {    enemy.updateTime += enemy.updateClock.getDelta();    if (enemy.updateTime > DESIGN.OCTREE_UPDATE_TIMEOUT && enemy.updateClock.running) {      enemy.updateClock.stop();      enemy.updateTime = 0;    }  }};

Своя атмосфера

Модуль с романтическим названием Atmosphere.js отвечает за элементы мира которые одинаковы для всех локаций: свет, небо, и в дальнейшем взаимодействие между другими модулями в анимационном цикле работает посредником.

Если вывалится за стену и забежать за край небаЕсли вывалится за стену и забежать за край неба

Я долго придумывал и придумал как сделать прикольное небо, облака: это огромная вывернутая по одной из осей и вращающаяся текстурированная сфера.

При некотором изменении позиции героя (сейчас это 10 метров) модуль проверяет всех живых врагов и панели и меняет их режимы в зависимости от актуальной ситуации. Сейчас дроны не видят героя через толстые пуленепробиваемые стекла. Для того чтобы это изменить нужно выделить стекла в отдельное октодерево, как и двери.

Пуленепробиваемые стеклаПуленепробиваемые стекла

Да, это вам не React c TS и тестами в финтех и банки!

Выводы которые я могу сделать на основе практики создания браузерной FPS на Three:

  • Мы не можем использовать тени и множество источников света

  • Мы должны экономить память в анимационном цикле и использовать в нем только готовые переменные

  • Мы должны и во всех остальных возможных аспектах максимально тщательно оптимизировать анимационный цикл, кастинг и обсчет столкновений в нем в контексте геймплея, так, чтобы сохранить драйв, но избежать падения производительности

  • Статическая типизация и юнит-тесты ничем не могут помочь в данном эксперименте

В принципе, я доволен тем что сейчас уже получилось. И хочется довести это до полной красоты. Поэтому если вы знаете кого-то кто увлекается скелетной анимацией и может согласится добавить несколько простых треков на мои glb скиньте, пожалуйста ему ссылку на статью?

Подробнее..

Из Vue 2 на Vue 3 Migration Helper

09.06.2021 20:16:20 | Автор: admin

Предистория

Была у меня курсовая по веб-разработке, делать очередной интернет-магазин как-то не хотелось, и решил я написать помощник миграции из Vue 2 (options-api) в Vue 3 (composition-api) с авторазделением на композиции с помощью алгоритма Косарайю по поиску областей сильной связности

Для тех, кто не в теме, поясню, так выглядит код с options-api:

export default {  data () {    return {      foo: 0,      bar: 'hello',    }  },  watch: {    ...  },  methods: {    log(v) {      console.log(v);    },  },  mounted () {    this.log('Hello');  }}

и примерно так с composition-api:

export default {  setup (props) {    const foo = reactive(0);    const bar = reactive('hello');    watch(...);    const log = (v) => { console.log(v); };    onMounted(() => { log('hello'); });    return {      foo,      bar,      log,    };  }}

Автоматическое разделение на композиции

Дабы не отходить от самой идеи композиций, помимо трансляции кода под новый синтаксис composition-api, было принято решение добавить и возможность разделения монолитного компонента на самостоятельные композиции, и их последующее переиспользование в главном компоненте. Как же это сделать?

Сначала зададимся вопросом, что же такое композиции? Для себя я ответил так:

Композиции это самодостаточная группа блоков кода, отвечающих за один функционал, зависящих только друг от друга. Зависимости тут самое главное!

Блоками кода в нашем случае будем считать: свойства data, методы, вотчеры, хуки, и все то, из чего строится компонент Vue.

Теперь определимся на счёт зависимостей блоков кода между собой. С этим во Vue достаточно просто:

  • Если computed, method, hook, provide свойство внутри себя использует другие свойства, то оно от них и зависит

  • Если на свойство навешен вотчер, то вотчер зависит от наблюдаемого им свойства

  • и так далее :)

data: () => ({  array: ['Hello', 'World'], // block 1}),watch: {  array() { // block 2 (watch handler) depends on block 1    console.log('array changed');  },},computed: {  arrayCount() { // block 3    return this.array.length; // block 3 depends on block 1  },},methods: {  arrayToString() { // block 4    return this.array.join(' '); // block 4 depends on block 1  }},

Допустим, мы смогли пройтись по коду и выделить все-все зависимости свойств между собой. Как всё это делить на композиции?

А теперь абстрагируемся от Vue, проблемы миграции, синтаксиса и т.д. Оставим только сами свойства и их зависимости друг с другом.

Выделим из этого ориентированный граф, где вершинами будут свойства, а ребрами - зависимости между свойствами. А теперь самое интересное!

Алгоритм Косарайю

Алгоритм поискаобластей сильной связностив ориентированном графе. Заключается он в двух проходах в глубину по исходному и транспонированному графам и небольшой магии.

Никогда бы не подумал, что простое переписывание реализации из C на TS может быть таким проблемным :)

Так вот. Применяя данный алгоритм, мы и получим заветные композиции, состоящие из сгруппированных по связям свойств. Если же свойство оказалось одиноким и без пары, мы отнесем его к самому будущему компоненту, если же нет выделим группу в одну композицию, которую будем переиспользовать.

Поиск зависимостей

Примечание: во всех функциях компонента в options-api свойства доступны через this

Здесь немного грусти, поскольку искать зависимости в .js приходится так:

const splitter = /this.[0-9a-zA-Z]{0,}/const splitterThis = 'this.'export const findDepsByString = (  vueExpression: string,  instanceDeps: InstanceDeps): ConnectionsType | undefined => {  return vueExpression    .match(splitter)    ?.map((match) => match.split(splitterThis)[1])    .filter((value) => instanceDeps[value])    .map((value) => value)

Да, просто проходясь регуляркой по строкому представлению функции в поисках всего, что идет после this. :(

Более продвинутый вариант, но такой же костыльный:

export const findDeps = (  vueExpression: Noop,  instanceDeps: InstanceDeps): ConnectionsType | undefined => {  const target = {}  const proxy = new Proxy(target, {  // прокси, который записывает в объект вызываемые им свойства    get(target: any, name) {      target[name] = 'get'      return true    },    set(target: any, name) {      target[name] = 'set'      return true    }  })  try {    vueExpression.bind(proxy)() // вызываем функцию в скоупе прокси    return Object.keys(target) || [] // все свойства которые вызвались при this.  } catch (e) { // при ошибке возвращаемся к первому способу    return findDepsByString(vueExpression.toString(), instanceDeps) || []  }}

При использовании прокси вышло несколько проблем:

  • не работает с анонимными функциями

  • при использовании вызывается сама функция а если вы там пентагон взламываете?

Создание файлов и кода

Вспомним зачем мы тут собрались: миграция.

Используя все вышеописанное, получив разбитые по полочкам свойства, нужно составить новый код в синтаксисе composition-api, то есть собрать строки, которые в конечном счете будут являться содержимыми файлов в проекте.

Для этого надо уметь представлять экземпляры объектов, строк, массивов и всего остального в их естественном, кодовом, виде. Вот эта функция:

const toString = (item: any): string => {  if (Array.isArray(item)) {    // array    const builder: string[] = []    item.forEach((_) => {      builder.push(toString(_)) // wow, it's recursion!    })    return `[${builder.join(',')}]`  }  if (typeof item === 'object' && item !== null) {    // object    const builder: string[] = []    Object.keys(item).forEach((name) => {      builder.push(`${name}: ${toString(item[name])}`) // wow, it's recursion!    })    return `{${builder.join(',')}}`  }  if (typeof item === 'string') {    // string    return `'${item}'`  }  return item // number, float, boolean}// Exampleconsole.log(toString([{ foo: { bar: 'hello', baz: 'hello', }}, 1]);// [{foo:{bar: 'hello',baz: 'hello'}},1]  т.е. то же самое, что и в коде

Про остальной говнокод я тактично промолчу :)

Итоговые строки мы записываем в новые файлы через простой fs.writeFile() в ноде и получаем результат

Пример работы

Собрав всё это в пакет, протестировав и опубликовав, можно наконец увидеть результат работы.

Ставим пакет vue2-to-3 глобально (иначе не будет работать через консоль) и проверяем!

Пример HelloWorld.js:

export default {  name: 'HelloWorld',  data: () => ({    some: 0,    another: 0,    foo: ['potato'],  }),  methods: {    somePlus() {      this.some++;    },    anotherPlus() {      this.another++;    },  },};

Пишем в консоли: migrate ./HelloWorld.js и получаем на выход 3 файла:

// CompositionSome.jsimport { reactive } from 'vue';export const CompositionSome = () => {  const some = reactive(0);  const somePlus = () => { some++ };  return {    some,    somePlus,  };};// CompositionAnother.jsimport { reactive } from 'vue';export const CompositionAnother = () => {  const another = reactive(0);  const anotherPlus = () => { another++ };  return {    another,    anotherPlus,  };};// HelloWorld.jsimport { reactive } from 'vue';import { CompositionSome } from './CompositionSome.js'import { CompositionAnother } from './CompositionAnother.js'export default {  name: 'HelloWorld',  setup() {    const _CompositionSome = CompositionSome();    const _CompositionAnother = CompositionAnother();    const foo = reactive(['potato']);    return {      foo,      some: _CompositionSome.some,      somePlus: _CompositionSome.somePlus,      another: _CompositionAnother.another,      anotherPlus: _CompositionAnother.anotherPlus,    };  },};

Итого

На данный момент все это доступно и работает, но ещё есть некоторые баги со строковым представлением не анонимных функций и путями (в некоторых случаях фатально для linux систем)

В планах запилить миграцию для single-file-components и .ts файлов (сейчас работает только для .js)

Спасибо за внимание!

npm, git

Подробнее..

Как работать в команде, которая пишет на 5 языках

23.04.2021 12:19:27 | Автор: admin

Привет, Хабр! Меня зовут Евгений Сальников, я тимлид одной из команд доставки в компании Lamoda. В нашей команде используются сразу пять языков программирования: PHP, Go, Vue, Typescript, Java и Kotlin. Когда я впервые услышал об этом на собеседовании, подумал, что так работать невозможно все слишком разное. Но спустя год мое мнение кардинально изменилось, и я нашел много преимуществ в таком подходе.


В этой статье я расскажу:


  • о нашем направлении и системах, обслуживающих доставку;
  • как мы живем с таким разнообразием технологий;
  • как планируем работу над разноплановыми задачами;
  • как растим экспертизу в fullstack-команде и расширяем направление.


Почему fullstack?


Мы часто слышим про fullstack это устоявшееся понятие, даже избитое. Оно подразумевает, что один человек может пилить и бекенд, и фронтенд все в одном флаконе. Сейчас в нашем направлении уже четыре команды из 27 человек, пять языков и четыре системы.


  1. DataMatrix это система для маркировки товаров. В нашей стране существует закон 487-ФЗ, по которому мы обязаны помечать ряд товаров QR-кодом. Маркировка содержит информацию о том, кто произвел этот товар, кто ввез в страну, когда он поступил в продажу. Это помогает понять, насколько легальная вещь лежит перед нами. Подробнее о DataMatrix рассказывали в отдельной статье.
  2. Система Express стоит в каждом пункте выдачи заказов и во всех транзитных складах. Она целиком написана с нуля внутри Lamoda, поэтому там учтены все наши процессы и потребности. На текущий момент вся система доставки построена на Express.
  3. Система XDC взаимодействует с внешними службами доставки с Почтой России, DPD и остальными.
  4. Также в зоне ответственности нашего направления мобильное приложение для торговых представителей, у которых есть планшет или телефон с ПО нашей разработки.

Мы стараемся стандартизировать все системы. Например, если что-то пишем на РНР, то делаем это одинаково: используем одинаковые фреймворки и библиотеки. Но есть задачи, которые нужно решать иными способами. И в таком случае я не разделяю подход, что все можно написать на одном языке программирования. Как правило, это не всегда оправдано и может приводить к неожиданностям. Для разных задач уже существуют готовые наработки или другие технологии. На мой взгляд, это нормально.


Мой любимый пример Apache Camel. Это интеграционный фреймворк на Java, который, грубо говоря, перекладывает данные из одного места в другое. У нас в компании эта технология обеспечивает взаимодействие с внешними курьерскими службами: мы получаем данные о заказе из API, преобразовываем их и отправляем в курьерскую службу. Написать эту задачу на РНР возможно, но будет неоправданно, потому что Apache Camel и так уже создан для этих целей. Такой подход позволяет легче адаптировать новые службы и новые API, тратить меньше времени на преобразование запроса из Json в XML. В Lamoda это адаптированная технология: если одна команда научилась ее готовить, мы делимся знаниями с коллегами. Сейчас Apache Camel используется уже в четырех командах.


Распределение ролей, покер-планирование и рост экспертизы


Раньше мне казалось, что существует жесткое разделение по системам. Например, пять человек занимаются одной системой, еще пять занимаются другой и просто взаимодействуют друг с другом. У нас такого нет. Каждый инженер команды может дорабатывать любую систему.



Например, у нас есть задача сделать удобную форму возврата. Для контроля технической стороны этого проекта мы назначаем техлида, которым может стать любой инженер из команды. Уже заранее он понимает, что для поставленной цели придется затронуть две системы: Express и XDC.


На схеме видно, что у Express и XDC также есть техлиды. У Express их даже два это очень большая система. Таким образом, техлид проекта сообщает техлидам систем о необходимых доработках, составляет и декомпозирует задачи, следит за тем, чтобы работа шла консистентно.


В то же время на всем направлении работает системный архитектор. Это серьезный специалист, который помогает всей разработке двигаться в едином направлении.


Планировать и оценивать задачи в нескольких системах сразу достаточно сложно, потому что может получиться неточный результат. В нашем подходе мы живем по двухнедельным спринтам. Встречаемся на общих встречах на грумингах и планнингах и используем покер-планирование для командной оценки задач по системе. Каждый человек оценивает все задачи, исходя из собственного опыта и экспертизы. Таким образом, мы получаем средние оценки выполнения всех задач, которые отображают реальные сроки.



Как я уже упоминал ранее, любой инженер может дорабатывать каждую из наших систем. Допустим, человек начинал работать только с одной из них чаще всего с Express, так как все наши системы и процессы так или иначе связаны с доставкой. Новичку мало понимать ее технически, требуется знать предметную область и разбираться в процессах, которые там происходят. Таким образом, человеку, который изучил одну систему, будет намного проще работать над задачками по доработке другой. Порог вхождения во что-то новое становится гораздо ниже.


Приведу несколько примеров нашего подхода к работе:


  • Раньше я часто сталкивался с тем, что есть отдельная команда для бэкенда и API мобильного приложения, а другая команда дорабатывает само приложение. Это требует формализации всех процессов и задач. У нас же один и тот же человек в одном спринте дорабатывает бэкенд и API мобильного приложения, да и само мобильное приложение. И это не разные задачи, а один проект по общим изменениям. На мой взгляд, это позволяет двигаться быстрее.
  • Следующий пример о том, как мы близки к инфраструктуре. В нашем направлении используется K8s и Atlassian. Все скрипты для разворачивания новых серверов или деплоя приложения также создаются внутри команды. Любой из наших инженеров может поправить скрипт деплоя или что-то написать на Ansible, чтобы развернуть новый сервер. Благодаря этому мы быстрее делаем доработки.
  • В нашей команде есть сервисы, написанные на Go, но исключительно утилиты. Часто бывает так, что нам нужно запросить миллион Data matrix у внешнего API. В этом случае писать большие истории на РНР невыгодно, потому что для этих целей создан Go. Это бы усложнило ситуацию, если инженер совсем не знаком со сторонними API и с нашими процессами. Но наши ребята могут написать нужную утилиту на подходящем языке. Go адаптирован к нашей компании. У нас есть экспертиза в этом, мы можем пойти к соседним командам и уточнить у них все вопросы.

Год назад я не верил, что такая работа возможна. Но все мои страхи о том, что ее невозможно планировать, не подтвердились. Практика показывает, что fullstack-работой можно управлять и такой подход значительно развивает экспертизу в команде.


Мои итоги спустя год: как расширять fullstack-команду


Fullstack-программист стоит дороже, чем остальные. В HR-отделе на вас посмотрят с недоумением, когда вы попросите найти специалиста, который умеет Go, Java, Kotlin и отлично знает РНР.


Есть три способа увеличить команду:


1. Брать со стороны. Чтобы попасть в нашу команду, не нужно знать все пять языков. На старте большинство из нас были с ними не знакомы. Достаточно знать РНР и SQL и не бояться работы с остальными технологиями и языками. Мы на каждом интервью сразу же говорим о том, что у нас используется несколько языков.


Также у нас выстроен процесс онбординга он длится 3 месяца. Каждый новичок получает план из набора технических задач, которые в совокупности охватывают все наши системы и технологии. Например, сначала РНР-разработчик начинает знакомиться с системой Express, а потом может выбрать другие задачи, исходя из своих интересов: кому-то больше интересен Kotlin, кто-то уже имеет экспертизу на Go. Также происходит постепенное погружение и в процессы команды: дежурства, знакомство с системой мониторинга, выполнение саппорт-задач.


2. Растить внутри. Процесс, запущенный на онбординге, не заканчивается ровно через 3 месяца. Человек по-прежнему продолжает погружаться в наши процессы, а его технологическая экспертиза растет. Со временем он может занять роль техлида. Для этого инженер знакомится сначала с системой, потом с бизнес-требованиями к ней, а со временем понимает, что бизнес-требования слишком велики для одной задачи. В таком случае он может стать техлидом этого проекта или системы. Погружение в такую работу происходит только через практику.


Новичкам же мы даем исключительно технические задачи, которые не сильно влияют на бизнес-составляющие. Поэтому там нет горящих сроков, и никто не будет торопить человека, который только недавно начал разбираться в новой области знаний. Мы понимаем, что если сотрудник впервые видит систему и эту технологию, погружение займёт какое-то время. С ростом вовлеченности инженера растет бизнес-составляющая этих задач и экспертиза сотрудника.


В таком подходе важно понимать, что разработчик это инженер. Для меня инженер это человек, который продолжает учиться, искать что-то новое и понимает, какие технологии лучше подходят для решения конкретной задачи. Ему важно постоянно развиваться, не зацикливаясь на чем-то одном. Я верю, что смена проекта помогает разнообразить жизнь инженера. Очень здорово, когда в команде есть несколько проектов, между которыми можно переключиться и попробовать разное. Это не даст заскучать.


3. Смена технологий. Это тоже отличный путь развития. Сначала я познакомился с РНР, и казалось, что на нем можно сделать абсолютно все. Но сейчас будет странно писать на РНР постоянно живущие воркеры. Для этого можно использовать другие языки. Или, например, когда я познакомился с Go, это казалось крутым, но чего-то не хватало, зная количество готовых библиотек для Java. Новые инструменты помогают лучше понять те, которыми уже владеешь.


За неполный год в мою команду добавилось еще 10 инженеров. Сейчас это уже целое направление. Но не все готовы работать в таком подходе: кому-то не зашёл фронтенд, кому-то не заходит разнообразие технологий. Это не значит, что ушедшие люди плохие инженеры. Мы работаем в огромной компании, поэтому возможен переход в другие команды внутри Lamoda. Бывают и обратные случаи, когда люди из других команд переходят в наше направление, и это тоже круто.


Новые языки и комплектность команды


Мы уже сталкивались с появлением новых языков: кто-то сразу же на них кидается, кто-то ждет, когда появятся стабильные версии. Мы не отрицаем тот факт, что какие-то задачи можно реализовать легче или быстрее на других технологиях. Да, не нужно зацикливаться на чем-то одном, но лучше здраво подходить к каждому выбору. Если появятся новые языки или новые технологии, которые позволят нам двигаться быстрее, конечно, мы их рассмотрим.


Также иногда встает вопрос о комплектности команды: сколько человек должны знать каждый язык. Такого минимума у нас нет. Приведу пример: человек, который знает один из языков, взял бы переходящую роль техлида и проект, где предполагается использовать новую технологию. У нас есть архитектурные комитеты, где мы доказываем, что такая технология оправдана. Если она уже адаптирована в компании, то мы можем брать ее в работу без архитектурного комитета. Техлид нарезает задачи часть из них будет исключительно техническими. В процессе реализации остальные участники команды познакомятся ближе с нужной технологией. Таким образом, мы взращиваем экспертизу. И постепенно все больше и больше инженеров знакомится с новой технологией. В конечном счете этот проект в команде будет реализовывать точно не один человек.


Подведу итог: расширять fullstack-команду возможно. Практика показывает, что можно брать инженеров со стороны и можно растить их внутри компании. Если у вас есть свой опыт формирования fullstack-команды, поделитесь им в комментариях.

Подробнее..

Категории

Последние комментарии

  • Имя: Murshin
    13.06.2024 | 14:01
    Нейросеть-это мозг вселенной.Если к ней подключиться,то можно получить все знания,накопленные Вселенной,но этому препятствуют аннуннаки.Аннуннаки нас от неё отгородили,установив в головах барьер. Подр Подробнее..
  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru