Frontend | Вопросы собесов
19.3K subscribers
30 photos
2 videos
887 links
Сайт easyoffer.ru
Реклама @easyoffer_adv
ВП @easyoffer_vp

Тесты t.me/+T0COHtFzCJkwMDUy
Задачи t.me/+_tcX2w2EmvdmMTgy
Вакансии t.me/+CgCAzIyGHHg0Nzky
Download Telegram
🤔 Как бы повернул блок на 45 градусов?

Чтобы повернуть блок на 45 градусов, используется свойство CSS transform с функцией rotate. Это свойство позволяет применять различные трансформации к элементам, включая вращение, масштабирование, смещение и наклон.

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

.rotated-box {
width: 100px;
height: 100px;
background-color: lightblue;
transform: rotate(45deg);
}


🚩Объяснение

Основной синтаксис
🟠Угол поворота элемента. Может быть положительным или отрицательным значением в градусах (deg), радианах (rad), градусах (grad) или оборотах (turn).
transform: rotate(угол);   


🟠Параметры
Угол поворота элемента. В данном случае элемент поворачивается на 45 градусов по часовой стрелке.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.rotated-box {
width: 100px;
height: 100px;
background-color: lightblue;
transform: rotate(45deg);
}
</style>
</head>
<body>
<div class="rotated-box"></div>
</body>
</html>


🚩Пояснение

🟠Свойство `transform`
Позволяет применять различные визуальные эффекты к элементу, такие как вращение, масштабирование и смещение.
🟠Функция rotate
Используется для вращения элемента на заданный угол. Положительные значения поворачивают элемент по часовой стрелке, отрицательные — против часовой стрелки.

🚩Использование в реальных проектах

🟠Вращение элементов
Вращение элементов может использоваться для создания интересных визуальных эффектов, таких как поворот иконок, изображений или блоков текста.
🟠Анимация
В комбинации с анимацией (@keyframes и transition), вращение может создавать динамичные и интерактивные пользовательские интерфейсы.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
🤔 Что такое метатеги и для чего они нужны?

Метатеги — это элементы внутри тега <head>, которые содержат служебную информацию о странице: кодировка, автор, описание, ключевые слова, viewport, Open Graph, favicons и т.д.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥2
🤔 Что такое amp страницы?

AMP (Accelerated Mobile Pages) — это технология от Google, которая позволяет создавать очень быстрые и легкие веб-страницы для мобильных устройств. AMP-страницы загружаются почти мгновенно, потому что:
Используют оптимизированный HTML (AMP HTML)
Ограничивают использование JavaScript
Подключают Google AMP Cache для мгновенной загрузки

🚩Зачем нужны AMP?

Ускоряют загрузку страниц → полезно для мобильных пользователей
Улучшают SEO → Google любит быстрые сайты
Увеличивают CTR → в поиске AMP-страницы помечены ⚡️ и привлекают внимание
Снижают нагрузку на сервер

🚩Как создать AMP-страницу?

AMP-страница – это обычный HTML, но с ограничениями и своими тегами.
<!DOCTYPE html>
<html ⚡️ lang="ru">
<head>
<meta charset="UTF-8">
<title>AMP Страница</title>
<link rel="canonical" href="https://example.com/normal-page.html">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script async src="https://cdn.ampproject.org/v0.js"></script>
<style amp-boilerplate>
body { visibility: hidden; }
</style>
<noscript>
<style amp-boilerplate>
body { visibility: visible; }
</style>
</noscript>
</head>
<body>
<h1>Привет, это AMP!</h1>
<amp-img src="image.jpg" width="600" height="400" layout="responsive"></amp-img>
</body>
</html>


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍6
🤔 Как поменять контекст функции?

В JavaScript контекст функции можно изменить с помощью методов call(), apply() и bind(). Эти методы позволяют указать, что будет использоваться в качестве this в теле функции.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥2
🤔 Опасности использования `innerHTML` в JavaScript

innerHTML – это свойство, которое позволяет вставлять HTML-код внутрь элемента как строку. Однако оно небезопасно и может привести к серьезным проблемам.
document.getElementById("content").innerHTML = "<h1>Привет!</h1>";


🟠Уязвимость: XSS-атаки (внедрение вредоносного кода)
Если innerHTML получает данные от пользователя (например, из формы или URL), хакер может вставить вредоносный JavaScript-код.
const userInput = "<script>alert('Вас взломали!');</script>";
document.getElementById("content").innerHTML = userInput;


🟠Проблемы с безопасностью CSP (Content Security Policy)
Если в веб-приложении включена CSP (защита от XSS), то innerHTML может нарушать политику безопасности.
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">


🟠Случайное удаление обработчиков событий
Если заменить содержимое элемента через innerHTML, все обработчики событий внутри него удалятся.
document.getElementById("btn").addEventListener("click", () => {
alert("Нажато!");
});

document.getElementById("container").innerHTML = "<button id='btn'>Клик</button>";
document.getElementById("btn").click(); // Ошибка, обработчик удален


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14
🤔 При каких значениях position работает z-index?

z-index работает, если position — не static, то есть:
- relative, absolute, fixed, sticky.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥8
🤔 Как общаться между iframe и самой страницой?

Общение между iframe и родительской страницей может происходить с помощью:
Метода postMessage (лучший способ)
Доступа к window.frames или window.parent (если тот же домен)
Передачи данных через localStorage или cookies

🟠`postMessage` – безопасный способ для разных доменов
Метод window.postMessage() позволяет передавать сообщения между разными окнами (iframe, popup, другие вкладки) даже если они на разных доменах.

🚩Передача данных из `iframe` в родительскую страницу

*Код в iframe (child.html)
// Отправляем сообщение родительскому окну
window.parent.postMessage({ type: "hello", data: "Привет, родитель!" }, "*");


Код в родительской странице (index.html)
window.addEventListener("message", (event) => {
console.log("Получено сообщение от iframe:", event.data);
});


* в postMessage означает, что сообщение отправляется любому домену. Лучше указывать конкретный, например:
window.parent.postMessage({ type: "hello" }, "https://example.com");


🚩Передача данных из родителя в `iframe`

Код в родительской странице (index.html)
const iframe = document.getElementById("myIframe");

// Ждём, когда iframe загрузится
iframe.onload = () => {
iframe.contentWindow.postMessage({ type: "greeting", data: "Привет, iframe!" }, "*");
};


Код в iframe (child.html)
window.addEventListener("message", (event) => {
console.log("Получено сообщение от родителя:", event.data);
});


🚩Доступ к `window.frames` и `window.parent` (только если тот же домен!)

Если iframe и основная страница находятся на одном домене, можно обращаться к их window напрямую.
Родительская страница → iframe
const iframe = document.getElementById("myIframe");

// Получаем объект `window` внутри `iframe`
iframe.contentWindow.document.body.style.backgroundColor = "lightblue";


iframe → Родительская страница
console.log(window.parent.document.title); // Доступ к заголовку страницы


🚩`localStorage` или `cookies` (если оба окна на одном домене)

Можно сохранять данные в localStorage или cookies, а другая сторона будет их читать.
Запись в localStorage из iframe
localStorage.setItem("message", "Привет от iframe!");


Чтение localStorage в родительской странице
console.log(localStorage.getItem("message")); // "Привет от iframe!"


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17
🤔 Что известно про доступность A11y?

A11y (Accessibility) — практика создания интерфейсов, доступных для всех, включая людей с ограниченными возможностями:
- Использование семантической разметки (<nav>, <main>, <button>);
- Поддержка клавиатурной навигации;
- Использование aria-атрибутов;
- Контрастность, масштабируемость текста;
- Тестирование с помощью экранных читалок.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10🔥3
🤔 Какие есть альтернативы у redux?

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

🚩React Context API + useReducer

Подходит для небольших и средних приложений
Встроено в React (не требует установки дополнительных библиотек)
useContext` позволяет передавать данные по дереву компонентов без "прокидывания" через props
useReducer работает как Redux, но проще
const AuthContext = createContext();

function authReducer(state, action) {
switch (action.type) {
case "LOGIN":
return { ...state, isAuthenticated: true, user: action.payload };
case "LOGOUT":
return { ...state, isAuthenticated: false, user: null };
default:
return state;
}
}

function AuthProvider({ children }) {
const [state, dispatch] = useReducer(authReducer, {
isAuthenticated: false,
user: null,
});

return (
<AuthContext.Provider value={{ state, dispatch }}>
{children}
</AuthContext.Provider>
);
}

function LoginButton() {
const { dispatch } = useContext(AuthContext);
return (
<button onClick={() => dispatch({ type: "LOGIN", payload: "Иван" })}>
Войти
</button>
);
}


🚩Zustand

Проще Redux, но с теми же возможностями
Нет лишних actions и reducers, только функции
import { create } from "zustand";

const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));

function Counter() {
const { count, increment } = useStore();
return <button onClick={increment}>Счетчик: {count}</button>;
}


🚩Recoil

Идеален для React-приложений
Гибче, чем Redux, с концепцией "атомов" (раздельные состояния)
import { atom, useRecoilState } from "recoil";

const countState = atom({
key: "count",
default: 0,
});

function Counter() {
const [count, setCount] = useRecoilState(countState);
return <button onClick={() => setCount(count + 1)}>Счетчик: {count}</button>;
}


🚩Jotai

Напоминает Recoil, но без сложных концепций
Поддерживает React Suspense и асинхронные состояния
import { atom, useAtom } from "jotai";

const countAtom = atom(0);

function Counter() {
const [count, setCount] = useAtom(countAtom);
return <button onClick={() => setCount(count + 1)}>Счетчик: {count}</button>;
}


🚩MobX

Автоматически отслеживает изменения состояния
Удобен для сложных приложений
import { makeAutoObservable } from "mobx";
import { observer } from "mobx-react-lite";

class CounterStore {
count = 0;

constructor() {
makeAutoObservable(this);
}

increment() {
this.count++;
}
}

const store = new CounterStore();

const Counter = observer(() => (
<button onClick={() => store.increment()}>Счетчик: {store.count}</button>
));


🚩XState

Подходит для сложных логик (например, UI-анимаций, состояний формы)
Удобно описывать последовательности действий
import { createMachine, interpret } from "xstate";

const toggleMachine = createMachine({
id: "toggle",
initial: "inactive",
states: {
inactive: { on: { TOGGLE: "active" } },
active: { on: { TOGGLE: "inactive" } },
},
});

const service = interpret(toggleMachine).start();
service.send("TOGGLE"); // Меняет состояние


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥3
🤔 Сталкивался ли во Vue, с таким понятием как потеря реактивности?

Да, это возможно при:
- добавлении новых свойств в объект напрямую (obj.newProp = value);
- мутировании вложенных структур;
- использовании Object.assign, Object.freeze;
- Vue 2: важно использовать Vue.set или this.$set.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥2
🤔 В чем отличие атрибутов от свойств в html тегах?

Атрибуты — это то, что записано в HTML.
Свойства — это то, что появляется в JavaScript-объекте element.

Когда браузер загружает страницу, он парсит HTML и создает DOM-объекты, в которых атрибуты превращаются в свойства. Однако атрибуты и свойства не всегда совпадают!

🟠Атрибуты (`attributes`)
Хранятся в HTML.
Всегда являются строками.
Их можно получить с помощью .getAttribute() и изменить через .setAttribute().
Они не меняются динамически при изменении свойства элемента в JS.
<input id="myInput" type="text" value="Hello">

const input = document.getElementById("myInput");
console.log(input.getAttribute("value")); // "Hello"


🟠Свойства (`properties`)
Хранятся в объекте элемента (HTMLElement).
Имеют разные типы данных (строки, числа, булевые и т. д.).
Меняются динамически при взаимодействии с DOM.
console.log(input.value); // "Hello"
input.value = "New text"; // Меняем свойство
console.log(input.getAttribute("value")); // "Hello" (атрибут НЕ поменялся)
console.log(input.value); // "New text" (свойство изменилось)


Пример с checked у чекбокса
<input type="checkbox" id="myCheckbox" checked>

const checkbox = document.getElementById("myCheckbox");

console.log(checkbox.getAttribute("checked")); // "checked" (атрибут есть, значит включен)
console.log(checkbox.checked); // true (свойство JS - boolean)

checkbox.checked = false; // Меняем состояние чекбокса
console.log(checkbox.getAttribute("checked")); // "checked" (атрибут не поменялся!)
console.log(checkbox.checked); // false (свойство изменилось)


🟠Как правильно работать с атрибутами и свойствами?
Если хотите работать с DOM → используйте свойства (element.value, element.checked и т. д.).
Если хотите работать с атрибутами в HTML → используйте .getAttribute() и .setAttribute().
const img = document.querySelector("img");

// Меняем атрибут src
img.setAttribute("src", "new-image.jpg");

// Меняем свойство width
img.width = 300;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
🤔 Что известно как видится идеальный процесс работы в команде над задачей?

- Чёткая постановка задачи и её декомпозиция;
- Обсуждение реализации, согласование интерфейсов;
- Деление ответственности;
- Использование гита, code review, CI/CD;
- Поддержка документации;
- Честная и быстрая обратная связь;
- Ретроспектива и доработка процессов.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13
🤔 Какие значения будут являться falsy значениями?

В JavaScript значения, которые при приведении к логическому типу (boolean) становятся false, называются "falsy" значениями. Существует всего семь таких значений:

🟠`false`
Логическое значение false.
console.log(Boolean(false)); // false    


🟠`0`
Число ноль.
console.log(Boolean(0)); // false    


🟠`-0`
Отрицательное число ноль (хотя это редко встречается на практике).
console.log(Boolean(-0)); // false    


🟠`0n`
Число BigInt с значением ноль.
console.log(Boolean(0n)); // false    


🟠`""` (пустая строка)
Строка без символов.
console.log(Boolean("")); // false    


🟠`null`
Специальное значение, обозначающее отсутствие какого-либо значения.
console.log(Boolean(null)); // false    


🟠`undefined`
Специальное значение, обозначающее необъявленное или неприсвоенное значение.
console.log(Boolean(undefined)); // false    


🟠`NaN`
Специальное значение, обозначающее не число (Not-a-Number).
console.log(Boolean(NaN)); // false    


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

🟠Проверка значений на falsy
В условиях (if statements) все falsy значения интерпретируются как false.
const values = [false, 0, -0, 0n, "", null, undefined, NaN];

values.forEach(value => {
if (!value) {
console.log(`${value} is falsy`);
}
});


Примеры в условиях
if (!false) {
console.log('false is falsy'); // выводится
}

if (!0) {
console.log('0 is falsy'); // выводится
}

if (!"") {
console.log('"" is falsy'); // выводится
}

if (!null) {
console.log('null is falsy'); // выводится
}

if (!undefined) {
console.log('undefined is falsy'); // выводится
}

if (!NaN) {
console.log('NaN is falsy'); // выводится
}


🟠Использование в логических операторах
Falsy значения также учитываются в логических операциях.
const value = null;

const result = value || 'default value';
console.log(result); // 'default value', потому что value является falsy


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7💊2
🤔 Что такое promise и какие состояния у него есть?

Promise в JavaScript — это объект, который представляет результат асинхронной операции. Он может находиться в одном из трёх состояний: pending (ожидание), fulfilled (выполнено) и rejected (отклонено). Fulfilled означает, что операция завершилась успешно, а rejected — что произошла ошибка. Это удобный способ работы с асинхронным кодом и упрощает обработку результатов таких операций.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥3
🤔 Чем spa отличается от классического сайта?

SPA (Single Page Application) и классический сайт (MPA – Multi Page Application) — это два разных подхода к созданию веб-приложений. Они отличаются по способу загрузки данных, навигации и производительности.

🚩Классический сайт (MPA – Multi Page Application)

Каждый раз при переходе на новую страницу браузер запрашивает HTML с сервера. Сервер формирует страницу и отправляет готовый HTML.
<?php
echo "<h1>Добро пожаловать!</h1>";
?>
<a href="/about">О нас</a>


🚩SPA (Single Page Application)

При первом заходе загружается одна HTML-страница и весь JavaScript. Дальше контент подгружается динамически через AJAX или Fetch API.
import React, { useState, useEffect } from "react";

function App() {
const [data, setData] = useState(null);

useEffect(() => {
fetch("/api/products")
.then(res => res.json())
.then(data => setData(data));
}, []);

return <div>{data ? JSON.stringify(data) : "Загрузка..."}</div>;
}

export default App;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14
🤔 Как из React осуществить доступ к данным?

Через:
- fetch/Axios для HTTP-запросов;
- hooks (useEffect, useState) для загрузки и хранения данных;
- контексты (useContext) для передачи данных по дереву компонентов;
- глобальные хранилища (Redux, Zustand, Recoil и т.д.);
- GraphQL-клиенты (Apollo, Relay).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊8👍4🔥1
🤔 Что такое нотация о-большое?

O-большое (Big O) — это способ описания эффективности алгоритма в зависимости от размера входных данных. Она показывает, как быстро растёт время выполнения или использование памяти при увеличении количества входных данных.

🚩Зачем нужна O-большое?

Когда мы пишем код, важно понимать, насколько он эффективен, особенно при работе с большими массивами или сложными вычислениями.
Big O помогает:
- Оценить время работы алгоритма.
- Понять, как алгоритм масштабируется.
- Сравнить разные алгоритмы и выбрать лучший.

🚩Примеры сложности алгоритмов

O(1) – Константное время
function getFirstElement(arr) {
return arr[0]; // Не зависит от размера массива
}


O(N) – Линейное время
function findElement(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) return i;
}
return -1;
}


O(N²) – Квадратичное время (плохо)
function bubbleSort(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; j++) {
if (arr[i] > arr[j]) {
[arr[i], arr[j]] = [arr[j], arr[i]];
}
}
}
return arr;
}


O(log N) – Логарифмическое время (оптимально)
function binarySearch(arr, target) {
let left = 0, right = arr.length - 1;

while (left <= right) {
let mid = Math.floor((left + right) / 2);

if (arr[mid] === target) return mid;
if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}

return -1;
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15
🤔 Какая основная причина для использования SSR?

SSR (Server-Side Rendering):
- Улучшает скорость загрузки первого экрана (первая отрисовка приходит сразу с сервера).
- Обеспечивает SEO-оптимизацию, поскольку поисковики получают готовый HTML.
- Полезен для динамического контента, где важна предзагрузка.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥4🤔1
🤔 Какие модификаторы есть у событий?

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

🚩`stopPropagation`

Этот метод предотвращает всплытие события вверх по дереву DOM.
По умолчанию, события в DOM распространяются по фазам: погружение (capturing) → цель (target) → всплытие (bubbling). Если вы хотите остановить обработку события на текущем элементе и не позволить ему "подняться" выше по дереву DOM, используйте stopPropagation.
<div id="parent" style="padding: 20px; background: lightblue;">
Родительский элемент
<button id="child">Дочерний элемент</button>
</div>

<script>
document.getElementById("parent").addEventListener("click", () => {
alert("Событие всплыло до родителя");
});

document.getElementById("child").addEventListener("click", (event) => {
alert("Событие на кнопке");
event.stopPropagation(); // Остановим всплытие
});
</script>


🚩`stopImmediatePropagation`

Этот метод, помимо остановки всплытия (как stopPropagation), предотвращает выполнение других обработчиков на том же элементе. Если у одного и того же элемента есть несколько обработчиков для одного события, stopImmediatePropagation гарантирует, что после его вызова остальные обработчики не будут выполнены.
<button id="myButton">Нажми меня</button>

<script>
const button = document.getElementById("myButton");

button.addEventListener("click", () => {
alert("Обработчик 1");
});

button.addEventListener("click", (event) => {
alert("Обработчик 2");
event.stopImmediatePropagation(); // Остановим все остальные обработчики
});

button.addEventListener("click", () => {
alert("Обработчик 3"); // Этот обработчик не выполнится
});
</script>


🚩`preventDefault`

Этот метод отменяет поведение элемента по умолчанию.
Некоторые элементы (например, ссылки или формы) имеют стандартное поведение. Например:
- Клик по ссылке ведет на новый URL.
- Отправка формы перезагружает страницу.
С помощью preventDefault можно предотвратить это поведение.
<a href="https://example.com" id="link">Перейти на сайт</a>

<script>
const link = document.getElementById("link");

link.addEventListener("click", (event) => {
event.preventDefault(); // Останавливаем переход по ссылке
alert("Поведение ссылки отменено");
});
</script>


🚩`passive`

Это модификатор, который не является методом, а используется в настройках обработчика событий. Он указывает, что обработчик не вызывает preventDefault. Этот модификатор помогает оптимизировать обработку событий, таких как прокрутка (scroll), делая их более производительными. Некоторые браузеры при обработке событий (например, touchstart или wheel) предполагают, что вы можете использовать preventDefault. Это замедляет прокрутку, так как браузеру нужно ждать завершения вашего обработчика. Указав passive: true, вы говорите браузеру, что не собираетесь отменять поведение.
window.addEventListener("scroll", () => {
console.log("Скролл работает");
}, { passive: true });


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15
🤔 Что такое falsy значения?

Это значения, которые при приведении к булевому типу становятся false.
1. Примеры: 0, '' (пустая строка), null, undefined, NaN, false.
2. Они полезны для проверки в логических операциях и условиях.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍2