Pull to refresh
46
0
Дмитрий Казаков @DmitryKazakov8

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

Send message

Эта задача проверяет исключительно понимание реализации на функциональных компонентах Реакта с использованием хуков. Для проверки этого можно придумать пример порелевантней, потому что если использовать кейс получения данных по апи - то адекватный ответ для всех, кроме джунов, это - "так делать нельзя". А джуны как раз будут пилить 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 поставился. Подтормаживало, конечно, но было интересно

Мне казалось, что чтобы вырасти из миддла в синьоры нужны паттерны. Читал много книг, делал в разных паттернах решение одной и той же задачи, сравнивал перфоманс, масштрабируемость, DX и т.п. Но на протяжении последующих лет пригодились только в обсуждениях в чатах + для общего развития мышления. В реальности нужно совсем другое - абстракция сложности и понятность кода. Первое подразумевает, что можно в выделенной библиотеке писать так, чтобы тебе было понятно, а в коде проекта - чтобы было понятно джуну. Если проект долгоживущий - добавляешь к библиотекам документацию, чем руководствовался при выборе таких подходов, чтобы пришедшие на смену архитекторы могли дорабатывать либо переписывать по своему усмотрению, но код проекта оставался бы таким же простым.

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

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

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

Вариант лучше, без дополнительных классов/data-theme/отдельных параметров --color-dark/--color-light, за которыми еще и нужно следить для избежания коллизий имен - это CSS variables записывать напрямую в <html style="--color-primary=#ccc"> . При смене темы соответственно менять их значения там же. И только для исключительных случаев (разная логика в разных темах) пригодится глобальный идентификатор в виде класса или data-theme. Таким образом значительно уменьшится количество кода и не нужно в стилях прописывать дополнительные служебные переменные

Надо упомянуть, что схема не будет работать с SSR - кука будет прикреплена к домену jsonplaceholder.typicode.com , а не домену фронта, так что node-сервер не сможет ее получить и зафорвардить в апи. Также и из локалстораджа он не получит токен. То есть для SSR юзер всегда будет неавторизован. Конечно, для авторизованных зон SSR зачастую избыточен, но ряд сайтов (маркетплейсы, к примеру) от этого пострадают, хотя и не слишком сильно.

На Хабре обсуждалось не раз предложение "сделать единое AST для TS, ESLint, Webpack и других сборщиков, переиспользовать в IDE", вроде вы тоже участвовали в обсуждениях. Но каждый раз приходим к тому, что есть разные лицензии, корпоративные политики (TS), ограничения операционных систем, разность механик работы разных инструментов. Я тоже хотел бы видеть "универсальный процесс, генерирующий универсальное дерево с адаптером ко всем языкам программирования", это был бы, наверное, лучший вклад в экосистему разработки за десятилетия, но остается только мечтать...

Только начал всю эту канитель, вроде как можно не 8 лет для подтверждения квалификации, а 6 + описание проектов на полкниги. Надеюсь на удачу, что 70-75 баллов каким-то образом стрельнут, также думаю проработать вариант с удаленной работой на австралийскую контору - может тоже зачтется. Спасибо за опыт, креплюсь)

Самое главное забыли - в ряде случаев (когда они появились - практически во всех браузерах так было) у стрелочной функции нет name, `const fn = () => false`, fn.name undefined|''. То есть в стек-трейсе будет anonymous, и если код минифицирован, найти причину бага очень сложно. Но в современных браузерах name назначается по имени константы. Это все еще приходится держать в голове. В определенных случаях и современные браузеры не назначат - `const fn = (() => () => { return false })()`.

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

Еще натыкался на то, что стрелочные методы классов в определенных версиях транспиляторов не декорируются, так как на выходе код совершенно другой, чем у нестрелочного метода.

Фича в целом отличная, но об ограничениях нужно помнить

Information

Rating
761-st
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity