Pull to refresh
44
-3
Дмитрий Казаков @DmitryKazakov8

Front-end архитектор

Send message

Согласен, главный минус - малое коммьюнити и количество библиотек. В реальных проектах нужен очень большой выбор библиотек, потому что все с нуля писать - не только занимает много времени (даже для селектов библиотеки пишутся годами из-за совместимости с большим количеством браузеров), но и требует намного более тщательной поддержки, что усложняет проекты. То есть упрощение написания функционала в SolidJS по сравнению с React становится незначительным по сравнению с тем, что нужно самостоятельно реализовывать на порядки больше компонентов.

Но библиотека хорошая, спасибо за статью.

Почему плохо - я привел несколько аргументов в комментарии выше.
Про сильную связанность с view, про сложность с SSR, про невозможность вызова вне Реакта (например до рендеринга приложения или в ответ на какую-то реакцию), про сложности генерации реалтаймовых валидаторов, про отсутствие возможности протестировать вне компонентов, про слабую связь со стором (например для вывода нотификаций).
Как хорошо - делать независимый от фреймворка слой апи, при необходимости передавая в него разные сущности типа сторов, экшенов, локализации. Пример я приводил здесь https://github.com/mobxjs-ru/api-by-config

На мой взгляд, сокращение имен классов - это экономия на спичках в ущерб DX.

  1. При сжатии gzip/brotli экономия на большой html будет в байтах, максимум - в паре килобайт. Это не повлияет на оценку Lighthouse и на пользовательский опыт, если вы не гонитесь за тысячными долями процента перфоманса

  2. При чтении дерева элементов в инспекторе видим несемантичные j1 g83 классы, соответственно как найти место в коде, в котором нужно поправить стиль? Можно переключиться на React Dev Tools, выбрать элемент, потом обратно в инспектор, проверить там новые стили, и дальше - копать код проекта, перебирая папки и файлы в рандомном порядке. Проблема усугубляется тем больше, чем больше разработчиков в компании, и чем хуже знаешь кодовую базу проекта. Частично проблема решится CSS maps, но тоже много лишних телодвижений.

    При этом семантичные CSS Modules сразу покажут где находится файл - src-components-layouts-userMenu src-pages-user.

  3. При регистрации ошибки в Sentry например, разработчик смотрит путь, который пользователь накликал (в Сентри это логируется). И в случае сокращенных имен он увидит click[.j1] - как с этим работать? Скорость дебага значительно увеличится.

  4. Для автотестов стабильные имена [path][name]__[local]--[hash:base64:5] можно использовать как уникальные идентификаторы элементов в ряде случаев и обойтись без data-test-id или id (если не учитывать в селекторе hash). Также можно дать не сильно разбирающемуся в автотестах сотруднику визуальный инструмент накликивания пользовательских сценариев. Тогда он передаст разработчику семантичный список действий click[.src-components-layouts-userMenu] и разработчик при необходимости очень быстро добавит id либо оставит селекторы в оригинальном виде, если эти классы достаточно специфичны.

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

Должен быть контект для решения задачи. "Наш продукт имеет систему виджетов, которые могут получать данные из разных источников, поэтому рассмотрим вариант когда url для фетча передается в компонент из внешнего источника". Дальше - "допустим, вы решили делать фетчинг через кастомный хук, расскажите, какие минусы и плюсы у этого решения и как бы вы это реализовали". Вот это была бы достойная задача на собеседование, проверяющая глубину опыта. А без контекста - это конь в вакууме, проверяющий читали ли вы документацию Реакта и fetch, чтобы проверить самую базовую базу

Ну вот позвольте рассказать, как я бы воспринял эту задачу, чтобы объяснить мою позицию.
Я работал в десятке компаний и сделал больше сотни сайтов, половина - на Реакте. Каждый день вырабатывал практики, как организовать слои приложения так, чтобы они были одновременно независимыми и при этом легко интегрировались друг в друга.
Я ожидаю, что на собеседовании проверят не то, как я различаю == и ===, и не чему будет равно [] == true, а проверят реальный опыт на реальных задачах, чтобы я смог показать глубину опыта.
Мне дают на собеседовании задачу про "компонент получает url", на что я сразу напрягаюсь, "компонент функциональный", на что соответственно сразу думаю про SSR, "хук, которых фетчит данные", на что сразу думаю - куда я попал?
Первое, что я отвечу - так делать нельзя и только джуны могут так делать. Расскажу про все, что перечислил в первом посте, займет минут 20. У собеседующего время ограничено, он не проверил мое "знание хуков Реакта", но получил море как бы не связанной с изначальным намерением информации.

Адекватный собеседующий скажет "тогда давайте заменим получение данных по апи просто на асинхронную функцию", и тогда я сразу соглашуть написать то решение, которое приведено в статье - с зависимостями во втором аргументе useEffect и с возвращением псевдо-прерывающей функции, чтобы при размаунте результат не обрабатывался. Конечно, добавил бы про обсерваблы, про варианты обхода ручного (!cancelled), про унификацию обработки, про типизацию ошибок, про коннект к разным частям приложения, про слои и т.п. еще на час беседы, но т.к. время ограничено и я четко понял, что задачей хотят проверить "понимание основ React", то опустил бы.

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

В итоге - мы оба потратили много времени на обсуждение организации апи и по итогу часа я выполнил 1 задачу из 3. Собеседующий так и записал, пришло письмо от HR "вы нам не подходите". История не особо выдуманная, на опыте было немало такого) Может, это мои заморочки, что не готов обсуждать совсем базовые штуки (шутка про Яндекс, в который я прошел около 20 собесов в разное время, чтобы увидеть какая дичь у них в коде), но и на джуна я не собеседуюсь - а такие вопросы тем не менее в каждой первой компании.

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

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

Да, как отправную точку - отлично, но в выводе статьи - "Задачка позволяет проверить, как кандидат понимает устройство рендеринга React...", то есть ответ "так нельзя" скорее всего приведет к тому, что интервьюер либо решит, что задачу кандидат не решил и не знает основ, либо не сможет проверить эти знания потому что под рукой нет более подходящей задачи. То есть все же считаю что правильно сказал - определенные разрабы отсекутся этой задачей, потому что она проверяет для интервьюера одно, а для соискателя может быть совсем другое (знание архитектур, а не setState и useEffect Реакта)

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

Эта задача проверяет исключительно понимание реализации на функциональных компонентах Реакта с использованием хуков. Для проверки этого можно придумать пример порелевантней, потому что если использовать кейс получения данных по апи - то адекватный ответ для всех, кроме джунов, это - "так делать нельзя". А джуны как раз будут пилить setState, useEffect, обрабатывать ошибки и бороться с race condition) То есть как раз "уверенно владеющие основами" люди. Если надо найти именно таких - то статья релевантная, хотя лучше бы без вызова апи, потому что отсекает более опытных разрабов.

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

  • как организовать апи, чтобы можно было вызывать как из Реакта, так и из экшенов / моделей / реакций, и при этом в Реакте можно было узнать состояние запроса и отобразить лоадер

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

  • как организовать типизированные запросы (чтобы не строки передавать в качестве урла, а аргументы функции) и обогащение запросов хедерами типа авторизации (это тема про "отдельный независимый слой апи")

  • как тестировать апи с моками и отдельно от вью/моделей и других слоев, а также в связке с ними

  • как сделать реалтаймовую валидацию входящих-исходящих данных исходя из TS-моделей

  • как связать отображение ошибок и глобальные компоненты (нотификации, модалки), при этом оставляя возможность отобразить ошибку в глубоко дочернем компоненте (например, вывод ошибки красным текстом под полем + нотификацию на странице в верхнем правом углу)

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

В дополнение к предыдущим комментаторам, всегда можно добавить соответствующие редиректы вручную. Для node js например таким кодом:

    if (req.url.endsWith('.js') || req.url.endsWith('.css')) {
      const acceptedCompression = getAcceptedCompression(req);

      if (acceptedCompression) {
        req.url = `${req.url}.${acceptedCompression.extension}`;
      }
    }

Полный пример - тут.

Полумеры для production не подойдут) Либо делать качественную обработку, либо пожертвовать чанками. Никто не будет следить вручную за всеми зависимостями, которые используют чанки - раз сделаешь импорт lodash - прибавится 500кб в несжатом виде, условно. Поэтому и написал в статье, что для тех проектов, в которых нужно минимальное количество js на старте, esbuild не подходит

Rollup+swc это аналог webpack+swc. Postcss как я указал в статье уже используется, то есть с css проблем не будет. Скорость однозначно лучше у esbuild, а на счет поддержки ts - можно же использовать esbuild+swc, тогда все, что поддерживает swc, можно использовать.

А куда их выделять, в entry массив? Если так - то если в каждом чанке импортится скажем MUI или lodash, то они будут включаться в каждый чанк, и в итоге мы не сэкономим трафик пользователя, а значительно увеличим его, получив еще и проблемы коллизий.

Вручную такой механизм, не залезая во внутрянку esbuild, будет сложно сделать. Я тоже думал о такой схеме:

  • до билда смотрим все файлы на предмет асинхронных импортов и собираем новый entry points массив из них

  • продолжаем сборку со всеми этими entry points

  • смотрим в метафайле на общие зависимости всех entry points и выносим их в отдельный entry point и прописываем в externals весь этот список

  • пересобираем

Но как видно из схемы, общие зависимости придется грузить сразу вместе с базовым выходным файлом. Как ни кручу в голове разные сценарии, пока сам esbuild не сможет создавать чанки с соответствующими ссылками на используемые и пересекающиеся зависимости, этот функционал корректно не реализовать. Возможно получится сделать схему из 2-3 последовательных ребилдов, но это не то. Если получится - было бы интересно узнать, как

Лучше бы esbuild и для продакшен-сборки использовали и добавили в него нормальное разделение на чанки, чем новый сборщик по факту делать...

Я не сторонник подхода, когда для разработки используется одна экосистема, а для production-билда - другая, и легко можно поймать на проде ошибки, которых локально не было. В Vite это именно так, поэтому в коммерческих проектах его не использую. Также перфоманс production сборки через Rollup (его использует Vite) хуже, чем моя текущая связка Webpack+SWC. Конкретные цифры не приведу, но в районе 20%, как и при чистом Rollup. А в дев-режиме ускорение с 0.2с (Webpack+SWC) до 0.1с (Vite) совершенно недостаточно, чтобы сподвигнуть на переход.

К Vite я тоже делал 3 подхода, но каждый раз натыкался на недостаточный функционал и низкий перфоманс, если не использовать ESM-dev-схему, которая мне не подходит, как написал выше. Поэтому тщательно изучать Vite после перехода на esbuild не вижу пока необходимости - скорее более интересно будущее Turbopack.

Думаю, имелось в виду именно в контексте Redux про миддлвары. Иначе это нападка на Promise, который имеет паттерн миддлвара, или Express.

либо использовать одну из многих либ для автобиндинга, если это не стрелочная функция - в mobx тоже есть autoBind: true параметр, если нужно. Но лучше приучаться писать в методах класса стрелочные функции, так не нужны дополнительные либы и обертки - чисто семантика языка

Спасибо за статью в популяризацию MobX, но многое не соответствует действительности.

  1. MobX - не библиотека глобальных сторов, и не библиотека локальных. Это система реактивности, когда можно подписаться на изменение параметров объекта.

const data = observable({ test: 1 }); 
autorun(() => console.log(data.test)); 
data.test = 2; 
// в консоли 1 и затем 2

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

  1. "библиотека, использующая в своей реализации Context API" - нет, MobX не привязан к фреймворкам типа React и не тянет их зависимостью. Но может быть использован во фреймворках, в том числе для передачи его реактивных объектов в React Context. Можно и через другие провайдеры и инджекты.

  2. "Mobx сторы хорошо масштабируются" - масштабируются архитектурные подходы, а MobX просто реактивная обвязка над объектами.

  3. "Mobx стор состоит из двух частей — самого стора с данными и провайдера этого стора" - только первое) Провайдер к фреймворкам - это отдельная история. Лучше написать что "чтобы подключить к React через Context нужно завести кроме стора еще и провайдер".

  4. "Стор в сущности функция‑генератор, которая возвращает объект" - нет, это может быть просто объект как в моем примере выше, но самый эффективный подход - оформлять сторы в виде классов

class Store { 
  constructor() { makeAutoObservable(this); }

  data = 1;

  toggle = () => { this.data = 2 } 
}

const store = new Store();

Таким образом этот стор сам по себе реактивный и типизированный.

  1. Если делать классами, то соответственно будет биндинг контекста и не придется писать

<button onClick={() => appStore.toggleTest()}>

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

<button onClick={appStore.toggleTest}>

сохраняя равенство по ссылкам и оптимизируя перфоманс процесса reconciliation Реакта.

Как правило, в проекте создается не только .env, но еще и example.dev.env и example.prod.env. Также нужно проверить, что в этих файлах есть все параметры, которые прописаны в ts-типах. Иначе будет частой ситуация, когда добавляется локальный ключ в .env, а в примерах не добавлен, и внезапно на компе другого разработчика проект не запустится (реальная и ооочень частая история). Вот пример, как можно это дело решить https://github.com/dkazakov8/dk-framework/tree/master/packages/compare-env

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

class Env {
  constructor(params: Record<keyof Env, unknown>) {
    Object.entries(params).forEach(([envKey, envValue]) => {
      const paramType = typeof this[envKey];

      if (paramType === 'boolean') {
        this[envKey] = envValue === true || envValue === 'true';
      } else if (paramType === 'string') {
        this[envKey] = (envValue || '').replace(/"/g, '').trim();
      } else if (paramType === 'number') {
        this[envKey] = Number(envValue || 0);
      }
    });
  }

  SENTRY_URL = '';
  HOT_RELOAD = false;
  HOT_RELOAD_PORT = 0;
}

export const env = new Env(process.env as unknown as Env);

// another file
import { env } from '../env';

env.SENTRY_URL // perfectly typed

Как параметры попадают из .env в process.env - это на усмотрение разработчика, самое простое решение - импортнуть dotenv. Также в моем решении предусмотрены типобезопасные дефолтные значения на случай, если в .env не выставлено значение типа "SENTRY_URL=". Но в отличие от статьи нет жестких проверок на типы и выдачи соответствующих ошибок если значение в .env указано не того типа - в целом фича хорошая, но малополезная, пока не встречал случая, чтобы кто-то смог сделать такую ошибку.

Спасибо за статью, возможно мои решения позволят в чем-то улучшить и ваш подход)

И у меня такой. Поставил на него Windows 95 - получился миниатиюрный псевдо-стационарный компьютер. Даже Fallout поставился. Подтормаживало, конечно, но было интересно

1
23 ...

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity