progway — программирование, IT
2.73K subscribers
25 photos
1 video
246 links
Чат: @prog_way_chat

Разборы вопросов и задач с собеседований, мысли, полезные материалы и просто вещи, что мне интересны из мира IT

Полезности и навигация в закрепе

По всем вопросам: @denisputnov
Download Telegram
Что такое Git Flow?

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

1. Основная ветка — чаще всего это master
Эта ветка содержит стабильную версию продукта, которая готова к выпуску. В ней всегда должен быть работающий и протестированный код. Каждый коммит в master обычно соответствует одной версии продукта

2. Ветка разработки — чаще всего это develop
В этой ветке происходит активная разработка. Все новые фичи и изменения сначала попадают сюда. После тестирования и проверки они будут слиты в master

Также есть вспомогательные ветки, которые маркируются своим префиксом — чаше всего это feature/*, release/*, bugfix/*, hotfix/*:

1. Ветки для разработки новых функций — feature/*
Они создаются от develop и после завершения работы сливаются обратно в develop. Нужны такие ветки для организации разработки отдельной фичи.

2. Ветка для фикса багов — bugfix/* и hotfix/*
Почти то же самое, что и feature/*, только эти ветки нужны для фикса багов. hotfix/* — для фикса багов в мастере или релизе. bugfix/* — для фикса багов в ветке develop

3. Ветка для подготовки версии продукта — release/*
Создаются от develop, когда разработка новых функций завершена и нужно подготовить релиз. После завершения сливаются в master и develop

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

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

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #useful
Какими способами можно скрыть элемент со страницы?

Для многих каверзный вопрос. В этом посте коротко рассмотрим какие есть варианты

1. Использовать display: none

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

2. Использовать visibility: hidden

Этот вариант, в отличии от display: none, не выводит элемент из потока документа. Элемент скрывается для глаз пользователя, но всё ещё продолжает обрабатываться и занимает место в вёрстке

3. Использовать opacity: 0

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

4. Позиционировать элемент далеко и надолго

.element {
position: absolute;
left: -999999px;
}


Абсолютно точно не советую использовать этот вариант, хотя работать будет

Есть ещё всякая экзотика типа <div hidden>, но это именно экзотика. Способы выше — основные, их точно хватит на практике.

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #web #useful
Что такое декларативность и императивность?

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

// Императивный способ создания списка из элементов массива
const items = [1, 2, 3, 4, 5];
const ul = document.createElement('ul');

for (let i = 0; i < items.length; i++) {
const li = document.createElement('li');
li.textContent = items[i];
ul.appendChild(li);
}

document.body.appendChild(ul);


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

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

import React from 'react';
import ReactDOM from 'react-dom';

const items = [1, 2, 3, 4, 5];

const App = () => (
<ul>
{items.map(item => (
<li key={item}>{item}</li>
))}
</ul>
);

ReactDOM.render(<App />, document.getElementById('root'));


В этом примере с использованием React мы описываем, что хотим увидеть: список элементов. Мы не указываем, как именно React должен создать и добавить эти элементы, мы просто описываем структуру, которую хотим получить

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

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

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #code #javascript #principles
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое Callback Hell и как с ним бороться?

Частый вопрос с собеса, особенно если идти куда-то повыше стажёра.

Callback Hell — это ситуация в асинхронном программировании, когда вложенные друг в друга функции обратного вызова (он же callback) образуют "лесенку", что делает код трудным для чтения и сопровождения.

doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doAnotherThing(newResult, function(finalResult) {
doSomethingMore(finalResult, function(lastResult) {
console.log(lastResult);
});
});
});
});


Как с этим можно бороться?

Конечно же промисы и async/await синтаксис:

Промисы позволяют писать асинхронный код более линейно и читаемо благодаря цепочке вызовов (chaining):

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doAnotherThing(newResult))
.then(finalResult => console.log(finalResult))
.catch(error => console.error(error));


А async/await — это более современный синтаксис над промисами, который так же позволяет развернуть вложенные колбеки в плоский код:

async function process() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doAnotherThing(newResult);
console.log(finalResult);
} catch (error) {
console.error(error);
}
}

process();


И о Promise, и о async/await у меня уже есть более подробные посты

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #code #javascript #patterns #data
Как открыть ссылку в новом окне или вкладке

Чтобы ссылка открывалась в новом окне или вкладке, достаточно добавить к тегу <a> атрибут target="_blank":

<a 
href="https://t.me/prog_way_blog"
target="_blank"
rel="noopener noreferrer"
>
Ну чё-то какой-то канал в телеге
</a>


Атрибут target="_blank" сообщает браузеру, что ссылку нужно открыть в новой вкладке. И также стоит обратить внимание на атрибут rel. Он предотвращает передачу информации о странице, с которой была открыта ссылка, а также защищает от потенциальных уязвимостей

Накидайте побольше китов 🐳 на пост и, если это будет актуально, сделаю отдельный пост с разбором noopener noreferrer и зачем оно надо

Если же вы сомневаетесь, ставить их или нет — ставьте всегда

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #web #useful
Разделение чанков в Vite

Часто может быть так, что не малое приложение с большим количеством зависимостей весит несколько мегабайт. Такое приложение будет грузиться клиенту очень долго, особенно, если у него низкая скорость соединения. Далеко не в любых условиях у пользователей есть возможность загружать наши фронтенды на 100 МБитной линии, поэтому оптимизация загрузки — очень важно.

Пользовательский код можно загружать ленивой подгрузкой, но что сделать с библиотеками, которые собираются в один JS файл? Использовать разделение на чанки.

Чанк — собранные блоки кода, в виде которых пользователь загружает себе ваше приложение.

Идея в том, чтобы разделить приложение на несколько чанков. Это может быть полезно по нескольким причинам:
1. Открываются прелести асинхронной загрузки нескольких чанков
2. Есть возможность загружать не весь фронтенд сразу, а порционно, по мере необходимости

Выглядеть конфигурация может вот так:

build: {
rollupOptions: {
output: {
manualChunks: {
core: [
'react',
'react-dom',
'react-router-dom'
],
network: [
'axios',
'@tanstack/react-query'
],
utils: [
'classnames',
'dayjs',
'escape-html',
'tailwind-merge',
'zustand',
'@loadable/component'
],
table: [
'@tanstack/react-table',
'@tanstack/react-virtual'
],
chart: [
'reactflow',
'@dagrejs/dagre'
],
icons: ['@ant-design/icons'],
ui: [
'@chakra-ui/react',
'chakra-react-select',
'framer-motion',
'prismjs',
'react-simple-code-editor'
],
},
},
},
}


Не очень красиво, но как есть. В единый чанк стоит объединять те библиотеки, что не могут работать друг без друга, например чанк core . Также есть ещё и частотная группировка — когда легковесные библиотеки собирают в один чанк, чтобы быстро загрузить их все, так как понадобятся они на всех страницах, например чанк utils

Что в итоге? Скорость загрузки приложения можно значимо увеличить. Для отрисовки, например, страницы авторизации, нам не нужны таблицы, графики и прочие красивости — загрузим чанки core и network, остальное нам не нужно. Остальные чанки мы можем загрузить по мере необходимости, используя асинхронные инструменты загрузки. Это могут быть @loadable, который рекомендует команда React или же вовсе обычный import.

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

Выглядеть это будет примерно так:
const About = lazy(() => import("./pages/about")); // для реакт компонентов

// более сложный пример импорта компонента с @loadable/components
const PageComponent = loadable((props) => import(`./pages/${props.page}`), {
fallback: <div>Page is Loading...</div>,
cacheKey: (props) => props.page
});

async function foo() {
const utils = await import("./utils.js") // для, например, утилсов
}


Более подробно о manualChunks можно прочитать в доке rollup

Спасибо за прочтение, для меня это важно ❤️

@prog_way_blogчат#theory #web #useful
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
CSS для печати страниц

Несколько лет назад я делал пет-проект — конструктор резюме. Кейс был такой, что хотелось сразу же печатать созданное резюме при нажатии сочетания клавиш Ctrl+P в браузере. Но проблема в том, что помимо самого резюме на печать попадало не только само резюме, а все кнопки, поля и прочие элементы страницы для его настройки. Такие кейсы можно решать при помощи CSS медиазапроса @media print:

@media print {
/* стили для печати */
}


То есть во время печати мы можем скрыть все ненужные элементы:

@media print {
.navbar, .footer, .controls {
display: none;
}
}


Ну или ещё проще:

@media print {
.no-print {
display: none;
}
}


А сам класс .no-print проставлять на все скрываемые при печати элементы.

Также есть ещё специальные CSS свойства печати, например, break-inside, которое дополнительно настраивает можно ли переместить блок со страницы на страницу при печати, что особенно актуально для целостных блоков типа “контакты” в резюме. Если указать break-inside: avoid, то такой элемент не сможет быть расположен на двух страницах сразу, а перенесётся весь, если для него не хватает места.

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

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #code #web #useful
Please open Telegram to view this post
VIEW IN TELEGRAM
Как отменить уже отправленный HTTP запрос?

Для отмены уже отправленного запроса нам пригодится встроенный в JavsScript объект — AbortController
Этот объект позволяет отменять уже запущенные асинхронные операции, fetch в том числе

const controller = new AbortController()

// отправляем запрос
fetch('https://.../', { signal: controller.signal })

// отменяем его
controller.abort()


Нужно это много где, я приведу самый очевидный пример с реактом:
Представим, что пользователь открывает страницу. На странице в useEffect идёт запрос к API, но пользователь, не дожидаясь ответа от сервера, переходит на другую страницу. Запрос есть, трафик занят, есть риск нарушения жизненного цикла компонента, а результаты этого запроса уже и вовсе не нужны. Вот так это решается:

useEffect(() => {
const controller = new AbortController()

// делаем запрос на маунт компонента
fetch('https://.../', { signal: controller.signal })

// отменяем запрос на анмаунт компонента
return () => controller.abort()
}, [])


Использование AbortController'a помогает избежать потенциальных утечек памяти и гарантирует, что запросы не будут выполняться после того, как компонент был размонтирован. Полезно это при любых запросах, так что можно смело сделать свой хук обёртку. Или просто использовать @tanstack/react-query 🌚

Спасибо за прочтение, это важно для меня ❤️

@prog_way_blogчат#theory #javascript #code #data
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM