Chulakov Dev
1.25K subscribers
109 photos
3 videos
191 links
Канал команды разработки Студии Олега Чулакова.

Советы по Frontend- и Backend-разработке web-сервисов, мобильных приложений, статьи и презентации от наших разработчиков, анонсы проектов и многое другое.
Download Telegram
Что будет выведено в консоль?
Anonymous Quiz
41%
5
18%
10
16%
Синтаксическая ошибка
26%
undefined
Container-query — это долгожданная возможность в CSS, которая появилась в последних версиях браузеров. Она позволяет применять стили к элементам в зависимости от свойств их родительского контейнера. Это дает гораздо большую гибкость при создании адаптивных и резиновых layout.

Container-query работает аналогично медиа запросам, только вместо размеров viewport проверяются размеры родителя элемента.

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

Это избавляет от лишних дополнительных оберток, упрощает структуру HTML и делает CSS-код более чистым и читабельным.

@chulakov_dev
Inline-функции — это механизм в Kotlin, который позволяет встраивать код функции непосредственно в вызывающий код вместо создания дополнительной функции на стеке вызовов. Это уменьшает накладные расходы на вызов функции высшего порядка и оптимизирует производительность.

Плюсы inline-функций

Уменьшение накладных расходов.
Избегая создание дополнительных функций и стека вызовов, inline-функции уменьшают накладные расходы на выполнение кода.

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

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

Представим, что у нас есть собственное расширение для Collection:

fun  Collection.filter(predicate: (T) -> Boolean): Collection = //…
val numbers = listOf(1, 2, 3, 4, 5)
val evenNumbers = numbers.filter { it % 2 == 0 }

При компиляции этого кода в Java создается объект специального типа и вызывается метод invoke для каждого элемента. Пример преобразования кода в Java будет выглядеть так:

// Код на Java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = CollectionsKt.filter(numbers, new Function1() {
@Override
public Boolean invoke(Integer it) {
return it % 2 == 0;
}
});

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

Чтобы избежать такой ситуации, в Kotlin мы можем использовать ключевое слово inline перед функцией высшего порядка. Это позволяет компилятору встроить код лямбда-выражения непосредственно в вызывающий код. Таким образом, мы избежим создания дополнительного объекта Function1 и вызова метода invoke, что повысит нашу производительность.

@chulakov_dev
Всем привет!

Несколько лет назад в Android-приложениях для доставки функциональности пользователю применялся один цельный APK-файл, содержащий все компоненты приложения. С появлением Dynamic Delivery в 2018 году разработчики получили возможность собирать и доставлять части приложения в виде Dynamic Feature Modules (динамических модулей).

Сегодня расскажем, что такое Dynamic Features, какие возможности они предоставляют и как их можно использовать для улучшения гибкости и производительности в Android-приложениях.

Dynamic Features — это способ организации Android-приложения путем разделения его на небольшие модули, которые можно загружать и устанавливать на устройство пользователя по требованию.

Возможности Dynamic Features

➡️ Dynamic Features позволяют разделить приложение на модули, загружая только те, которые требуются, и таким образом значительно сократить размер установочного файла.

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

➡️ Разработчики могут работать над отдельными модулями независимо, что способствует быстрой разработке и более эффективному тестированию функциональности.

➡️ С Dynamic Features вы можете предоставить пользователям возможность загружать дополнительные функции по их выбору, делая приложение более персонализированным.

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

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

@chulakov_dev
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет, друзья!

На прошлой неделе у нас был пост про inline-функции. В продолжение темы расскажем о reified-типах в Kotlin.

Ключевое слово reified в Kotlin используется только в inline-функциях. Reified позволяет получить информацию о типе generic-параметра во время выполнения программы. В обычном случае информация о типах стирается и недоступна во время выполнения, но с помощью reified ее можно сохранять и использовать в других частях приложения.

Пример применения reified-типа с inline-функцией:

inline fun <reified T> printType() {
println(T::class.simpleName)
}

fun main() {
printType<Int>() // Вывод: Int
printType<String>() // Вывод: String
}

Преимущества использования reified-типов с inline-функциями

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

Безопасность типов. Reified-типы предотвращают затирание типов данных во время выполнения, обеспечивая более высокую типовую безопасность.

Удобство использования. Применение reified-типов сокращает необходимость передачи информации о типе вручную, что делает код более чистым и читаемым.

Inline-функции и reified-типы предоставляют разработчикам мощные инструменты для оптимизации производительности, улучшения безопасности типов и структуры кода. Эти инструменты могут быть особенно полезными при работе с функциями, обеспечивая более эффективную и надежную разработку на языке Kotlin.

@chulakov_dev
Друзья, привет!

В честь Дня знаний предлагаем решить задачку☝🏼
Пишите свои варианты в комментариях. Правильный ответ выложим там же.

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

Инвариантность
Инвариантность означает, что тип является неизменным относительно других типов. В контексте обобщений, если у вас есть обобщенный тип например MyType<T>, MyType<A> и MyType<B> не считаются связанными друг с другом, даже если типы A и B связаны отношением наследования.

Ковариантность
Ковариантность позволяет использовать подтипы вместо основного типа. Это означает, что если у вас есть обобщенный тип MyType<out T>, то MyType<A> является подтипом MyType<B>, если A является подтипом B. Ковариантные типы могут использоваться только в «выходных» позициях.

fun main() {
val producerA: Producer<A> = ProducerImplA()
val producerB: Producer<B> = producerA
}
interface Producer<out T> {
fun produce(): T
}
class A : B()
open class B

В этом примере Producer<A> является подтипом Producer<B>, поэтому мы можем присвоить producerA (тип Producer<A>) переменной producerB (тип Producer<B>).

Контравариантность
Контравариантность позволяет использовать супертипы вместо основного типа. Это означает, что если у вас есть обобщенный тип MyType<in T>, то MyType<A> является супертипом MyType<B>, если A является супертипом B. Контравариантные типы могут использоваться только во «входных» позициях.

fun main() {
val consumerB: Consumer<B> = ConsumerImplB()
val consumerA: Consumer<A> = consumerB
}
interface Consumer<in T> {
fun consume(item: T)
}
open class B
class A : B()

В этом примере Consumer<B> является супертипом Consumer<A>, поэтому мы можем присвоить consumerB (тип Consumer<B>) переменной consumerA (тип Consumer<A>).

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

@chulakov_dev
Привет, друзья!

15-16 сентября в Ульяновске пройдет международная IT-конференция «Стачка», на которой выступит наш Frontend Team Lead Альберт Рыбалко с докладом «Таймеры в HTML5».

Спикер расскажет, что такое Host и почему он важен при работе с Web Api, подробно разберет работу таймеров и методы их очистки, а также объяснит, как микрозадачи выполняются в окружении JavaScript и какую роль они играют в асинхронном программировании.

Альберт выступит в секции «FrontEnd» 16 сентября.

@chulakov_dev
Всем привет!

Сегодня затронем важную тему — поверхностное и глубокое копирование в JavaScript.

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

Читать пост

@chulakov_dev
Продолжаем серию заметок про запуск проекта с использованием Strapi. В первом посте мы рассказали, как собрать свой образ Strapi, а во втором — как запускать его в docker-окружении. Сегодня мы настроим подключение Strapi к базе данных Postgres.

👉🏻 Читать инструкцию

@chulakov_dev
Привет, друзья!

Предлагаем сегодня решить такую задачку. Нужно вывести в консоль числа от 0 до 10. Как вы думаете, будет ли код на картинке работать? Напишите в комментариях: да/нет и почему?

@chulakov_dev
infer в примерах

Ключевое слово infer используется в контексте условных типов TypeScript для их автоматического вывода в определенных ситуациях и не может применяться вне расширения.

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

Другими словами, когда вы сталкиваетесь со сложными типами в TypeScript, infer помогает вытащить или определить неизвестные части этих типов, делая ваш код более гибким и легким для понимания.

Вы можете представить его как способ сказать TypeScript: «Выясни сам, какой здесь тип, и дай мне его использовать». Это особенно полезно, когда вы работаете с функциями или классами, где точные типы не всегда ясны из контекста.

Примеры применения ключевого слова infer.

1. Вывод типов из аргументов функции:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function add(a: number, b: number): number {
return a + b;
}

type Result = ReturnType<typeof add>; // Result будет иметь тип number


2. Примеры с рекурсивными типами.
Реализация типа Reverse:
type actually = "123123123"

type Reverse<T extends string, R extends string = ""> = T extends `${infer Head}${infer Rest}`
? Reverse<`${Rest}`, `${Head}${R}`>
: R;

type result = Reverse<actually>; // "321321321"

Реализация типа Replace:
type actually = "912342181";
type Replace<T extends string, U extends string, R extends string = ""> = T extends ${infer Head}${infer Rest}
  ? Head extends U ? Replace<`${Rest}`, U, R> : Replace<`${Rest}`, U, `${R}${Head}`>
  : R;
 
type result = Replace<actually, "1">; // "923428"


Ключевое слово infer в TypeScript позволяет делать выводы о типах на основе переданных аргументов или примененных типов, что помогает улучшить гибкость и переиспользуемость кода.

@chulakov_dev
CSS Logical Properties

CSS Logical Properties предоставляют альтернативу традиционным физическим свойствам, таким как width, height, margin, border, padding и подобным, для более адаптивного оформления веб-страниц.

Они учитывают направление текста и другие языковые особенности.

CSS Logical Properties позволяют нам быть более гибкими в работе с мультиязычными интерфейсами и значительно сокращают количество кода, который нужно написать для адаптирования страниц под разные языки. Вот несколько примеров использования ⬇️

Логические отступы:
.container {
margin-inline-start: 20px; /* Логический внутренний отступ слева (или справа) */
margin-block-end: 30px; /* Логический внутренний отступ вниз (в зависимости от направления текста) */
}


Логическая ширина и высота:
.elent {
inline-size: 200px; /* Логическая ширина */
block-size: 100px; /* Логическая высота */
}


Логические границы:
.box {
border-inline: 2px solid #333; /* Логическая граница в горизонтальном направлении */
border-block: 1px dashed #555; /* Логическая граница в вертикальном направлении */
}


Логические паддинги:
.content {
padding-inline: 15px; /* Логический внутренний отступ в горизонтальном направлении*/
padding-block: 10px; /* Логический внутренний отступ в вертикальном направлении */
}


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

@chulakov_dev
Разбираемся в разнице: target="_blank" и target="blank" в HTML

🌐 Привет! Сегодня мы разберем интересный вопрос: в чем разница между target="_blank" и target="blank" при использовании в HTML? Детали могут показаться незначительными, но на практике они оказывают большое влияние на поведение ссылок в ваших веб-приложениях.

🎯 target="_blank"
Это стандартный способ открытия ссылки в новой вкладке браузера.

Каждый раз при клике на ссылку создается новая вкладка или новое окно браузера.
Пример использования:

<a href="https://example.com" target="_blank">
Открыть example.com в новой вкладке
</a>


🆚 target="blank"
При использовании target="blank" (без нижнего подчеркивания) браузер ведет себя по-другому.
"blank" здесь рассматривается как название фрейма, а не как ключевое слово. Если браузер не находит фрейм с таким именем, он создает новую вкладку с этим именем.

Повторные клики на разных ссылках с target="blank" будут перезаписывать содержимое одной и той же вкладки, вместо того чтобы открывать новую вкладку каждый раз.

Пример использования:

<a href="https://example.com" target="blank">
Открыть example.com в именованном фрейме/вкладке "blank"
</a>


💡 Это знание особенно ценно, когда вы сталкиваетесь с ситуацией, в которой ссылка, предположительно с target="_blank", не открывается в новой вкладке или открывается только один раз.

Осознание того, что target="blank" без нижнего подчеркивания ведет к созданию одной постоянной вкладки для всех ссылок, может сэкономить вам много времени при отладке и поиске проблем в вашем коде.

@chulakov_dev
Изучаем Array.prototype.flat()

Сегодняшняя тема посвящена одной из полезных функций, введенной в ECMAScript 2019, — методу Array.prototype.flat().

📚 Что такое Array.prototype.flat()?
Метод flat() позволяет «выровнять» многомерные массивы ранее заданной глубине. Это особенно полезно, когда вы работаете с массивами, структура которых может быть сложной или неизвестной.

🌐 Как работает Array.prototype.flat()?
flat() принимает один необязательный аргумент — глубину, которая указывает, насколько глубоко метод должен «выравнивать» массив. По умолчанию глубина равна 1.

Пример использования:
let multiDimensionalArray = [1, [2, 3], [4, [5, 6]]];

// Выравниваем на один уровень
let flatArray = multiDimensionalArray.flat();
console.log(flatArray); // Выведет: [1, 2, 3, 4, [5, 6]]

// Выравниваем на два уровня
let flatArrayDeep = multiDimensionalArray.flat(2);
console.log(flatArrayDeep); // Выведет: [1, 2, 3, 4, 5, 6]


@chulakov_dev
Этапы рендеринга в браузере

Давайте вспомним, как это происходит 🤓

1. Загрузка ресурсов (Loading): браузер загружает HTML-документ и связанные ресурсы, такие как стили CSS, скрипты JavaScript, изображения и медиафайлы.

2. DOM: из полученного HTML-документа формируется Document Object Model (DOM).

3. CSSOM: параллельно с DOM браузер обрабатывает стили CSS и формирует CSS Object Model (CSSOM).

4. Accessibility Tree: параллельно с DOM и CSSOM формируется Accessibility Tree, которое содержит информацию о структуре веб-страницы и ее элементах для вспомогательных технологий.

5. Render Tree: на основе DOM и CSSOM формируется дерево рендеринга (Render Tree), содержащее только видимые элементы и их стили.

6. Scripting: выполнение JavaScript, который может изменять DOM и CSSOM, что приведет к повторным процессам рендеринга.

7. Layout: браузер вычисляет положение и размеры каждого элемента в Render Tree (Layout или Reflow).

8. Painting: отрисовка элементов на странице на основе информации из Render Tree.

9. Composite: разбиение отрисованных элементов на слои и их объединение для формирования окончательного изображения страницы.

10. Оптимизация производительности и кеширование: применение методов оптимизации и кеширование для улучшения производительности загрузки и рендеринга.

11. Обработка событий: взаимодействие пользователя с веб-страницей может вызывать обновления DOM и запускать новые циклы рендеринга.

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

@chulakov_dev
Объединение интерфейсов в TypeScript

🔥 Сегодня рассмотрим одну замечательную возможность TypeScript — объединение интерфейсов, или merging interfaces. Этот механизм позволяет создавать более гибкие и масштабируемые типы.

🛠 Как работает объединение интерфейсов?
TypeScript уникален тем, что позволяет сливать несколько объявлений интерфейса в одно. Если вы объявляете два интерфейса с одинаковым именем, TypeScript автоматически объединит их.
interface User {
name: string;
age: number;
}
interface User {
email: string;
}


Результирующий интерфейс User будет иметь свойства name, age и email
const user: User = {
name: 'Алексей',
age: 30,
email: 'alexey@example.com',
}
box-shadow VS drop-shadow

⚡️Привет! Сегодня разбираемся с разницей свойства box-shadow и фильтра drop-shadow.

Свойство box-shadow добавляет к блоку одну или несколько теней. Оно принимает либо значение none, что означает отсутствие теней, либо список теней, разделенных запятыми в порядке от переднего к заднему.

drop-shadow применяет эффект падающей тени к входному изображению. Тень представляет собой размытую смещенную версию альфа-маски входного изображения, нарисованную определенным цветом и расположенную под изображением. Значения интерпретируются как для box-shadow, но с опциональным третьим вариантом, который является стандартным отклонением, а не радиусом размытия.

📎 В чём же разница?
box-shadow дает нам тень, охватывающую весь элемент, даже если элемент не имеет фона.
drop-shadow применяется не к самому элементу, а к его визуальному представлению, включая прозрачные части и создает тень только от непрозрачных частей элемента. Помимо этого, drop-shadow не поддерживает ключевое слово inset, которое превращает тень из внешней во внутреннюю.

Радиус размытия в свойстве box-shadow в CSS определяет, насколько сильно тень будет размыта и растянута относительно элемента: чем больше значение радиуса, тем больше тень и тем меньше её чёткость.

С другой стороны, стандартное отклонение в drop-shadow аналогично радиусу размытия в box-shadow. Но, поскольку drop-shadow применяется к визуальному изображению элемента, эффект может выглядеть иначе, особенно если элемент имеет прозрачные области.

Пример использования:
.drop_shadow {
filter: drop-shadow(0 0 16px #000);
}


.box_shadow {
box-shadow: 0px 0px 16px #000;
}
Восстановление состояния после применения git push --force

📌 Применение git push --force может быть необходимым в ряде ситуаций, например, для коррекции ошибок в истории коммитов. Однако ее использование может привести к потере данных в репозитории. Как вернуть состояние до применения команды? Решение есть!

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

+ deadbeef...f00f00ba master -> master (forced update)


Значение deadbeef является коммитом в ветке master до применения команды git push --force.

💡Для восстановления нам необходимо выполнить

git push --force origin deadbeef:master


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

Хотите узнать, как работать с огромными числами без потери точности?

Сегодня познакомимся с BigInt в JavaScript

BigInt введен в ECMAScript 2020 и предназначен для работы с очень большими целыми числами

▶️ Читайте нашу новую заметку, где мы расскажем о том, что такое Biglnt и как можно его использовать