Обновить
52
Дмитрий Казаков@DmitryKazakov8

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

0,1
Рейтинг
45
Подписчики
Отправить сообщение

Посмотрел репо - там все основано на схемах, а не на исходных типах. Для фронтенда такое избыточно при работе по апи

Да, без TypeScript конечно тяжело проектам - получается неоткуда взять информацию о структурах, кроме ручных JSDoc. С TS AST конечно намного больше возможностей по автоматической генерации валидаторов

Entity Registry — плоский реестр сущностей, который ... обеспечивает точечный ре-рендер только тех компонентов, чьи данные действительно изменились

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

const UserList = observer(({ userIds }: { userIds: Set<UserId> }) => {
  const filteredUsers = userStore.usersDTO
    .filter(({ id }) => userIds.has(id)); 

  return (
    <ul>
      {filteredUsers.map((user) => <UserItem key={user.id} user={user} />)}
    </ul>
  );
});

Тут UserItem обновится точечно при изменении данных конкретного пользователя, и можно напрямую мутировать сущности (но лучше конечно через персистентное хранилище в виде БД бэка или LS, зависит от требований к функционалу).

Может быть я неправильно понял вопрос и не на то ответил - я говорил как “вернуть состояние назад” в beforeEnter, но возможно имелось в виду именно предотвращение в beforeLeave. Это пока не стабилизированный функционал, к сожалению:

  • браузер поменял URL, вызвался redirect+lifecycle

  • beforeLeave говорит preventRedirect(), state остается старым, URL в браузере - новым (что неправильно)

Сделаю доработку в библиотеке и добавлю тестов на эти кейсы, чтобы URL менялся обратно, вроде такого механизма

if (reason === ‘unmodified’) { 
  if (source === ‘popstate’ && actualUrl !== currentUrl) { 
    win?.history.replaceState(null, ‘’, currentUrl); 
  } 
}

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

P.S. Скорее всего из-за ограниченного доступа к History браузера - что нет полной манипуляции стеком истории, а только push в конец и replace текущего. Поэтому стабильного preventRedirect с "пост-фактум popstate" не получалось, и оставил половинчатое решение на будущее.

К сожалению или к счастью, сайты не могут блокировать браузерные кнопки вперед-назад и popstate соответственно) Поэтому нельзя “предотвратить переход, если нажал Назад в браузере”. Вот как сделано в роутере:

listener() {
  void this.redirect({ ...this.urlToState(location.href), replace: true });
},
historySyncStart() {
  win?.addEventListener('popstate', this.listener);
},

То есть жмешь “Назад”, запускается стандартный redirect, то есть сработает beforeEnter соответствующего конфига. И в нем можно проверить - откуда пришел

dashboard: {
  path: '/dashboard',
  loader: () => import('./pages/dashboard'),
  async beforeEnter({ redirect, currentState }) {
    // Пользователь находился на странице авторизации,
	// нажал кнопку "назад" и оказался на странице дашборда.
    // запретить браузеру менять свой URL мы не можем,
    // но можем явно проверить этот кейс и вернуть обратно на авторизацию
	
    if (currentState?.name === 'login') {
	  return redirect(currentState);
	}
  }
}

Рассинхрон будет в любом случае, каким бы роутером ни пользовались - браузер меняет URL (из js это не достать), посылает событие popstate (js это уже может поймать, вытянуть location.href и запустить цикл валидации и редиректов), затем в данном случае роутер заредиректит обратно. Но на состоянии приложения это не скажется - оно будет консистентным и источником правды для рендеринга.

Если говорить другими словами - каждая вкладка браузера это безопасный sandbox без root-доступа к API браузера из js. И popstate для роутера - полноценный заход на сайт как если бы было по прямой ссылке, но с доступом к предыдущему состоянию currentState, которое можно восстановить (эмуляция доступа к истории браузера).

Да, там делаю очень замороченный TS AST парсер с поддержкой всего, что может быть в Open API, включая реэкспорты, алиасы, построение инверсированных зависимостей (дерева импортов). Конкретно для этого кейса будет такой валидатор

user: [
  'union',
  { gender: ['literal', 'F'], childrenCount: 'number' },
  { gender: ['literal', 'M'], salary: 'number' }
]

И если бэк пришлет { gender: 'M', salary: '100' } то будет ошибка response.user is none of 2 types. Для дебага должно быть достаточно.

Ну и в любом случае это лучше, чем ловить где-то при открытии модалки undefined has no method toFixed in user.salary.toFixed()

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

Но на фронте задача другая - если использовать TS, то важна именно совместимость по типам - что пришла строка, а не число (и неважно - это email или isoDate), так обеспечивается работоспособность рантайма. А проверки на email в основном нужны бэку при сохранении данных (ну и для форм если надо - можно zod или аналог прикрутить).

При этом намного удобнее не полностью Swagger Open API переносить в проект, а детально заводить типы - если фронту в TypeUser нужно только 3 поля, а не 800 присылаемых бэком с relationships, ios / android / tv спецификой, то лучше так и завести. Это же потом и стабильность улучшит - не будет ломаться при изменении неиспользуемых полей или ошибок в них, фронтендер всегда может сказать "мы используем 3 поля, проверьте не устарела ли выдача из 800 полей", трафика меньше, тестировать проще, в сторы фронта не протекает лишнего и т.п. Думаю любой найдет много причин держать код и рантайм чистыми, а не "тащить все что дают".

В обозримом будущем планирую выпустить новую библиотеку, которая немного с другой стороны подходит к валидации, чем Zod, Valibot и другие schema-first решения.

В ней TS типы запроса и ответа по эндпоинту остаются в текущем виде и являются полноценным источником правды:

import type { TypeUser } from '@models/TypeUser';

type TypeRequest = { id: string; };

type TypeResponse = TypeUser;

А библиотека из этого файла генерирует валидаторы, которые можно вызвать в рантайме:

export default {
  request: { id: 'string' },
  response: {
    id: 'number',
    name: 'string',
    email: 'string',
  },
} as const;

Соответственно, остается встроить проверки в функцию вызова апи

import schema from 'validatiors/api/getUser.ts'

async function getUser(request: TypeRequest): TypeResponse {
  // удаляет все лишнее
  const reqErrors = check({ 
    schema: schema.request, 
    value: request, 
    getExtraneous: console.log 
  });
  
  if (reqErrors) throw new Error(reqErrors);
  
  const response = await fetch('/api/user').json();
  
  // удаляет все лишнее
  const resErrors = check({ 
    schema: schema.response, 
    value: response, 
    getExtraneous: console.log 
  });
  
  if (resErrors) throw new Error(resErrors);
  
  // TypeScript верит вам на слово,
  // но валидаторы не пропустят расхождений по типам и структуре данных
  return response;
};

Фронт ясно говорит, что ему нужны только 3 поля в TypeUser, поэтому check очистит легаси, приходящее с бэка, специфику для других платформ, дубликаты с разным неймингом.

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

Поддерживать TS-типы как источник правды намного проще (они все равно нужны в приложении), чем описывать схемы в Zod-стиле.

{ path: '/users/all', name: RouteNames.UserList, component: UserList }

это не спасет от пересечений имен, так как они заводятся в массиве. Чтобы TS исключал пересечения, нужна объектная структура

createConfigs({
  users: { path: '/users', loader: () => import('./pages/users') },
  user: { path: '/user', loader: () => import('./pages/user') },
  userSettings: { path: '/user/settings', loader: () => import('./pages/userSettings') }
});

// autocomplete + fast travel: 'users' | 'user' | 'userSettings'
router.redirect({ name: 'user' });

В остальном согласен со статьей - по name куда удобнее редиректить в больших проектах, чем по untyped path.

Спасают реалтаймовые валидаторы, тогда между фронтом и бэком контракт будет зафиксирован. Удобно, когда они автоматически из TS моделей выводятся - условно TypeResponse = { id: number } получится стабильным источником истины по типам и реальному контенту.

$mol всех побеждает, включая VanillaJS

Вот тут ссылка прямая, было интересно посмотреть актуальное сравнение в бенчмарках)

Vanilla JS document.getElementsByTagName("span"); 8,280,893
Prototype JS Prototype.Selector.select('span', document); 62,872
YUI YAHOO.util.Dom.getElementsBy(function(){return true;},'span'); 48,545
Ext JS Ext.query('span'); 46,915

Кажется, целая эпоха ушла...

просто остается ждать пока нас полностью не попрут

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

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

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

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

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

Я все ждал когда же статья вернется к теме - какие аргументы использовать для разубеждения "начальства", сравнение с другими психологическими манипуляциями вроде "мы семья, поэтому работайте по 12 часов", метрики по когнитивной сложности чтения лишних абстракций от LLM и потере понимания схемы работы проекта... Темы обширные и интересные, но вот статья закончилась не успев даже начаться.

В целом согласен) Сейчас топ-тир модели как раз на уровне мидла, который изучил много паттернов и все хочет попробовать. Абстракции, фабрики фабрик, кастомные эвентные системы, стратегии, контроллеры, registry и т.п. - Codex буквально в каждой задаче это все строит в моих экспериментальных плейграундах, если прямо детально план не расписывать.

Джун ничего не поймет и сделает ПР в духе "работает и ладно".

Мидл когда это увидит - восхитится и начнет все это плодить и множить, создавая fizz buzz enterprise для простых задач.

А вот синьор по рукам всем будет бить "эти 500 строк делаются вот этой одной строкой, хватит оверинженирить и тратить время на поддержку этого лапшекода".

Я обобщаю и утрирую, но ситуация в ИТ сейчас очень шаткая и индустрию трясет от ИИ-хайпа. Учиться профессии - можно, но рассчитывать на быстрый оффер для новичка - точно нет

доказал, что React был прав

В чем именно? У Solid уже был механизм для эффективного отображения в UI состояния загрузки

<Show when={!api.firstLoading} fallback={Loader}>{data}</Show>

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

Думаю, вы путаете "внедрение функционала конкурентов ради облегчения миграции" и "разные команды приходят к одному и тому же".

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

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

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

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

По поводу metadata (title, description, social теги) - это хотя и можно сделать через роутер (внутри beforeEnter), лучше все-таки использовать компоненты страниц. То есть проставлять их при рендере на onMount или другими способами конкретной UI-библиотеки, так сохранится поддержка SSR и MPA.

Под локализацией не понял, что имеется в виду, так как есть разные схемы. Например, заводят поддомены вида lang.mydomain , тогда в конфигурации роутера никаких изменений делать не нужно. Либо если через params - можно к каждому path добавлять параметр /:lang/home , либо через query: { lang: (v) => ['en', 'ru'].includes(v) } . Я бы рекомендовал вариант с поддоменами - поисковики с этим лучше работают, да и код не нужно подстраивать.

Статистики именно по "качеству" нигде нет, исхожу только из личного опыта и открытой статистики. Например, в прошлом году на Гитхабе 81% коммитов были именно в приватных рабочих репо, и процессы там как правило строже - с ревью, тестами, постоянной production-доработкой и адаптацией к новым технологиям.

А в публичных репо большая часть - пет-проекты или библиотеки даже без базовых тестов / эксперименты, которые в проде никто и не использовал. Да и из оставшихся репо многие устарели или заброшены с много лет висящими issues, потому что OSS очень непросто монетизировать, а на энтузиазме далеко не уедешь. Поэтому мысль "ИИ в основном обучается на менее качественном коде, не готовом к продакшену и стабильной предсказательной генерации", думаю, валидна.

Конечно, есть OSS авторы с шикарными библиотеками, но как правило они их писали как раз для продакшена и после нескольких лет доработок и обкатки выложили в публичные репо. Есть и компании, у которых бизнес-модель построена на OSS и они могут нанимать большие команды, чтобы делать качественно. Но это капля в море

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

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

1
23 ...

Информация

В рейтинге
3 949-й
Зарегистрирован
Активность