Select your region
and interface language
We’ll show relevant
Telegram channels and features
Region
avatar

Влад Кибенко // qbnk // Mini Apps, Development and Me

heyqbnk
Стример, разработчик, блогер. Улучшаю платформу Telegram Mini Apps, создаю исключительные продукты, выступаю на конференциях. Лобби: t.me/heyqbnk_chat Twitch: twitch.tv/qbnk
Subscribers
1 660
24 hours
2
30 days
-2
Unusual 24 hours drop
Post views
751
ER
45,24%
Posts (30d)
21
Characters in post
1 290
Insights from AI analysis of channel posts
Channel category
Technology and Apps
Audience gender
Male
Audience age
25-34
Audience financial status
Middle
Audience professions
Technology & Software Development
Summary
April 12, 16:06

👩‍💻
fp-ts fp-ts когда-то был для меня очень большим открытием. Если я правильно понимаю, то эта библиотека представляет собой набор функций, чаще всего используемых в мире функционального программирования. Над документацией, конечно, стоит поработать в контексте…

April 12, 14:43
Media unavailable
1
Show in Telegram

👩‍💻
fp-ts
fp-ts
когда-то был для меня очень большим открытием. Если я правильно понимаю, то эта библиотека представляет собой набор функций, чаще всего используемых в мире функционального программирования. Над
документацией
, конечно, стоит поработать в контексте примеров использования, но как заявляется на главной странице, вы должны знать, что такое функциональное программирование, и какие абстракции и паттерны в нём существуют. Тогда, возможно, использование библиотеки будет более интуитивным.
Абстракции
Из этой библиотеки я использую преимущественно 3 абстракции:

Option
. Это абстракция, описывающая контейнер с опциональным значением. Если мысленно представить это в виде типа на TypeScript, то будет что-то вроде такого:
type Option = (
| { kind: 'fulfilled', value: T }
| { kind: 'empty' }
);
Такая абстракция, например, позволяет однозначно понять, возвращенное результат является пустым, либо же нет? Тогда не придётся строить логику вокруг falsy-значений из JS. Пример значения, которое может вызвать путаницу — это пустая строка (
''
). Это пустое значение, либо же ожидаемый заполненный результат? Неизвестно, потому что всё зависит от того, как реализовали функцию, которая это значение возвращает. Абстракция
Option
этой двоякости позволяет избежать.
Пример использования:
import * as fp from 'fp-ts';
function getUserAgent() {
return typeof window === 'undefined' ? fp.option.none() : fp.option.some(navigator.userAgent);
}
fp.function.pipe(
getUserAgent(),
fp.option.match(
() => console.log('No user agent'),
ua => console.log('User agent', ua),
)
);

Either
. Абстракция, которая уже ближе к теме. Она представляет собой контейнер с 2 "слотами". Значение может быть лишь в одном из слотов — либо в левом, либо в правом. В левом слоте принято хранить ошибку, в правом — значение успешного выполнения. Эта абстракция уже используется повсеместно, и вот вам немного модифицированный предыдущий пример:
import * as fp from 'fp-ts';
function getUserAgent() {
return typeof window === 'undefined'
? fp.either.left(new Error('Called on server side'))
: fp.either.right(navigator.userAgent);
}
fp.function.pipe(
getUserAgent(),
fp.either.match(
error => console.log('Error occurred', error),
ua => console.log('User agent', ua),
)
);
Заключение
Прелесть
fp-ts
заключается в том, что она позволяет описывать те ошибки, что может выбрасывать (в нашем случае возвращать) функция. Таким образом, вам не надо ковыряться в каком-нибудь JSDoc, равно как и поддерживать его в актуальном состоянии. Сами типы функции будут просто говорить о том, что конкретно может пойти не так. Тут, наверное, можно провести какую-то аналогию с GoLang, только в случае TypeScript чаще описываются конкретные ошибки, когда в GoLang принято всё скрывать за интерфейсом
error
.
Ну и наконец, эта же библиотека позволяет сообщать о том, что функция в принципе способна возвращать ошибку. Согласитесь, приятно понимать, может ли функция вернуть какую-либо ошибку? В довесок, это вполне себе хорошо читается из типов функции. Вот пример из
@tma.js/bridge
:
export declare function retrieveRawInitDataFp(): E.Either>;
Здесь мы точно понимаем, что функция может вернуть ошибку
RetrieveRawInitDataError
, а может вернуть опциональную строку (init data может отсутствовать, и это ожидаемо, не является ошибкой).
Несмотря на то, что библиотека повсеместно используется в
@tma.js
, было бы некорректным обязать разработчиков её использовать только потому, что она мне нравится. По этой причине одна и та же функция предоставляется в 2 вариациях — под использование в fp-ts, а также под традиционное использование с выбросом ошибок.
На самом деле,
fp-ts
стоит посвятить отдельный пост, потому что функционала там выше крыши. Например, можно "pipe-ить" вызовы. В своих примерах вызовом
fp.function.pipe
я на этом намекнул.
Но об этом как-нибудь в другой раз.

April 12, 14:43
Media unavailable
1
Show in Telegram

👩‍💻
Как я обрабатываю ошибки и сохраняю типизацию
Обработка ошибок, пожалуй, это один из самых неприятных процессов, которые есть в разработке программного обеспечения. Нам всегда хочется, чтобы система работала идеально и ожидаемо, но реальность всегда немного иная, и иногда что-то идёт не так. По этой причине мы с вами всегда должны думать на тему того, как вообще обрабатывать такие исключительные случаи.
Несмотря на то, что обрабатывать ошибки не так уж и приятно, понимание необходимости этого процесса является важным отличием опытного разработчика от неопытного, желающего сделать продукт полноценным, устойчивым от безразличного.
В этом посте я расскажу о том, какие инструменты использую для работы с ошибками, как сохраняю при этом типизацию, и как почти не использую
try catch
.
👩‍💻
error-kid
Начнем с библиотеки, которую я когда-то написал лично для создания классов ошибок, просто упаковав это в несколько разных функций под разные цели. Её я использую почти во всех своих проектах, а также в семействе
@tma.js

error-kid
.
По сути, библиотека представляет из себя набор из 2 функций —
errorClass
и
errorClassWithData
, а используется так:
import { errorClass, errorClassWithData } from 'error-kid';
class UnknownError extends errorClass({ name: 'UnknownError' }) {}
const error = new UnknownError();
error.message; // ''
error.cause; // undefined
error instanceof Error; // true
error instanceof UnknownError; // true
UnknownError.is(new Error); // false
UnknownError.is(error); // true
class TimeoutError extends errorClassWithData<
{ duration: number },
[duration: number, cause?: unknown]
>({
name: 'UnknownError',
data: duration => ({ duration }),
super: (duration, cause) => [`Timed out: ${duration}ms`, { cause }],
}) {}
const err = new TimeoutError(1000);
err.data; // { duration: 1000 }
err.message; // "Timed out: 1000ms"
err.cause; // undefined
На самом деле, в разработке можно обойтись и без неё, просто иногда придётся дублировать один и тот же полезный функционал из одного класса в другой (статичный метод
is
, например). Чтобы этого избежать, я просто использую библиотеку.

April 11, 09:35

Трансляция запущена!
Разработка и просмотр связанного видео-контента !project
— Software and Game Development

twitch.tv/qbnk

April 10, 08:41
Media unavailable
1
Show in Telegram

April 09, 10:32
Media unavailable
1
1
Show in Telegram

AppStore
🥸

April 03, 11:22

Давайте теперь по поводу прогресса с Платформером.
— Новые лаунчер и админку я-таки выкатил, они уже работают, но есть одно
НО
— в лаунчере есть визуальные недоработки + ошибка гидратации. Их надо посмотреть и поправить.
— Монорепо Платформера необходимо актуализировать. Несколькими постами раннее я писал, что слои из Nuxt мне не подходят, и придётся переключаться на монорепо. Это обязательно надо сделать, потому как из-за обнаружившейся проблемы пришлось отключить type-checking.
— Хочется попробовать Vue + Storybook, чтобы понимать, как выглядят текущие компоненты из UI Kit-а. Ну и вдобавок хочется это задеплоить.
Этим мы будем заниматься на ближайших стримах. После этого вернёмся к backend-у Платформера на GoLang.

April 03, 08:28
Media unavailable
1
Show in Telegram

🙂
Вчера я попробовал
@tanstack
/vue-virtual
@tanstack/vue-virtual
— это библиотека, предназначенная для реализации виртуализации во Vue. Сначала я попробовал более легковесную реализацию через composable из
@vueuse/core
, именуемый
useVirtualList
, но понял, что его функционала мне недостаточно. Могу сказать, что реализацию от tanstack буду использовать и дальше, потому что функционал библиотеки достаточно широкий, и закрывает множество проблем.
В этом же посте я хотел бы рассказать такой механизм во frontend-разработке, как
виртуализация
, и почему важно о нем знать.

Виртуализация
Начнем с определения. Виртуализация — это механизм, который позволяет отрисовывать список элементов таким образом, что в DOM будет присутствовать лишь необходимое для корректного отображения количество элементов.
Представьте, что вы реализуете какую-нибудь бесконечную ленту. Спустя несколько минут doom-scrolling-a, пользователь просмотрит уже внушительное количество элементов. Предположим, что количество их будет равно 300 элементам. По отношению к браузеру будет неуважительным отрисовывать все эти 300 элементов, особенно учитывая тот факт, что пользователь видит лишь 3-4 элемента в каждый момент времени. Неуважительно потому что это будет создавать лишнюю нагрузку на сам браузер. Ну и чем больше скроллим, тем больше элементов будет копиться.
Проблема будет куда заметнее, когда этот список надо отрисовать сходу. Как пример — восстановить состояние ленты и скролла. Представьте, если пользователь успел наскроллить на 1000 элементов, и тут мы, восстанавливая состояние, начинаем рендерить 1000 элементов. Лицо браузера представили?
Ощущая эту безобразие, вам в голову должен прийти вопрос — "а как сделать так, чтобы мы всё так же имели возможность скроллить ленту, но отображать лишь те элементы, что должны быть видны в текущий момент времени?". Ну и ответ, очевидно, очень простой — при помощи
виртуализации
.
Устройство механизма достаточно простое — "виртуализатор" знает с какими элементами работает, знает их высоты, знает высоту видимой области, знает количество. Он предоставляет методы, которые позволяют определить, какие элементы сейчас видны и какие у них должны быть смещения относительно начала ленты. В реализации от tanstack всё просто:
1.
Вычисление высоты ленты
. Она равна сумме высот всех элементов вместе с некоторыми другими значениями. Предположим, это будет 18.000px. Лента должна находиться внутри scrollable-контейнера, в виртуализатор передается этот контейнер для трекинга изменений видимой области.
2.
Расположение элементов
. В примерах в документации библиотеки предлагается делать ленту с относительным позиционированием, каждый элемент расположить абсолютно в начале ленты, а сдвигать при помощи
transform: translateY(...)
. Значение для translateY библиотека нам передает.
Конечный CSS примерно такой:
.scrollable-container {
height: 500px;
overflow: auto;
}
.feed {
position: relative;
}
.feed-item {
position: absolute;
top: 0;
left: 0;
right: 0;
}
Что мы получаем в итоге:
1. Scrollable-контейнер, который представляет собой ленту.
2. Динамически отрисовываемые 3-4 элемента вместо 300, в зависимости от текущего положения скролла.
3. Благодарность пользователя и браузера.

March 30, 17:04
Media unavailable
2
2
Show in Telegram

Вот и пришел, наконец, мой Мустанг из мира самокатов — White Siberia Teverun Fighter. 6-киловаттный полноприводный зверь для игры в боулинг.
Как уже и говорил, моим прежним питомцем был минималистичный, шустрый и крепкий Kugoo G1 Jilong, который я тоже считаю отличным первым самокатом. Конечно, сравнивать его с новым транспортом смысла особого нет. Разница в 4 киловатта, примерно 4 килограмма веса, а также в оснастке.
Первые впечатления мы уже испытали. Новый самокат гонит так, будто пытается отправить тебя в гроб, что не может не радовать. Несмотря на это, я не планирую ездить на нем выше 50 км/ч, и выбираться на дороги. Я ценю свою жизнь и жизнь других людей, поэтому вожу предельно осторожно и стараюсь не нарушать правил (хоть уже это делаю, управляя таким самокатом). Быть может, когда сдам на права категории А (мотоциклы), куплю «броню», тогда и выберусь на дороги. А до тех пор, о пределе самоката в ~85 км/ч и думать не буду.
Крутой девайс, ничего не скажешь. Новичкам и адекватным людям не рекомендую — убьётесь
😬

March 28, 08:30
Media unavailable
1
Show in Telegram

🥳
Сегодня у меня юбилей — 30 лет! Этот День Рождения подкрался как-то незаметно. На самом деле, с каждым годом следишь за этим всё меньше и меньше, а обращаешь внимание только на более-менее знаменательные отметки, юбилеи. Словами не описать, как время…