Chulakov Dev – Telegram
Chulakov Dev
1.16K subscribers
139 photos
5 videos
204 links
Канал команды разработки Студии Олега Чулакова.

Советы по Frontend- и Backend-разработке web-сервисов, мобильных приложений, статьи и презентации от наших разработчиков, анонсы проектов и многое другое.

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

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

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

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

@chulakov_dev
👍22🔥82🤩2
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
👍95🔥3
Всем привет!

Несколько лет назад в 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
🔥15👍2
Привет, друзья!

На прошлой неделе у нас был пост про 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
🔥14👍4
Друзья, привет!

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

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

Инвариантность
Инвариантность означает, что тип является неизменным относительно других типов. В контексте обобщений, если у вас есть обобщенный тип например 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
👍8🔥7
Привет, друзья!

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

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

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

@chulakov_dev
25👍7🔥2
Всем привет!

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

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

Читать пост

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

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

@chulakov_dev
👍12🔥6
Привет, друзья!

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

@chulakov_dev
🔥15👍5
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
👍28🔥132👏1
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
21🔥8🤔2
Разбираемся в разнице: 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
🔥27🤯5👍31😁1
Изучаем 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
21🔥9👍4
Этапы рендеринга в браузере

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

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
15👍11🔥4
Объединение интерфейсов в 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',
}
19🤯6😱2
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;
}
👍15🔥54
Восстановление состояния после применения git push --force

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

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

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


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

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

git push --force origin deadbeef:master


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

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

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

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

▶️ Читайте нашу новую заметку, где мы расскажем о том, что такое Biglnt и как можно его использовать
16🔥4
Привет! Сегодня разбираемся с возможностью тестирования верстки с помощью Browserstack!

💻 При наступлении фазы тестирования, проверку работоспособности сайта и поиск багов на экранах различных устройств можно осуществить с помощью Devtools.

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

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

📱 Мобильное тестирование — более 100 возможных комбинаций iOS и Android. Присутствует полная информация об устройствах для баг-репортов. Вдобавок есть возможность открыть несколько телефонов одновременно для сокращения затрат времени на проверку.

Desktop — 20 вариаций операционных систем, включая Windows и MacOS. Дает возможность протестировать проект на самых популярных версиях браузеров.

➡️ Browserstack поможет сократить время тестирования и увеличить количество возможных проверок на недоступных устройствах.
🔥19