Благодарю за статью и хорошо структурированный код. Сам незримо участвовал для получения опыта — за пару дней собрал рабочий прототип (тоже canvas), определил моменты в которых был слаб и точечно прокачал. Отдавая себе отчет, что первое-второе место для javascript-разработчика (а участвовало их много) взять будет неоправданно сложно, а поощрительные призы экономически невыгодны, дальше дорабатывать не стал. А пара дней на то, чтобы расшевелить мозг новыми задачами — это удовольствие.
Поддерживаю, статья вообще ни о чем, конкретики никакой. Хранить утилиты в отдельной папке, использовать чистые функции, думать перед тем как писать код… Для кого это все
Спасибо, вы правы, подход createProxy(store).registration.email.toString() / toString(createProxy(store).registration.email) может облегчить рефакторинг, если совпадут факторы: в проекте используется типизация, она сделана корректно, IDE ее понимает и корректно меняет параметры при рефакторинге.
В остальных случаях строка подходит отлично — findAndReplace 'store.registration.email' справится с той же скоростью и надежностью, пожалуй я перебдел в данном случае.
Стараюсь не вписываться в такие проекты — жизнь одна, и если оглядываешься и видишь что полгода не развивался вообще, а писал бизнес-логику и правил баги, в постоянной спешке и стрессе, то должны быть мощнейшие другие мотивирующие факторы. В госкомпаниях и их подрядчиках таких факторов как правило немного. Благо живем в мире, переживающем глобализацию, и найти интересный и хорошо оплачиваемый проект вполне возможно
Для сервиса шаблонизированных темплейтов на node.js я использовал Marko — он на порядок быстрее Pug, кстати, так как преобразует все в чистые функции. Выдерживал прессинг танка с коронными 4мс скоростью ответа (вход-выход по nginx). Возможно, пригодится
Реакт да, тормоз в этом смысле, но у него ssr это побочная фича, которая мало кому нужна
Полностью согласен про «лучшее из возможного» — все эти презентации, встречи и т.п. нужны для того, чтобы согласовать разумные сроки. Мне встречались такие кейсы:
— сроки назначены сверху (или клиентами), часто уже просрочены, и абсолютно оторваны от реальности. Здесь обучение руководства с помощью презентаций и маппинг их радужных представлений с реальностью необходимы, иначе проект просто умрет. Например, от сильного прессинга, под которым невозможно станет работать.
— сроки сильно приуменьшены, но реальны — как принято говорить, «минимально необходимы». В этом случае нужно донести, что бизнес-задача будет выполнена, но образуется техдолг, который будет замедлять и замедлять развитие проекта, до состояния когда простейшая задача будет выполняться полдня или целый день и приводить к непредсказуемым багам. На моем опыте в такое состояние проект приходит в среднем за полтора года, при подобной схеме управления.
— сроки адекватные, техдолг минимальный
— сроки раздутые — тут уже работу нужно проводить с командой, чтобы не распалялась на «а давайте все перепишем на новую хайповую технологию» и другие бесполезные в основном затеи.
Если работаю в компаниях с первыми двумя «управленческими схемами», и при этом «предоставьте Ваше видение мы им подотремся», варианта два — проверить себя, как долго сможешь там отсидеть, либо найти более адекватных ребят.
Достаточно классический разговор — в молодости по максимализму не раз вел такие, отстаивая идеализированный мир, где важны не деньги, а самореализация и позитивный след в истории, способствующий развитию. Со временем баланс выровнялся, и примерно ["деньги", "реализация и развитие", "польза для общества"].test(benefit => weight(benefit) === "33.3%") === true. Для меня этот баланс поддерживает гармонию компонентов в системе «личность».
Я не могу сказать, что что-то здесь для меня вторично. Вполне реально найти компанию, отвечающую такому соотношению, как, в общем, и любому другому. У вас деньги занимают 90%? Найдете и такую компанию. Хотя куда проще было бы для подобной цели работать чиновником.
К слову, достижение корректного соотношения в целях работает не только параллельно, но и последовательно — я могу устроиться на проектную работу ради денег на 2 месяца, а после его завершения — заняться обучением и творчеством, волонтерством на 2 месяца. В случае, если предложений от гармоничных компаний (на мой взгляд) в данный момент нет.
Пока (?) не перекупили, делюсь опытом для тех, кого тоже пока (?) не перекупили. Мне довелось пока (?) не превратиться в разработчика-мебель, за бонусы готового не раздражать начальство своим мнением и опытом, и исправно создающим сотни багов и мучений пользователям своим быстрокодом.
А про все то что вы описываете я уже сказал — «Не с первого раза, так с десятого», зависит от того, зачем вы вообще занимаетесь программированием, и если только за деньги — возможно, это не ваше.
react-router ну очень многого не умеет, тоже думаю о том, чтобы написать статью про проектирование качественного роутера. И про ssr на этом стеке тоже тема актуальная, хотя его, по моим прикидкам, сделать намного проще.
Хорошая практика — везде использовать именованные экспорты / импорты. Вместе import Field from './field' лучше использовать import { Field } from './Field'. Иначе готовьтесь к Fild / Feld и т.п. переменным по недосмотру.
Непрофессиональный цикл статей, на мой взгляд. Даже в простейших примерах явные ошибки. То setState как будто синхронный, то текстовые пропы от руки каждый раз пишутся вместо констант, то вот label здесь без htmlFor. Если кто-то все же читает, лучше используйте такой паттерн:
<Input.Text
label="Name"
storePath="forms.registration.email"
/>
// и внутри компонента
const id = generateId(props.storePath);
return (
<div>
<label htmlFor={id}>{props.label}</label>
<input type="text" id={id} value={getValueFromStore(props.storePath)}/>
</div>
)
Акцент на автоматической генерации id, удобстве получения данных из стора, и фокусировании инпута при клике на label.
В примере storePath строкой задано, что при рефакторинге может вылезти в баг, поэтому тоже лучше генерировать storePath={pathToString(forms.registration.email)}
Дипломатические методы — обсуждение, убеждение, презентации для руководства разного уровня — могут принести достаточно ресурсов, чтобы сделать большую часть проекта качественно. Я в нескольких компаниях на цифрах показывал, сколько убытков в деньгах и времени приносит некачественный быстрокод и засилье «дешевых» разработчиков в проекте — и если для руководства важен бизнес и результат, а не распил, то непременно пойдут навстречу. Не с первого раза, так с десятого.
«Извращение», «все неправильно» и особенно «все уже поняли что всем нужна типизация», как модно было говорить в докладах на недавних конференциях, для меня не аргументы использовать эту технологию.
Если бы я знал о явных преимуществах и они стоили всех мучений и долгого обучения — как вы правильно заметили, за полгода команда довольно сильных специалистов не научилась «правильно работать со строгой типизацией», хотя в основном проблемы зависели не от команды, то можно было бы дальше возиться с этим и обильно осваивать бюджет компании. Но нет. Предпочитаю делать продуманные качественные продукты и приносить пользу, а не возиться с почти бесполезными инструментами, не двигающими к этой цели.
Я не говорю за всех разработчиков и за все проекты — как я писал выше, в отдельных библиотеках использование типизации скорее оправдано, а в сложных многосоставных системах вроде SPA явно негативный эффект — замедление разработки без видимых профитов. IMHO
Я описал только малую часть проблем — каждый день несколько часов работы команды уходило на типизацию и борьбу с негативными эффектами. Разумеется, все можно победить — вопрос зачем? Пользу от типизации в SPA я до сих пор не увидел.
Ошибки в типах в рантайме только увеличились. Рефакторинг стал сложнее — в ES6 проекте 90% рефакторинга выполняется findAndReplace с помощью регулярок, с типизацией работа удваивается. Переменные как были с опечатками или не отражающие назначения, так и остались. В опенсорс-зависимостях очень часто выходят версии с некорректными типами, ломающими сборку, и приходится форкать и вручную исправлять. Перечислять минусы могу еще долго.
Я действительно хочу понять, что именно сподвигает некоторых людей топить за Typescript в SPA.
Проигрывание этой истории и логирование последних действий при возникновении глобальной ошибки, полагаю, тоже сделать несложно — мне не пригождалось, так как стараюсь решать проблему поиска причины залогированного бага с помощью именованного stackTrace, типизированного error.name и человекопонятного error.message.
Поэтому я бы не рассматривал встроенный в Redux механизм истории как серьезный аргумент в его пользу — если он действительно нужен, реализовать его довольно просто.
По теме гонок и каскадных обновлений Redux, как вы правильно заметили, своим диспетчером решает проблему «частично», и мне, к сожалению, приходилось работать в приложениях, где «все остальные части» не решались, и они очень страдали от нагромождения бойлерплейтов и дополнительных библиотек и мидлвар, в которых крайне сложно было создать стабильный флоу. Это не камень в огород Redux, я скорее о том, что проектирование архитектуры никто не отменяет.
import React from 'react';
import { observer } from 'utils';
import { useStore } from 'hooks';
function TestComponent() {
const store = useStore();
const { currentTP: { executions } } = store;
return <div>{executions.fetchSymbol ? 'Загружается...' : 'Загружен'}</div>;
}
export const TestComponentConnected = observer(TestComponent);
Вроде полностью соответствует вашему описанию, только в виде объекта с ключами выполняемых асинхронных действий. Это выгоднее, так как позволяет обновлять компонент только при обновлении стейта конкретного действия — если же использовать массив (loaders.indexOf('fetchSymbol') !== -1), компонент будет обновляться и при попадании в этот массив всех других, ненужных для данного компонента стейтов. Хотя в Redux можно сделать селектор для этого. Глобальный лоадер можно завязать на Object.entries(executions).some(([key, value]) => value === true).
Разумеется, на любом движке или ванильном JS можно сделать то же самое, просто на MobX это очень просто и удобно.
createProxy(store).registration.email.toString()
/toString(createProxy(store).registration.email)
может облегчить рефакторинг, если совпадут факторы: в проекте используется типизация, она сделана корректно, IDE ее понимает и корректно меняет параметры при рефакторинге.В остальных случаях строка подходит отлично — findAndReplace 'store.registration.email' справится с той же скоростью и надежностью, пожалуй я перебдел в данном случае.
Реакт да, тормоз в этом смысле, но у него ssr это побочная фича, которая мало кому нужна
— сроки назначены сверху (или клиентами), часто уже просрочены, и абсолютно оторваны от реальности. Здесь обучение руководства с помощью презентаций и маппинг их радужных представлений с реальностью необходимы, иначе проект просто умрет. Например, от сильного прессинга, под которым невозможно станет работать.
— сроки сильно приуменьшены, но реальны — как принято говорить, «минимально необходимы». В этом случае нужно донести, что бизнес-задача будет выполнена, но образуется техдолг, который будет замедлять и замедлять развитие проекта, до состояния когда простейшая задача будет выполняться полдня или целый день и приводить к непредсказуемым багам. На моем опыте в такое состояние проект приходит в среднем за полтора года, при подобной схеме управления.
— сроки адекватные, техдолг минимальный
— сроки раздутые — тут уже работу нужно проводить с командой, чтобы не распалялась на «а давайте все перепишем на новую хайповую технологию» и другие бесполезные в основном затеи.
Если работаю в компаниях с первыми двумя «управленческими схемами», и при этом «предоставьте Ваше видение мы им подотремся», варианта два — проверить себя, как долго сможешь там отсидеть, либо найти более адекватных ребят.
["деньги", "реализация и развитие", "польза для общества"].test(benefit => weight(benefit) === "33.3%") === true
. Для меня этот баланс поддерживает гармонию компонентов в системе «личность».Я не могу сказать, что что-то здесь для меня вторично. Вполне реально найти компанию, отвечающую такому соотношению, как, в общем, и любому другому. У вас деньги занимают 90%? Найдете и такую компанию. Хотя куда проще было бы для подобной цели работать чиновником.
К слову, достижение корректного соотношения в целях работает не только параллельно, но и последовательно — я могу устроиться на проектную работу ради денег на 2 месяца, а после его завершения — заняться обучением и творчеством, волонтерством на 2 месяца. В случае, если предложений от гармоничных компаний (на мой взгляд) в данный момент нет.
А про все то что вы описываете я уже сказал — «Не с первого раза, так с десятого», зависит от того, зачем вы вообще занимаетесь программированием, и если только за деньги — возможно, это не ваше.
import Field from './field'
лучше использоватьimport { Field } from './Field'
. Иначе готовьтесь к Fild / Feld и т.п. переменным по недосмотру.Непрофессиональный цикл статей, на мой взгляд. Даже в простейших примерах явные ошибки. То setState как будто синхронный, то текстовые пропы от руки каждый раз пишутся вместо констант, то вот label здесь без htmlFor. Если кто-то все же читает, лучше используйте такой паттерн:
Акцент на автоматической генерации id, удобстве получения данных из стора, и фокусировании инпута при клике на label.
В примере storePath строкой задано, что при рефакторинге может вылезти в баг, поэтому тоже лучше генерировать
storePath={pathToString(forms.registration.email)}
Удобнее было бы не контролировать с помощью PropTypes постфактум, а дать сразу набор возможных значений
Если бы я знал о явных преимуществах и они стоили всех мучений и долгого обучения — как вы правильно заметили, за полгода команда довольно сильных специалистов не научилась «правильно работать со строгой типизацией», хотя в основном проблемы зависели не от команды, то можно было бы дальше возиться с этим и обильно осваивать бюджет компании. Но нет. Предпочитаю делать продуманные качественные продукты и приносить пользу, а не возиться с почти бесполезными инструментами, не двигающими к этой цели.
Я не говорю за всех разработчиков и за все проекты — как я писал выше, в отдельных библиотеках использование типизации скорее оправдано, а в сложных многосоставных системах вроде SPA явно негативный эффект — замедление разработки без видимых профитов. IMHO
Ошибки в типах в рантайме только увеличились. Рефакторинг стал сложнее — в ES6 проекте 90% рефакторинга выполняется findAndReplace с помощью регулярок, с типизацией работа удваивается. Переменные как были с опечатками или не отражающие назначения, так и остались. В опенсорс-зависимостях очень часто выходят версии с некорректными типами, ломающими сборку, и приходится форкать и вручную исправлять. Перечислять минусы могу еще долго.
Я действительно хочу понять, что именно сподвигает некоторых людей топить за Typescript в SPA.
Доброго дня.
тривиально реализуется через autorun. Можно делать глобальные слепки -
очищая от функций и ненужных данных, разумеется, либо пушить только изменения конкретных данных
Проигрывание этой истории и логирование последних действий при возникновении глобальной ошибки, полагаю, тоже сделать несложно — мне не пригождалось, так как стараюсь решать проблему поиска причины залогированного бага с помощью именованного stackTrace, типизированного
error.name
и человекопонятногоerror.message
.Поэтому я бы не рассматривал встроенный в Redux механизм истории как серьезный аргумент в его пользу — если он действительно нужен, реализовать его довольно просто.
По теме гонок и каскадных обновлений Redux, как вы правильно заметили, своим диспетчером решает проблему «частично», и мне, к сожалению, приходилось работать в приложениях, где «все остальные части» не решались, и они очень страдали от нагромождения бойлерплейтов и дополнительных библиотек и мидлвар, в которых крайне сложно было создать стабильный флоу. Это не камень в огород Redux, я скорее о том, что проектирование архитектуры никто не отменяет.
Вроде полностью соответствует вашему описанию, только в виде объекта с ключами выполняемых асинхронных действий. Это выгоднее, так как позволяет обновлять компонент только при обновлении стейта конкретного действия — если же использовать массив (
loaders.indexOf('fetchSymbol') !== -1
), компонент будет обновляться и при попадании в этот массив всех других, ненужных для данного компонента стейтов. Хотя в Redux можно сделать селектор для этого. Глобальный лоадер можно завязать наObject.entries(executions).some(([key, value]) => value === true)
.Разумеется, на любом движке или ванильном JS можно сделать то же самое, просто на MobX это очень просто и удобно.