TGViewer
Channel Public Channel
Григорий Петров / IT

Григорий Петров / IT

@hryh0rii

Блог о frontend-разработке
Subscribers
42
Photos
1
Videos
0
Links
1
Recent Posts 9 shown
Post #42 537
📃 «Чистый код» Р. Мартин Содержательные имена

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

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

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

🧠 1. Передавайте намерение
const u — не информативно.
const usersList — сразу ясно, про что переменная.
Если название все еще требует комментарий — его надо менять.

🚫 2. Избегайте дезинформации
Если usersList — это объект, а не список — это запутывает. Название должно соответствовать сути.

🔀 3. Делайте различия осмысленными
f и l — это что? firstName и lastName — сразу понятно. Не жертвуйте читаемостью ради длины.

🗣 4. Произносимость важна
smth, cntnt, usrnm — сокращения, которые сложно читать и обсуждать. Лучше полностью: something, content, username.

🔍 5. Используйте имена, которые легко найти
event проще найти в коде, чем e. Константы и строки лучше выносить в переменные — легче искать и переиспользовать.

💡 6. Префикс I в интерфейсах не обязателен
IUser, IProps — отголоски прошлого, по мнению автора. Сейчас IDE и типизация отлично справляются без префиксов.

🏷 7. Классы — существительные, методы — глаголы
User, Cart, ProductList — объекты.
getUser(), sendMessage() — действия. Так прозрачнее.

🧩 8. Не перегружайте смыслами
Слово add может значить всё что угодно. Лучше appendToCart(), registerUser(), insertItem().

🌍 9. Пространство задачи или решения
Термины из предметной области (checkoutForm) или из IT (binaryTree) — главное, чтобы понятно было в контексте и без эклектики.

📦 10. Добавляйте контекст, где нужно
state — это часть адреса, статус или Redux? Лучше address.state, userState или addressState. Или вообще оформить как объект address.

А вы как часто сталкиваетесь с плохими переменными или пишете их сами? Признавайтесь 😐

#чистыйкод #робертмартин #js
  • ❤ 3
  • 👍 3
  • 🔥 3
Post #40 329
👩‍💻Svelte: {#snippet}, {@render} и {@const} или же почему не слоты?

Привет, спишь? Я нет, вернулся из магазина с хлебом и Svelte 😑

Бывает ведь такое, что порой мы не только красим кнопки, но еще всякие компоненты делаем гибкие и новомодные? Бывает, конечно

И порой случается так, что для большей гибкости нам приходится использовать дополнительный инструментарий, например Svelte и Vue для этого предоставляют <slot>

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

🌟Мы, как обычно, делаем запрос на сервер при монтировании компонента, забираем пользователей в переменную users, за сохранность которой отвечает руна $state, известная нам прежде


<script>
import { onMount } from 'svelte';
let users = $state([]);

onMount(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
users = await response.json();
});
</script>

{#each users as user (user.id)}
<article class="user">
<div class="user__header">
<span>{user.name}</span> |
<span>{user.email}</span> |
<span>{user.username}</span> |
<span>{user.phone}</span> |
<a href={user.website}>{user.website}</a> |
</div>
<address class="user__address">
<span>{user.address.street} St.</span> |
<span>{user.address.suite} suite</span> |
<span>{user.address.city} city</span> |
<span>{user.address.zipcode}</span> |
<span>{user.address.geo.lat}</span> |
<span>{user.address.geo.lng}</span>
</address>
<div class="user-company">
<span>Company: <b>{user.company.name}</b></span>
<ul class="user-company__list">
<li class="user-company__item">Catch phrase: <i>{user.company.catchPhrase}</i></li>
<li class="user-company__item">BS: <i>{user.company.bs}</i></li>
</ul>
</div>
</article>
<hr />
{/each}


🌟Разбиваем нашу текущую разметку на три сниппета: userHeader, userAddress и userCompany, каждый из них берет в себя аргументом объект пользователя и забирает нужные поля, которые мы с помощью тега @const спокойно деструктурируем прямо по месту


{#snippet userHeader(user)}
{@const {name, email, username, phone, website} = user}
<div class="user__header">
<span>{user.name}</span> |
<span>{user.email}</span> |
<span>{user.username}</span> |
<span>{user.phone}</span> |
<a href={user.website}>{user.website}</a> |
</div>
{/snippet}

{#snippet userAddress(user)}
{@const { address: { street, suite, city, zipcode, geo } } = user}
<address class="user__address">
<span>{street} St.</span> |
<span>{suite} suite</span> |
<span>{city} city</span> |
<span>{zipcode}</span> |
<span>{geo.lat}</span> |
<span>{geo.lng}</span>
</address>
{/snippet}

{#snippet userCompany(user)}
{@const { company: { name, catchPhrase, bs } } = user}
<div class="user__company user-company">
<span>Company: <b>{name}</b></span>
<ul class="user-company__list">
<li class="user-company__item">Catch phrase: <i>{catchPhrase}</i></li>
<li class="user-company__item">BS: <i>{bs}</i></li>
</ul>
</div>
{/snippet}


🌟После чего создаем компонент User, который принимает в себя пропсами объект пользователя и имена этих сниппетов, где в итоге с помощью render тега мы выводим все, что прежде организовали:


<script>
const { user, userHeader, userAddress, userCompany } = $props();
</script>

<article class="user">
{@render userHeader(user)}
{@render userAddress(user)}
{@render userCompany(user)}
</article>


🌟И в цикле заменяем все наше полотно одним емким компонентом:


{#each users as user (user.id)}
<User {user} {userHeader} {userAddress} {userCompany} />
<hr />
{/each}


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

#svelte
  • 🔥 6
  • ❤ 4
  • 👍 3
Post #39 275
👩‍💻Vue: inheritAttrs и гибкая работа с компонентами

Внезапно, да? Но тем не менее, сегодня у нас Vue 👆

В нем есть такая штука, как атрибуты, которые бывают пропускаемыми и непропускаемыми. Разберёмся сегодня с этим.

📌Пропускаемые атрибуты — это те атрибуты, которые явно определены в виде пропсов компонента. Они предназначены для того, чтобы передавать данные от родительского компонента к дочернему. Например, если у вас есть компонент с пропсом title, то любой атрибут title, переданный этому компоненту, будет считаться пропускаемым.

📌Непропускаемые атрибуты, в свою очередь, — это любые атрибуты, которые не были определены в виде пропсов компонента. Эти атрибуты не используются самим компонентом, но по умолчанию Vue передаёт их на корневой элемент компонента. Примером может служить атрибут class, если он не был определён как пропс.

➡️Рассмотрим простой компонент Content.vue, который не содержит никаких пропсов:


<template>
<div>
<h2>
<slot name="title">no title</slot>
</h2>
<p>
<slot name="description">no description</slot>
</p>
</div>
</template>


➡️Если мы используем этот компонент и передадим ему атрибуты, такие как titleClass, descClass и wrapClass, они будут переданы на корневой элемент — div:


<Content
titleClass="title"
descClass="description"
wrapClass="wrap"
>
<template v-slot:title>Hello world!</template>
<template v-slot:description>Hello description!</template>
</Content>


➡️Отрендеренная разметка будет выглядеть следующим образом:


<div titleclass="title" descclass="description" wrapclass="wrap">
<h2>Hello world!</h2>
<p>Hello description!</p>
</div>


➡️Это ожидаемое поведение, поскольку Vue по умолчанию передает непропускаемые атрибуты на корневой элемент компонента. Однако, если мы захотим изменить это поведение, мы можем воспользоваться опцией inheritAttrs.
Мы добавим следующую настройку в наш компонент:



<script setup>
defineOptions({
inheritAttrs: false,
});
</script>

<template>
<div :class="$attrs.wrapClass">
<h2 :class="$attrs.titleClass">
<slot name="title">no title</slot>
</h2>
<p :class="$attrs.descClass">
<slot name="description">no description</slot>
</p>
</div>
</template>


➡️Теперь непропускаемые атрибуты не будут передаваться на корневой элемент, вместо этого они будут доступны через объект $attrs. Разметка после рендера изменится:


<div class="wrap">
<h2 class="title">Hello world!</h2>
<p class="description">Hello description!</p>
</div>


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

#vue
  • 👍 5
  • 🔥 4
  • ⚡ 3
  • ❤ 1
Post #38 298
👩‍💻Svelte: работа со store

Отшумели летние дожди новогодние праздники, а это значит, что речь снова пойдет о Svelte 💁‍♂️

В предыдущих публикациях мы узнали про основные руны, вывод данных, как это все корректно собирать в одну конструкцию и применять на практике

Но если же потенциальное приложение масштабируется, то вполне логичной проблемой может стать props drilling, поскольку компонентов становится больше, пропсы передавать по-прежнему надо и зачастую это становится довольно затруднительно

Чтоб избежать это, Svelte предлагает нам store – объект, который обеспечивает реактивный доступ к своим значениям, для этого нам достаточно использовать руны, конечно же

Мы можем использовать их вне компонентов и помещать в js файлы, с расширением .svelte.js или .svelte.ts

Поехали разбираться! 💪

🌟Уже по сложившейся традиции предположим, что нам снова необходимо забрать задачи из сервера и вывести их
Но теперь мы будем хранить все наши значения в общем хранилище, которое создадим в файле state.svelte.js


export const todoState = $state({
todos: [],
});


🌟Реализуем экшен, который забирает задачки и записывает в стейт, по-прежнему все очень просто, поскольку мы работаем с базовым функционалом рун, то значит можем мутировать данные


export async function getTodos() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
headers: {
'Content-Type': 'application/json',
},
});
if (response.ok) {
const todos = await response.json();
todoState.todos = todos;
}
}


🌟Аналогичным образом реализуем функцию создания задачи


export async function addTodo(todoTitle) {
const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ title: todoTitle }),
});
if (response.ok) {
const todo = await response.json();
todoState.todos.push(todo);
}
}


🌟Для удаления сперва находим индекс выбранного нами элемента и удаляем с помощью splice


export async function deleteTodo(todoId) {
const response = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId}`,
{ method: 'DELETE' }
);
if (response.ok) {
const index = todoState.todos.findIndex((todo) => todo.id === todoId);
todoState.todos.splice(index, 1);
}
}


Основной функционал готов, попробуем теперь его применить прямо в App.svelte, разберемся сперва с блоком script и после допишем разметку.
Здесь нам понадобится:
➡️импортировать нужные экшены и сам стейт
➡️создать функцию для добавления задач
➡️создать функцию для удаления по клику
➡️и вызвать экшен, который вернет нам все задачки при монтировании компонента


import { onMount } from 'svelte';
import { todoState, getTodos, addTodo, deleteTodo } from './state.svelte.js';

let todoTitle = $state('');

const todoFormSubmit = async (e) => {
e.preventDefault();
await addTodo(todoTitle);
todoTitle = '';
}

const onClickDeleteTodo = async (todoId) => await deleteTodo(todoId);

onMount(async () => {
await getTodos();
});


После манипуляций выше остается все применить, для этого мы:
➡️создадим форму с текстовым полем, по отправке которой будем дергать функцию todoFormSubmit
➡️а также выведем с помощью конструкции {#each} все элементы нашего стейта
➡️в задачках отрисуем кнопку для удаления и дернем функцию onClickDeleteTodo


<section>
<form onsubmit={(e) => todoFormSubmit(e)}>
<input type="text" bind:value={todoTitle} placeholder='add todo...' />
</form>

<ul>
{#each todoState.todos as todo (todo.id)}
<li>
<span>{todo.title}</span>
<input type="checkbox" checked={todo.completed} />
<button type="button" onclick={() => onClickDeleteTodo(todo.id)}>x</button>
</li>
{:else}
<li>Nothing...</li>
{/each}
</ul>
</section>


Вуаля! Логика отделена, компонент стал чище и все хранится теперь в отдельном файле, данные из которого можно переиспользовать в разных компонентах не прибегая к бесконечной передаче пропсов
  • 🔥 4
  • ❤ 3
  • 👍 3
Post #37 154
👩‍💻 Svelte: $derived и работа с состоянием

И снова здравствуйте 🤔

Не отвлекаясь от подготовки к новогодним праздникам рассмотрим одну руну, которая позволяет довольно комфортабельно взаимодействовать с состоянием приложения

Пока во Vue есть computed, Svelte же предлагает $derived, что в переводе буквально означает производное состояние от какого-либо другого

Как всегда с примерами жизнь краше, поэтому предположим, что у нас есть список задач, которые нужно получить

🌟Создаем запрос при монтировании компонента и записываем данные в стейт, для этого нам понадобится onMount и уже известная руна $state:

import { onMount } from 'svelte';

let todos = $state([]);

onMount(async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/todos');
todos = await res.json();
});
</script>


🌟Например, нам нужно отфильтровать уже завершенные задачи, чтоб вывести в каком-то отдельном месте, а заодно и посчитать количество всех имеющихся, для этого мы как раз воспользуемся $derived

let todosLen = $derived(todos.length);
let todosCompleted = $derived(todos.filter(todo => todo.completed));


📌При этом если изменятся исходный стейт todos, то автоматически изменится и производные todosLen с todosCompleted

Но вот клиент внезапно захотел, чтобы в заголовках задач добавлялась приставка [completed] и [uncompleted], это уже несколько сложнее и потенциально в одну строку не вместится, что делать?

Для этого Svelte предлагает $derived.by, который принимает функцию в качестве аргумента и позволяет выполнять более сложные манипуляции

🌟Берем наши todos, $derived.by и скрещиваем:

  let tempTodosArray = [];
for (const todo of todos) {
if (todo.completed) {
tempTodosArray = [...tempTodosArray, {
...todo,
title: `${todo.title} [completed]`
}]
} else {
tempTodosArray = [...tempTodosArray, {
...todo,
title: `${todo.title} [uncompleted]`
}]
}
}
return tempTodosArray;
});


И все, клиент доволен, мы справились, а при выводе массив продолжит автоматически пересчитываться, если произойдут какие-то изменения в родительском состоянии

#svelte
  • ❤ 5
  • 🔥 4
  • 👍 3
Post #36 147
👩‍💻Svelte: обработка промисов с помощью {#await} и нюансы {#each} блоков

Доброго времени суток 👓

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

Svelte же в свою очередь предлагает новую фичу: {#await} блоки, которые упрощают работу с этим сценарием. Предположим, нам нужно вывести список постов

🌟Для начала создадим функцию с запросом на сервер и вернем промис:

<script>
const getPosts = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
return res.json();
};
</script>


🌟И следом прибегаем к использованию того самого {#await} следующим образом:

{#await getPosts()}
<span>waiting...</span>
{:then data}
{#each data as post (post.id)}
<p>{post.title}</p>
{:else}
<span>nothing...</span>
{/each}
{:catch error}
<span>something went wrong {error}</span>
{/await}


🌟Как мы видим, обрабатываются все три состояния: pending в блоке await, fulfilled в then и rejected в catch, тем самым избавляя от излишних переменных, проверок и дополнительных конструкций

НО

📌При желании можно отказаться и от catch, а так же и от await, если в них нет нужды. Например:

{#await getPosts() then data}
{#each data as post (post.id)}
...
{/each}
{/await}


🌟Перейдем к циклу, который выводит данные, теперь не обязательно делать дополнительныю проверку на пустоту массива, ведь each имеет дополнительный блок else, в котором можно выводить все, что нужно, если массив окажется пустым или данные недостаточно корректными

💡Также в круглых скобках есть post.id, это так называемый ключ, если его указать, Svelte будет лучше отслеживать точечные изменения в данных и тем самым избегает лишние ререндеры, что может лучше сказываться на оптимизации, если сценарий предусматривает более динамичные манипуляции

#svelte
  • 🔥 6
  • 👍 4
  • ⚡ 3
  • ❤ 1
Post #34 169
Руны в Svelte — это специальные ключевые слова, которые управляют поведением компилятора. Хотя их синтаксис напоминает функции с префиксом $, они не требуют импорта, так как являются неотъемлемой частью команды, часть корабля самой технологии.
Руна
$state() используется для создания реактивного состояния, которое автоматически обновляет интерфейс при изменении данных.
Допустим, нам нужно получить список задач и отобразить его.
<script>
import { onMount } from 'svelte';

let todos = $state([]); // создаем реактивное состояние

// асинхронно запрашиваем из сервера список задач
async function getAllTodos() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos');
if (!response.ok) {
throw new Error(`Статус ошибки: ${response.status}`);
}
todos = await response.json();
} catch (error) {
console.error(error.message);
}
}

// при монтировании компонента делаем запрос
onMount(async () => {
await getAllTodos();
});
</script>

<!-- Проверка, что пришел точно массив и в нем есть элементы -->
{#if Array.isArray(todos) && todos.length > 0}
<ul>
<!-- Проходим по массиву и выводим -->
{#each todos as todo}
<li>
<span>{todo.title}</span>
<input type="checkbox" checked={todo.completed} />
</li>
{/each}
</ul>
{/if}
Если мы используем в $state массив или объект, то результатом становится прокси — объект в JavaScript, который перехватывает операции с данными и добавляет к ним свою логику. Это позволяет Svelte отслеживать изменения в массивах и объектах на любом уровне вложенности. Важно, что такие данные можно мутировать, например, с помощью метода .push(), при этом оригинальные данные не будут изменены благодаря работе прокси.
Однако бывают случаи, когда нам не обязательно нужна глубокая реактивность. Например, если мы динамически подгружаем диаграммы или список публикаций в ленте, для этого может пригодиться руна $state.raw, которая предотвращает прямую мутацию состояния. Это полезно, если нам нужно управлять состоянием вручную и избежать перехватов операций на уровне вложенных данных.
Предположим, что при монтировании списка задач мы хотим изменить заголовок первого элемента массива. Для примера рассмотрим два варианта — с использованием $state([]) и $state.raw([]):
let todos = $state.raw([]); 
// let todos = $state([]); // если бы использовали обычное $state
...
onMount(async() => {
await getAllTodos();

// При $state([]) следующая строка сработает напрямую:
// todos[0].title = 'hello first string';

// Но при $state.raw([]) нужно обновить массив целиком:
todos = todos.map((todo, index) =>
index === 0
? { ...todo, title: 'hello first string' }
: todo
);
});
Иногда нам необходимо отслеживать изменения в массиве или объекте и для этого подойдет руна $state.snapshot. Дело в том, что если мы попробуем просто вывести реактивное состояние в консоль, Svelte сразу предупредит нас об этом:
let todos = $state([]);
...
onMount(async() => {
await getAllTodos();
// Warning: Your `console.log` contained `$state` proxies. Consider using `$inspect(...)` or `$state.snapshot(...)` instead
// console.log(todos);
console.log($state.snapshot(todos));
});
Используя $state.snapshot, мы получаем статичный снимок текущего состояния, который больше не является реактивным. Это полезно, если нам нужно просто посмотреть на данные без отслеживания последующих изменений.
Также в процессе разработки можно использовать $inspect, которая запускается каждый раз при изменении отслеживаемого состояния. Она работает исключительно в режиме разработки и полностью исчезает после сборки. В отличие от обычного console.log, руна $inspect автоматически обновляется и логирует новые значения при каждом изменении данных:
let todos = $state([]);
...
onMount(async() => {
await getAllTodos();
});
$inspect(todos);
Важно помнить, что $inspect предоставляет возможность следить за изменениями реактивного состояния в реальном времени, что может быть крайне полезно при отладке
  • ❤ 5
  • 👍 5
  • 🔥 4
Post #30 222
Всем привет 😐

Меня зовут Гриша, я действующий frontend-разработчик

Моим основным стеком на данный момент являются JS, Vue и Svelte, а также все, что касается верстки

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

Чем мы и будем промышлять, присоединяйтесь 👉
  • 👍 13
  • ❤ 7
  • 🔥 7
Post #1
Channel created

About this channel

How can I read @hryh0rii without a Telegram account?
TGViewer shows the public web preview Telegram publishes for Григорий Петров / IT: recent posts, photos, videos and the subscriber count, with no app, login or account.
How many subscribers does Григорий Петров / IT have?
Григорий Петров / IT (@hryh0rii) has 42 subscribers on Telegram, refreshed roughly every 30 minutes.
Does Григорий Петров / IT know I viewed it here?
No. Public channel previews carry no viewer identity, and TGViewer has no accounts or tracking of what you look up.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →