Search
Write a publication
Pull to refresh
4
0
Send message

Гляньте на Ангуляр... Ничего удобнее не придумали

Видел, кровь из глаз до сих пор течет

Про tradeoff "производительность vs фишки"

Вы опять несете чушь, вот смотрите:
1) Assembler(быстро) vs Javascript(медленно) - да, куча реальных удобных фишек за которые можно заплатить.


2) Реактивная мутабильность(быстро) vs Иммутабильность(медленно) - используя иммутабильность мы получаем более грязный, более раздутый, с большим кол-вом бойлерплейта код, который даёт ровно ноль реальных преимуществ, но при этом мы за это ещё и должны заплатить потерей производительность, в в идеальных условиях(и то, для фейковой иммутабильности) в 5 и более раз, а так легко и в 10 и в 100 раз. Выбирать то, что по всем фронтам хуже, а ещё и медленнее в разы и десятки раз - звучит как полный бред. И называть это tradeoff'ом смешно, это чистой воды безальтернативный downshifting и downgrade, идя на которые ты только теряешь.

вы всё равно можете использовать её, просто избегая прямых мутаций состояния.

Зачем? Если я знаю что я делаю и для чего я это делаю. И на шару не пишу this.count++. Зачем мне срать самом себе в удобстве, в красоте коде, в чистоте кода и в производительности.

Так да, голый реакт сам себе дно, из плюсов только JSX. Это вроде и так всем известно. Но если использовать его по назначению, как view, а управление состоянием отдать MobX'у, то всё как рукой снимает. И это сочетание превращается в отличный инструмент.

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

Не правда, ничего руками делать не нужно, MobX всё сделает сам, автоматически. Никаких лишних рендеров не будет. Особенно если сконфигурировать mobx на автобатчинг. Это объективно. Так же объективно, как я сам использую не MobX последние 2 года, а его самописный аналог, просто потому что могу) А с 2016 по 2023 использовал с react'ом исключительно mobx, потому что все альтернативы хуже.

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

Согласен

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

Да, и для меня тоже. Это обязательное условие для выполнения иммутабильности. В противном случае
oldState.title = 123;
И ваш актуальный "иммутабильный" объект, изменился, а вы не в курсе и react не в курсе, да и вообще никто не в курсе.

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

Может быть, но по состоянию на сегодняшний день, знания замылились и их можно обновить)


Ваш бенчмарк показывает:

1) Используя immer или Structural sharing вы НЕ используете иммутабильность. А используете костыль, и завуалировано используете мутабильность и proxy getters/setters.

https://stackblitz.com/edit/stackblitz-starters-bduimynh?file=src%2Findex.tsx

2) Ваш костыль всё равно в разы медленнее работает. А учитывая то, что код при этом гораздо хуже и грязнее, то этому нельзя простить падение производительности в разы.

А ещё у MobX скорость стабильная, а у костыля immer она плавает и чем идеальнее для него условия(меньше отношения к реальному миру).
И как вишенка на торте, MobX в качестве реализации реактивности с использованием getters/setters не самое быстрое решение, мой аналог MobX'a работает в среднем в 20 раз быстрее, а в определенных сценариях в 1000 раз быстрее. Т.е. если сравнить фейковую иммутабильность immera и удачную реализацию мутабильной реактивности, то проигрыш MobX'у умножьте ещё в 20 раз)

Как вы там говорите?)

в современных managed языках сплошь immutable, ибо так на круг выходит дешевле.

Дешевле? Пока этому ровно 0 подтверждений. Это и не удивительно, если понимать как работает CPU, RAM и т.п.

я спорю только с вашим аргументом, что иммутабельность -- супердорогая штука

Да, она дорогая) Опять же это очевидно любому кто понимает как работает CPU, RAM и т.п. А ещё это подтверждается бенчмарками.

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

Чёт вы рано начали оправдываться

Да нет) Я констатирую факты) В абсолютных значениях MobX не самый быстрый, но по сравнению с иммутабильностью он чудовищно быстрый) А у нас как раз разговор иммутабильность vs мутабильная реактивность)

Разумеется даже самая быстрая реализация иммутабельности для объекта с 1к полями будет медленнее прокси.

А это усреднённое значение, с упором на реальную жизнь, часто в реальной жизни мы имеем дело с массивами объектов, допустим 100 элементов и каждый это объект с 15 полями, а в некоторых полях могут быть ещё вложенные объекты и итого это условно равносильно, объекту с 1.5к-2к полями и больше. Но и то, это со звездочкой, один плоский объект с 2к полями будет быстрее в иммутабильности чем массив более мелких объектов, тем более с вложенными объектами где совокупное кол-во полей также 2к.

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

Зачем же вы с убогими-то сравниваете?

Да, реализация реактивности с помощью getters/setters у MobX не самая быстрая, но даже ее в принципе за глаза хватает в real world приложениях. Моя реализация реактивности с помощью getters/setters, которую я использую на своих проектах в этом тесте в 20 раз быстрее MobX.

я спорю только с вашим аргументом, что иммутабельность -- супердорогая штука

Как бы да, это дорогая штука, как по CPU, так и по RAM. Учите матчасть. Вот тут простыми словами всё изложено https://habr.com/ru/companies/ruvds/articles/926286/comments/#comment_28580098

которая якобы приводит к ПОЛНОМУ копированию всего состояния на каждый чих.

Как бы да, копирование ПОЛНОЕ) Иначе вы иммутабильность путаете с мутабильностью. Если копирование не полное, то вы НАРУШАЕТЕ иммутабильность, они больше иммутабильностью не является.

Это просто чушь, выкурите structural sharing уже.

Чушь несете вы, не понимая принципов работы процессора, материнской платы и оперативной памяти. И уж тем более не понимаете принципе иммутабильнсти.

Я просто в шоке от того, что вы не понимаете разницу между этим(Выше на скрине) и вот этим

this.count++;

У вас знания и понимание реально нулевые.

Вот вам, бенчмарк, в выгодных условиях для вашей фейковой полу иммутабильности со Structural Sharing

https://stackblitz.com/edit/stackblitz-starters-1f9errps?file=src%2Findex.tsx

Разница в 100 раз карл!

Вместо тысячи слов.

А вот с настоящей, используя так же ваш кривой Structural Sharing

https://stackblitz.com/edit/stackblitz-starters-kutw927n?file=src%2Findex.tsx

Тут уже в 500 раз проигрыш

А вот с настоящей иммутабильностью, но без лишней кривой прослойки в виде Structural Sharing

https://stackblitz.com/edit/stackblitz-starters-hkgeey7x?file=src%2Findex.tsx

Тоже проигрыш в 100 раз, но тут хотя бы настоящая иммутабильность.

Главная ремарка: примеры упрощены, и отражают именно работу MobX, а не обработку всех corner case) И все ваши пункты не имеют никакого отношения непосредственно к MobX. А вообще Keep It Simple Stupid и будет тебе счастье.

Конкретных разоблачений "брехни", конечно же, не дождёмся.

С чего вдруг? Я уже всё разоблачил, вот же они:

https://habr.com/ru/companies/ruvds/articles/926286/comments/#comment_28580098

И вот:

https://habr.com/ru/companies/ruvds/articles/926286/comments/#comment_28584342

И вот:

https://habr.com/ru/companies/ruvds/articles/926286/comments/#comment_28585100

И вот:
https://stackblitz.com/edit/vitejs-vite-dspjoj?file=src%2FApp.tsx&terminal=dev

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

А не поделитесь каким-то примером, где описано как правильно и при этом без мутных best practices? Вопрос без подвоха.

Не обращайте внимания, он описал сугубо индивидуумов, которых природа наградила так сказать альтернативным мышлением. Посмотрите этот коммент - https://habr.com/ru/companies/ruvds/articles/926286/comments/#comment_28589778

А ещё посмотрите вот это, тут конкретно показано как использовать react + mobx правильно:

https://stackblitz.com/edit/vitejs-vite-dspjoj?file=src%2FApp.tsx&terminal=dev

https://stackblitz.com/edit/vitejs-vite-ffchmx?file=src%2Fpages%2Fmain%2Findex.tsx&terminal=dev

https://stackblitz.com/edit/vitejs-vite-3ajbxs?file=src%2Fpages%2Fmain%2Findex.tsx&terminal=dev

А вот тут показано как надо сконфигурировать MobX чтобы всё было идеально, чтобы был автобатинг по умолчание и возможность там где надо(input и textarea) его отключать и снова включать.

https://stackblitz.com/edit/stackblitz-starters-yefape38?file=src%2FmobxConfig.ts,src%2FApp.tsx

Не снимет

Интересно, у всех снимает, а у вас не снимает?

т.к. в react оно пропихивается через явный костыль

Ага, т.е. хуки и useState это костыль. Понятно)))

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

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

export class MyState {
  fetching = false;
  items = [];
  error = null;

  constructor() {
    makeAutoObservable(this);

    this.fetchItems();
  }
  
  fetchItems = async () => {
    this.fetching = true;
    try {
      const response = await apiRequest(`GET /api/v1/items`);
      this.items = response.data;
      this.error = null;
    } catch(e) {
      this.error = e.message;
    } finally {
      this.fetching = false;
    }
  }
}

То, я боюсь представить чтобы было. если бы там был redux или голый реакт, или что угодно другое.

Нет, если правильно использовать то всё отлично

А в чём проблема? Это элементарно, причём by design.

но тут та же проблема что и со всем React в целом - излишек свободы говнокода и мутные best practice, зачастую выглядящие как грязные хаки.

У MobX таких проблем, не надо нести ахинею, он не пропагандирует писать говнокодище, в отличии от redux и ему подобных. Просто посмотрите на код из этого же комментария, это самое типичное и стандартное использование MobX, укажите мне хоть строку говнокода.

Или вот:

export const MyComponent = observer(() => {
  const [state] = useState(() => new MyState());
  
  if (state.fetching) return <div>fetching...</div>;
  
  return (
    <div>
      <button onClick={state.fetchItems}>Load more</button>
      {state.items.map(item => <div>{item.title}</div>)}
    </div>
  )
});

А вот типичное и стандартное использование, но уже внутри Реакт компонента у которого есть локальное состояние. Укажите мне хоть строку говнокода.

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

А потом удивляемся почему нас такой убогий софт окружает, который выжирает всю оперативку, лагает на мощном желе и т.п. Ну а как иначе, когда такой подход к разработке и такое "понимание" как всё устроено на самом деле.

Или Immer, например.

Proxy getters/setters поверх иммутабильности, опять "гениально") Зачем лишняя прослойка(иммутабильность)?

Тот, кто меняет -- знает, и должен соблюдать правило: "нет реальных изменений -- ничего не меняй"

Вы путаетесь в показаниях и включаете заднюю, вы не понимаете как работает иммутабильность, как она соблюдается и что для этого нужно делать) Зато льёте сказки про

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

Не надо от балды. Давайте говорить на языке фактов и того, как на самом деле работают те или иные вещи, а не на языке розовых пони в мире розовых облаков.

Это ответственность того, кто меняет данные

Гениально. Т.е. все ваши мнимые "гарантии" мгновенно испарились.

Да, нужна определённая дисциплина -- либо библиотека, берущая это на себя.

Класс, рекурсивный обход 2х объектов и сравнение полей)

Сделайте с сеттерами пачку из 100 изменений, вызвав эффект лишь один раз по окончании пачки. Например, в массиве 100 объектов нужно выставить у каждого "active = false" и не триггернуть 100 последовательных перерисовок. И, при этом желательно не делать 100 подписок на событие "onActiveChanged".

Элементарно.
Вот: https://stackblitz.com/edit/stackblitz-starters-c87ad3cy?file=src%2Findex.tsx

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

Вообще-то я фронт-энд разработчик, а микроконтроллеры это хобби, как и программирование в целом и изучение работы процессоров и периферии на низком уровне. Последние 9 лет использую MobX (мутабильность!) в связке с реактом.

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

const oldValue = { count: 1 };
// Упс, ссылка изменилась, а данные нет. Все гарантии в унитаз. 
// Лишний рендер здраствуй. Лишняя память привет.
const newValue = { count: 1 };

Как бы)

Вы с другой колокольни смотрите.

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

Так вот, ладно если бы платой за иммутабильность была только потеря производительности и памяти, так к этом прибавляется плата говонокода.
Вместо state.count++ приходится код в говно превращать.

Иммутабельность даёт гарантии, что объект, который ты получил, не взорвётся у тебя в руках останется тем же всегда, и тебе не надо кропотливо и пристально следить, не поменялось ли что-то в его нутрях, или втыкать и таскать повсюду кучу флагов isModified, или перерисовывать весь UI на каждый чих.

А вы как, на шару разрабатываете? Или модифицируете объекты осознанно, а не просто чтобы символом побольше напечатать?

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

Иммутабельность предоставляет простой способ детектирования наличия изменений: если ссылка на объект не поменялась, значит 100% изменений в нём нет, можно пропустить всю работу по проверке и обновлению данных и UI.

Да, максимально тупой и топорный, согласен. Но и не без приколов:

const oldValue = { count: 1 };
// Упс, ссылка изменилась, а данные нет. Все гарантии в унитаз. 
// Лишний рендер здраствуй. Лишняя память привет.
const newValue = { count: 1 };

Открою великую тайму, с 2010 года есть такие штуки getters/setters, вот с этих помощью прекрасно детектируются все изменения в объекте.

state.count = 0;
// Было изменение, вызывает реакции
state.count = 1;
// НЕ было изменений, НЕ вызывает реакций
state.count = 1;

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

Да вы что, прям яваскрипт знает какая часть неизменной будет, а какая нет?))) А может ещё это бесплатно и память не кушает?))) Опять розовые очки. Вернемся в реальность, либо полное копирование, либо нарушаете принцип иммутабильности.

У меня для вас плохие новости. Снимите розовые очки и попишите на C, реализуйте все эти подходы и вы поймете, что то, что вы думаете(просто где-то прочитав или услышал и приняв это за истину) это не имеет отношение к реальности. По копируйте структуры в цикле, проведите бенчмарки скорости и результат вас шокирует. Особенно ярко выражен он будет на голом железе, где исполнение программы и не делит ресурсы с тысячи других процессов. А измерять производительность там легко, подключили к ножке логический анализатор, подали/убрали напряжение с ножки, сделали работу(бенчмарк), подали/убрали напряжение с ножки и сравнили время между этими сигналами.
Купите себе микроконтроллер и попишите для него программки, там как раз голое железо, маленькие частоты десятки или сотни Mhz, это ничто по сравнению с гигагерцами и множеством ядер. Там вообще всё построено на мутабильности, а если вы там начнете заниматься иммутабильной дичью, то ваша программа будет:
a) Стоять на коленях из-за медленной работы на ровном месте
б) Может закончиться память (она там измеряется в килобайтах, а не в гигабайтах)

Знаете что такое "быстрые кучи/арены" на самом деле? Это тупо сразу же аллоцированный кусок в памяти, вне зависимости от того будут ли в него записаны какие-то данные полезные или нет, его просто у тебя сразу отнимают без вариантов. А вы знаете что работа с ним не бесплатна? У вас вокруг их работы построена целая обвязка, которая за всем этим следит, выравнивает память и т.д и т.п. всё это жрёт процессорное время, всё это жрёт память, всё это жрёт энергию.

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

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

Лично мне кажется, что так больше контроля на потоком данных и

Вам кажется. Больше контроля быть не может априори, ибо ровно ноль ограничений накладывает MobX.

соблюдается принципы low coupling и high cohesion

Хоспадя, что за новая мода пошла, увидели low coupling и high cohesion, теперь из каждого утюга о них поют. Жаль только с реальной жизнью всё это не контачит. Так что забудьте про эти глупости. Самое главное о чем стоить думать это KISS(Keep it simple stupid), а всё остальное второстепенно. Ну и принципы с MobX можно соблюдать какие душе угодно, всё по той же причине, отсутствие ограничений.

А вот с RxJS только write only once вырви глаз код, который поддерживать без слёз нереально можно соблюдать.

Ну вот как-то по субъективным ощущениям это работает лучше.

Это вам точно кажется)

1
23 ...

Information

Rating
5,337-th
Registered
Activity

Specialization

Frontend Developer
Lead
TypeScript
JavaScript
React
Node.js
MobX