Pull to refresh

Comments 104

Если использовать Observable вместо реактовских хуков, то может тогда посмотреть на Solid?

Проблема хуков переходом на Solid не решается. Нам нужны механизмы разделения бизнес-логики и рендеринга.

Ну так речь о том, что Solid — он как React, но в нём всё сделано через обсёрваблы. Вот на них всю логику пилить, а затем передавать в компоненты. Разница с реактом будет в том, что множества циклов рендеринга Virtual DOM не будет, а будут только его точечные изменения (поскольку VDOM в Solid тоже работает через обсёрваблы).

Я бы сказал так: Solid как React, но он не реакт. Кроме того, Solid использует сигналы, а писать на них, лично мне — не нравится.

Ну так бы и сказали сразу, что Solid просто не нравится :)

Мне не нравится атомы и сигналы как концепция.

Поддерживаю.
this.count++;
куда лучше и приятнее чем
setCount(c => c + 1);
Да и в целом, мутабильность куда приятнее, удобнее и быстрее чем иммутабильность.

а чем атомы от обсёрваблов отличаются?

а чем атомы от обсёрваблов отличаются?

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

Observable это тоже общее понятия, но его реализации могут быть принципиально разными, в одном случае это может быть MobX, в другом вот это:


С ручными подписками, отписками и испусканием событий. Вот и сравните их, и то и то как бы является Observable, но если сравнить их лоб в лоб, то это небо и земля в плане удобства использования в качестве управления состоянием.

В случае с MobX или kr-observable код остается максимально нативным, хотите увеличить счетчик? Пожалуйста this.counter++ или someState.counter++ . Хотите изменить значение флага? Пожалуйста this.opened = true или someState.opened = true .

Главное всё это можно делать из любого места и в любой момент.

Так это всё понятно. Вопрос же был не в отличии реализаций солида и альтернатив, а в отличии концепции атомов и обсёрваблов. Именно потому, что единой терминологии нет. Вот у Карловского минимальная реактивная сущность называетс атомом, здесь — обсёрваблом. Но что уважаемый автор в это вкладывает, заявляя, что атомы ему не нравятся?

а в отличии концепции атомов и обсёрваблов

Observer это реально существующий паттерн.
Atom - нет.

В этом принципиальная разница)

Всё это, в том числе так называемый "атом" это реализации Observer, разной степени паршивости.

Observer в свою очередь это просто самый банальный Publisher/Subscriber.

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

Иными словами, вообще не стоить придавать значения этим словам и терминам. Главное понимать суть. Реактивное(это когда мы любым образом реагируем на что-то, и любым образом провоцируем те самые реакции. Не надо вкладывать в это слово что-то эдакое сложное) состояние(у него 100500 реализаций) это всегда Publisher/Subscriber по принципу работы. Subscriber в случае с реактом это компонент(а точнее его forceUpdate функция), а Publisher это любое проявление уведомления Subscriber'a, о том, что ему надо выполнится. В случае стейт менеджмента это триггер forceUpdate функции компонента после изменения данных, которые в нем используются, например увеличения счетчика.

Но что уважаемый автор в это вкладывает, заявляя, что атомы ему не нравятся?

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

От классического паттерна Наблюдатель всё-таки у большинства реализаций атомов-обсёрваблов отличие в неявном механизме подписки, что как раз и является киллер-фичей подобных библиотек.

Нет такого понятия(в мире программирования) как атом, атомов-обсёрваблов и т.п. Это просто люди сами обозвали свои реализации атомами.
В свою очередь всё это фактически реализации Observer / Publisher-Subscriber, просто они решили не говорить pub/sub а решили сказать что это атом, с таким же успехом можно было сказать это белка, и потом кто-то бы спрашивал, а чем белка отличается от наблюдателя.

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

Касаемо MobX и kr-observable:
Это просто реализация паттерна Наблюдатель (Publisher/Subscriber) с использованием возможностей языка JS (getters/setters), которые и позволяют тот самый неявный механизм подписок и уведомления об изменениях. А не какие-то особенные атомов-обсёрваблов .

Ибо это одно и то же, просто детали реализации иные.

Вот об этом и был мой вопрос. Дело в том, что в Solid нет понятия атомов, а @nihil-proсказал, что атомы ему не нравятся. Но если это просто название конкретной реализации, то почему мы сравниваем реализацию с интерфейсом (паттерном) Observable. А если есть какое-то отличие в интерфейсе, то, опять-таки хорошо было бы сослаться на конкретную реализацию, потому что общего интерфейса или паттерна Atom нет.

Дело в том, что в Solid нет понятия атомов, а @nihil-proсказал, что атомы ему не нравятся

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

И того, в сухом остатке получается:
1) Реализация наблюдателя реализации наблюдателя рознь, и они могут быть прям как небо и земля.
2) Все сравнения в данном контексте на самом деле именно реализаций наблюдателя, даже когда люди их пытаются называть как отдельные вещи, аля атомы или сигналы, потому что по сути всё это и есть наблюдатель (mobx, redux, effector, signals и т.д. и т.п.).

то почему мы сравниваем реализацию с интерфейсом (паттерном)

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

Типо нет, это не чайный гриб, это камбуча)) Или нет, это не прыщ, это акнэ))

Атом (atom/cell/signal) имеет семантику изменяемой реактивной переменной инкапсулированной вместе с формулой пересчёта своего значения. Когда бы мы ни обратились к значению атома - он всегда выдаёт актуальное значение. Ну либо просит подождать, но это тоже актуально, так как данных у него может пока что и не быть. Если при вычислении значения одного атома, происходит обращение к другому, то они автоматически связываются как подписчик-издатель.

https://mol.hyoo.ru/#!section=docs/=h5rg2r_mmlg15

Pub/Sub тут не светится в публичном апи, является деталью реализации и в общем случае может вообще отсутствовать.

Pub/Sub тут не светится в публичном апи

А ни кто не говорит, что Pub/Sub в этом случае должен светиться в публичном АПИ, вся суть в другом, что итоговый принцип такой же. Есть подписчики, и есть те, кто уведомляет подписчиков.

import { signal, effect } from "@preact/signals-core";

const counter = signal(0);

// Subscriber
effect(() => console.log(counter.value));

counter.value++; // Publisher
counter.value = 10; // Publisher
const events = new EventEmitter();
let counter = 0;

// Subscriber
events.on('counter_change', () => console.log(counter))

counter++; 
events.emit('counter_change'); // Publisher
counter = 10;
events.emit('counter_change'); // Publisher

Как говорится найди 3 отличия.

Атом (atom/cell/signal) имеет семантику ... и дальше по тексту

Реализация наблюдателя / pub/sub с дополнениями, например в виде введения computed'ов, и спасибо если они хотя бы будут кэшируемые. Разумеется более хитрая, с проверкой на то, было ли реальное изменение или нет и т.д. и т.п.

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

Это называется Watch, никаких подписок между атомами тут нет.

Это называется Watch, никаких подписок между атомами тут нет.

Какая разница как это называется? Хоть белка. И при чем тут между "атомами" вообще? Если watch смотрит за изменениями, а значит пописан, а значит pub/sub.

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

Возможно такая, что это совсем другой паттерн.

Ага, это седан. Ни в коем случае это не автомобиль, это именно седан. Автомобиль это вообще другое.

А по вашей демагогии выходит, что любое чтение и запись в переменную - это паб-саб.

Нет, не любое. Если изменение переменной приводит к реакции на это, то это Pub/Sub в базе своей. Вы можете это завернуть в 10 оберток и обозвать белкой, но от этого истинная суть не меняется, есть те, кто подписан на изменения, а есть те, кто оповещают подписчиков об изменениях. Под каким соусом всё это подано, это уже детали реализации, но сам базовый принцип тот же.

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

А эффект реагирует лишь на изменение значения его атома, но никаких других.

Поздравляю, это Pub/Sub.

const events = new EventEmitter();

// "эффект"
events.on('event1', () => console.log('fire event1'))

// "атом" для "эффекта" event1
events.emit('event1'); // event1 регагирует

// "атом" для "эффекта" event2
events.emit('event2'); // event1 НЕ регагирует

Получается "эффект" event1 реагирует на "атом" event1, и не реагирует на "атом" event2 . Прямо как вы и описали.

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

А кто запретил подписаться на event1 только один раз?) Хочешь будет только один callback реагировать на even1, хочешь 10 callback'ов будет реагировать. Хозяин барин.

В solid сигналы

const [value] = createSignal(0)

Чтение value()

Он же observable под капотом и есть. Остальное — детали интерфейса и реализации.

Если использовать Observable, то может тогда посмотреть на Angular?

То есть это свой урезанный mobx? С тем же публичным API. Вопрос: зачем?

Где сравнение, чем это удобнее, например effector, mobx, redux, reatom. Хоть что-то…

Хотя бы про производительность бы. Ну хотя б одно преимущество…

Почему я должен взять и перейти на это даже с хуков? Потому что это красивее пишется?

Про что вообще статья?

А еще предлагаю порассуждать об инфраструктуре вокруг стейт-менеджеров. Сам стейт менеджер написать не сложно, п вот какие-нибудь удобные библиотеки для него уже сложнее! Например, роутер, форм library, fetching? Потому что стейт менеджер в реакт должен решать только одну проблему: разделение логики и ui на разные слои! И как только мы все это выносим в стейт менеджер, мы сразу начинаем думать о формах, роутере и остальном, что взаимодействует с этим стейт менеджером.

Поэтому, раз тут про удобство! Было бы удобно, если бы был стейт менеджер, которыц покрывает весь спектр задач, а не только кусочек!

Писать не сложно? Смотря что туда закладывается. Мемоизируемые компьютеды, автоподписки, порядок вызовов подписчиков, untrackable-values и продолжать можно очень долго.

Я, например, очень заморочился сделать так, чтобы Supercat Store проходил тест на реактивность от Карловского.

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

То есть это свой урезанный mobx? С тем же публичным API. Вопрос: зачем?

Вы себе тут противоречите. Если Observable предлагает тот же API, то почему на ваш взгляд он «урезанный»? Я с этим утверждением не согласен. Observable предлагает тот же API при существенно меньшем размере. Он немного производительнее и чуть чуть удобнее (не нужно писать runInAction).

Где сравнение, чем это удобнее, например effector, mobx, redux, reatom.

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

Хотя бы про производительность бы.

Есть ссылка в статье. Производительнее редакса, мобх и зустанд. По потреблению памяти еще и jotai с recoil.

Поэтому, раз тут про удобство! Было бы удобно, если бы был стейт менеджер, которыц покрывает весь спектр задач, а не только кусочек!

Например, роутер, форм library, fetching?

Можете привести пример такого, например на мобх? На мой взгляд, стейт менеджер не швейцарский нож, который как известно плохо делает все. Роутингом должен заниматься роутер, фетчингом fetch. Если приведете пример конкретной задачи, я с удовольствием отвечу.

Вероятно, подразумевается некая реактивная интеграция роутера со стейтом. Например, двунаправленная реактивность истории, реактивность самих роутов как объектов и т.п.

Да, именно это я имею ввиду! Никто еще не додумался.. эффектор вот там farfetched написали и atomic-router, которые прям привязаны к effector! Но это все энтузиасты, а хотелось бы от самих разрабов…

Чего именно хотелось бы? Сделать реактивным history? Это пару строк кода, для этого не нужна отдельная либа, и уж точно это не должно быть встроено в стейт-менеджер.

Если Observable предлагает тот же API, то почему на ваш взгляд он «урезанный»? Я с этим утверждением не согласен. Observable предлагает тот же API (как mobx)

Ну API mobx всё-таки больше. В Observable я не нашел даже computed.

Его не нужно искать.

get foo() { this.a + this.b } // computed

computed - это не просто геттер, а его оптимизация. Например, если я в одном экшене сделаю this.a++ и this.b--, то наблюдатели foo не должны триггериться, потому что они подписаны на foo, а не на this.a и this.b (как в случае обычного геттера)

Не совсем понял, можешь привести пример?

наблюдатели foo тригернутся если значение foo поменяется, а это произойдет если изменится this.a или this.b (или оба).

значение foo поменяется, а это произойдет если изменится this.a или this.b (или оба).

Разумеется, это не всегда так. Тривиальный пример:

import { Observable, autorun } from "kr-observable";

class Foo extends Observable {
  a = 1;
  b = 2;

  get less() {
    return this.a < this.b;
  }

  incB() {
    this.b++;
  }
}

const foo = new Foo();

autorun(() => {
  console.log("less ", foo.less);
});

setTimeout(() => {
  foo.incB();
}, 1000);

Здесь "less true" пишется два раза. А с компутедом (в мобх) - один.

Спасибо. Надо добавить такое поведение в observable.

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

В общем, сравниваться по размерам в килобайтах покамест рановато)

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

В 99.9% случаев автобатчинг(асинхронные реакции) предпочтительней. А для 0.1% опционально можно взводить флаг и реакция будет синхронной, я так и делаю и с MobX'ом (по умолчанию он у меня разумеется сконфигурирован на автобатчинг (асинхронные реакции)) используя это для компонента Input и Textarea, для них нужны именно синхронные реакции, иначе если курсор поставить не в конце текста, то его перебросит в конец во время ввода. У kr-observable это как раз и происходит, поэтому нужно добавлять возможность помечать свойство, чтобы на его изменение были синхронные реакции и/или взводить флаг и пока он в true, реакции на все изменения чтобы были синхронные. А так да, автобатчинг в 99.9% случаев более чем полезен и необходим.

это как раз и происходит

Спасибо. Не обращал внимание раньше. Починю.

Ну штош...

Решил разобраться в этом и немного не понимаю в чем соль, или что я упустил.

function bigWork() {
  console.log('a iam doing a big work')
}

class Mobx {
  a = 1;
  b = 2;

  constructor() {
    makeAutoObservable(this)
  }

  get less() {
    console.log('invoke less')
    bigWork()
    return this.a < this.b;
  }

  incB() {
    this.b++;
  }
}

const foo = new Mobx();

autorun(() => {
  console.log("less in autorun", foo.less);
});

let count = 0
setInterval(() => {
  if (count < 4) {
    foo.incB()
    ++count
  }
}, 2000)

// invoke less
// a iam doing a big work
// less in autorun true

// invoke less
// a iam doing a big work

// invoke less
// a iam doing a big work

// invoke less
// a iam doing a big work

// ...

Что мы тут имеем. less под капотом превращается в свойство а не геттер, его значение = значение гетера в изначальном состоянии.

Дальше мы подписываемся на изменения свойств который геттер читает, и при их изменении дергаем оригинальный гетер а результат присваиваем свойству less. Таким образом, мы получаем computed.

И вот тут я поломался, потому что не понимаю зачем. Оригинальный геттер все равно вызывается при изменении свойств которые он читает. Из этого следует, что если в нем будут сложные вычисления (bigWork) – мы их не избежим.
Предположим, что сложные вычисления зависят от значения computed

autorun(() => {
  if (foo.less) {
    bigWork()
  }
});

Что в таком случае:

В mobx autorun вызовется один раз и мы вызовем bigWork()

В observable autorun будет вызван несколько раз (на каждое изменение foo.b), но bigWork() будет вызван один раз, так как foo.less не изменился.

Вы итоге получаем, что и в mobx и в observable геттер будет вызываться на каждое изменение a или b. Autorun будет вызываться разное количество раз, но от этого ничего не меняется.

Я что-то упустил?

Решил разобраться в этом и немного не понимаю в чем соль, или что я упустил.

@Alexandroppolus имеет ввиду вот в чем соль:

kr-observable
https://stackblitz.com/edit/vitejs-vite-5hdb1d?file=src%2Fmain.ts&terminal=dev

mobx
https://stackblitz.com/edit/vitejs-vite-djgmcs?file=src%2Fmain.ts&terminal=dev


Вот так у вас
Вот так у вас
Вот так в MobX
Вот так в MobX

Т.е. autorun подписался именно на значение less и в MobX autorun срабатывает когда именно значение less изменяется. А у вас срабатывает когда изменяется любое свойство от которого зависит этот computed.

Все дело не в том, что геттер будет вызывать каждый раз когда изменяются его зависимости(так и должно быть), а в том, что он является computed'oм и реакции которые на него подписаны должны вызываться только тогда, когда значение этого самого compted изменяется.

Это все понятно, я, разумеется, сам запускал и одно и другое, но это не ответ.

Тут разница чисто визуальная. Mobx явно подписался на less, который в свою очередь неявно подписан на a и b.

Observable просто явно подписывается на a и b. И в одном и в другом случае при каждом изменении a и b вызывается оригинальный getter less и пересчитывается значение. Соотвественно, если в less будут тяжелые вычисления – они выполнятся и там и там.

Все дело не в том, что геттер будет вызывать каждый раз когда изменяются его зависимости(так и должно быть), а в том, что он является computed'oм и реакции которые на него подписаны должны вызываться только тогда, когда значение этого самого compted изменяется.

Хорошо хоть не в вордовском документе скриншот прислал.

С рабочего не разрешают загружать картинки

но bigWork() будет вызван один раз, так как foo.less не изменился.

это потому что бигВорк в данном конкретном случае должен вызываться только если less===true

А если бы он должен был отрабатывать при любом less, то вот тогда мы бы увидели разницу:

autorun(() => {
    bigWork(foo.less)
});

Боже, это великолепно.

Даже не знаю с чего начать, и как себя сдерживать. Но попробую:

Песочница

Будь добр использовать то же что и все, а не кидать ссылки на сомнительные ресурсы где даже импорты на моле.

Вычисление всего до авторанов:

Вычисления как раза идут после autorun. Что логично, если читать документацию.

The autorun function accepts one function that should run every time anything it observes changes. It also runs once when you create the autorun itself.

Посмотри логи: https://codesandbox.io/p/sandbox/mdm9cp

Автораны не работают

Автораны работают. Конечно, ты написал максимально странный, бессмысленный и запутанный код, чтобы никому и в голову не пришло разобраться, лишь бы хайпа словить. Это в твоем стиле, конечно, но по факту, два свойства которые действительно участвуют в этих вычислениях это App.A и App.B, которые изначально 0. Все остальное гетеры которые зависят от кучи запутанного лапшикода и A с B.

autorun(() => console.log("A-B", App.A, App.B));

Добавь, запусти, и получишь «неработающий» autorun.

Ты поэтому свою зовел, чтобы быть в ней главный? ))

Конечно, в твоей песочнице $mol в лидерах. Готов вернуться к обсуждению, когда у твоих решений будет хоть какие-то количество скачиваний в npm, и когда он будут бенчаться на авторитетных ресурсах, например тут: https://krausest.github.io/js-framework-benchmark/index.html

А пока это пустой треп. «Я в своей песочнице самый лучший!» — поздравляю. Отличное достижение.

А в лидерах там (сюрприз) не только и не столько $mol. Но раз уж он вас так заинтересовал, то с результатами других бенчмарков, можете ознакомиться тут.

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

  1. Оберните компоненты в hoc observer

  2. Добавьте нужным классам extends Observable и/или примените к объектам декоратор makeObservable

Надеюсь я вас убедил, что использовать Observable удобно 😉

Ушли от классов в react stateless и вернулись к классам обратно, через хук Observable, потому что классы удобней и читабельней.

Интересная штука. В mobx немного напрягает то что makeAutoObservable не работает с наследованием. Здесь, как я понимаю, такой проблемы нет. Но из минусов - класс обязательно должен наследоваться от Observable.

Наследование от Observable позволяет избежать проблем с наследованием вообще, и убирает необходимость переопределять свойства. Это делает observable чуть-чуть производительнее.

В обсуждениях в репозитории Mobx один из мейнтейнеров привёл код как можно сделать наследование. Я попробовал этот код на нескольких проектах, допилил чуть-чуть и выложил на NPM https://www.npmjs.com/package/mobx-store-inheritance

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

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

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

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

но у него достаточно огромный минус - это размер бандла по сравнению с аналогами

Серьезно?)) Огромный минус?)) Я разочарован. Напомните ка сколько там весит react +r eact-dom?) 1kb? Ага) Этот "минус" невероятно притянут за уши, в масштабах проекта. его размер вообще ничего не значит. Тем более благодаря ему, самого по себе кода надо писать гораздо меньше, а это тоже дает экономию в размере тоже, по факту этот "минус" не актуален.

С таким же успехом можно сказать что JS это один сплошной минус, по сравнению с ассемблером или CИ, он намного тяжелее, медленнее, жрет намного больше памяти и т.д. и т.п., давайте все дружно теперь писать на ассемблере, ведь это так удобно и классно))

Что за пассивная агрессия. Это факт, посмотри сколько весит redux, zustand, effector и прочие стейт менеджеры, и сравни их размеры, с размером MobX, он будет существенно больше, а это БОЛЬШОЙ минус, который перекрывают сплошные плюсы при работе с MobX

Если для тебя разница в 30-40кб не является минусом, тем более в большом проекте, то видимо у тебя недостаточно опыта рассуждать на эту тему

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

Это факт, посмотри сколько весит redux, zustand, effector и прочие стейт менеджеры

Какая разница сколько весят этот "прекрасные инструменты"? Если они всё равно не пригодны для адекватного использования.

Ещё раз, посмотрите сколько весит React + React DOM, с вашей "логикой" вы вообще вошли не в ту дверь :D
Во первых реакт весит намного больше чем vanila js, peact, и т.д. и т.п. Во вторых браузер это вообще монстр которой выжирает оперативную память и процессорное время на раз два. В третьих JS намного больше ресурсов потребляет в отличии от СИ. Ну и так далее по списку.

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

а это БОЛЬШОЙ минус, который перекрывают сплошные плюсы при работе с MobX

Так смешно конечно))) Перекрывает говорит))) Двигатель в машине кстати знаете сколько весит? Архренеть сколько, ну его нафиг, давайте выкинем. А КПП? Тоже фигня какая, весит много - ОГРОМНЫЙ минус, тоже в топку. Теперь машина весит на пару сотен кг легче, правда больше не ездит, но это не важно, главное мы избавились от ОГРОМНОГО минуса в виде пары килограмм. Вот посмотрите на велосипеды, там вообще вся система весит от силы 1-2 кг и там 24 скорости и двигатель не нужен, педали крути и вперед. Вот идиоты в машины ставят какие-то ДВС тяжелые, КПП какие-то, ещё и автоматические, так они ещё тяжелее, да ещё и кпд у них похуже будет, капец просто, во дают. Более того, они на столько упоротые что ещё какую-то жижу туда заливают, бензин кажется называется, полный бак заливают и ещё +50кг в весу, как там Задорнов то говорил всегда?)

Если для тебя разница в 30-40кб не является минусом, тем более в большом проекте, то видимо у тебя недостаточно опыта рассуждать на эту тему

Ахахха, а да?) Ну раз такие критерии, то да, более того у меня ещё в зависимостях react+react-dom которые весят как 3-4 Mobx'a, так что я вообще полный ноль.

Всегда забавно наблюдать, как дилетанты худо-будет освоившие react, mobx и больше ничего, спорят кто из них больший профессионал.

Как на mobx решается такая типовая задача: ленивая фабрика, создающая объект по ключу при появлении первого подписчика, и вызывающая деструктор при потере последнего с автоматическим освобождением всех выделенных на этот объект ресурсов?

Можно небольшой обезжиренный пример использования? Не совсем понятно, какое API требуется и кто на кого подписывается.

class App extends Object {

    @mem uplink( next = '/foo' ) { return next }
    @mem logging( next = true ) { return next }

    @mem root() {
        // hold a logger alive while the logging is enabled
        if( this.logging() ) this.logger() 
        this.socket().send( 'Hello World' )
    }

    // lazy Socket factory
    @mem socket() {
        // should reuse an existing connection for the same uplink
        return JsonSocket.connection( this.uplink() )
    } // should close an unused connection

    // lazy Listener factory
    @mem logger() {
        // should reuse an existing logger for the same socket
        return this.socket().listening( console.log )
    } // should unsubscribe an unused listener

}

class JsonSocket extends Object {

    // lazy Socket registry
    @mems static connection<
        This extends typeof JsonSocket
    >( this: This, uri: string ) {
        return new this( uri )
    }

    @act send( message: any ) {
        this.socket.send( JSON.stringify( message ) )
    }

    // lazy Listener registry
    @mems listening( handle: ( message: any )=> void ) {
        return new Listener(
            this.socket,
            'message',
            ( event: MessageEvent )=> JSON.parse( event.data ),
        )
    }

    readonly socket: WebSocket

    constructor(
        readonly uri: string,
    ) {
        super()
        this.socket = new WebSocket( uri )
    }

    destructor() {
        this.socket.close()
    }

}

class Listener< NarrowEvent > extends Object {

    constructor(
        readonly host: EventTarget,
        readonly type: string,
        readonly task: ( event: NarrowEvent )=> void
    ) {
        super()
        this.host.addEventListener(
            this.type,
            this.task as ( ( event: Event )=> void ),
        )
    }

    destructor() {
        this.host.removeEventListener(
            this.type,
            this.task as ( ( event: Event )=> void ),
        )
    }

}

Эх, жаль на mobx ничего такого сделать не получится..

На Supercat Store без проблем. Там у атомов есть методы, которые навешивают листенеры на события onHasSubscribers и onNoSubscribers.

В JS нет деструкторов, а то, что можно реализовать самостоятельно в качестве деструктора не гарантирует что GC пройдется и очистит.

У мобх есть onBecomeObserved и onBecomeUnobserved для таких задач.

Ну то есть лаконичный код на mobx вы нам не покажете?

Разговор глухого с немым. Не вижу в твоем коде ни деструктора, ни высвобождения ресурсов.

у тебя какая-то профильная специальность определять дилетантов? Первый раз решил написать комментарий на хабре, что бы поддержать автора опен-сурс стейт менеджера, налетело просто два супер-токсичных душных аля "Я ВСЬО ЗНАЮ, Я ГЭНИЙ" и сидят что-то доказывают, хотя мне честно говоря абсолютно плевать на мнение, у вас его никто не спрашивал. Иди зачилься, чаю налей, девочку вызови, или как ты там обычно отдыхаешь

Давайте я лучше вас вызову, чтобы вы и меня поддержали, а не называли мой опен-сурс стейт менеджер вонючим.

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

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

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

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

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

🤦

сидит сравнивает стейт менеджер, с библиотекой для пользовательских интерфейсов веб-платформ, программ и приложений

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

( можешь не отвечать, бред этот читать не намерен, тратить своё время )

Аккуратно с текстами, а то целых гигантских 30kb траффика загрузите, это же огромный минус, который сводит на нет использование интернета как такового. Вот ещё бы вы килобайты не гоняли по сети да? И лишний стакан воды смотрите не выпейте, а то временно обретете плюс 300 грамм огромной массы, это тоже сводит на нет все плюсы от питья воды. Куда не посмотри, везде сплошные огромные минусы.

( можешь не отвечать, бред этот читать не намерен, тратить своё время )

Это правильно, не стоит 30kb трафика гнать через своего провайдера, это не позволительно.

Mobx в gzip 17 + 2 = 19кб, не 30-40:
- https://bundlephobia.com/package/mobx@6.13.5
- https://bundlephobia.com/package/mobx-react-lite@4.0.7

Там же посмотрел Effector 11 + 3 = 14кб.
- https://bundlephobia.com/package/effector
- https://bundlephobia.com/package/effector-react

То есть конкретно тут разница в размере незначительная. Попытка сэкономить на размере понятна, просто когда в проекте есть аналитика, библиотеки для графиков, анимаций, онлайн-чаты, скрипты для оплаты, то странно что возникает желание чуть-чуть сэкономить на стейт-менеджере, который является ядром приложения. Конечно если бы мне нужно было разрабатывать встраиваемый виджет или проект для страны с плохим интернетом и кнопочными телефонами, то тогда был бы смысл пересмотреть стейт-менеджер. Однако ещё больше смысла в этом случае - сменить реакт на исчезающий фреймворк вроде Solid (реакт весит ~43кб)

Замечание достаточно дельное, и согласен со словами которые вы написали.

Я просто давненько смотрел его размеры, и написал то что помнил, но даже с 30-40 я готов был жертвовать этими сайзами, потому что для меня Mobx ускоряет, упрощает, и делает более читаемым на 90%, а для меня и моей команды это важно.

Насчёт реакта, я согласен на 100%, и то что SolidJS это реакт здорового человека тоже факт, который невозможно оспорить никак.

Я очень надеюсь что он сможет стрельнуть как реакт, и такие фреймворки как NextJS и другие будут его поддерживать, потому что NextJS (pages роуты который) для меня тоже достаточно удобный, и имеет много вещей, которые мне не надо с нуля каждый раз делать

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

Очень надеюсь что Солид ещё покажет себя, и сможет встать на уровень с реактом по кол-ву скачиваний)

Увидел что вы являетесь одним из разработчиков Mobx, могу только сказать СПАСИБО за то что сэкономили 90% моих нервов, и времени <3

Честно говоря, не понимаю моду мерить размер бандла в gzip. Видимо, чтобы казалось, что библиотека весит меньше. Тогда можно ещё указывать степень сжатия. Так-то mobx весит 64kb. И этот показатель не только важен для оценки трафика, но вообще сколько кода js выполняется. На Хабре не раз подмечали прямую зависимость тормозов от объема выполняемого кода.

Честно говоря, не понимаю моду мерить размер бандла в gzip

Это не мода. Это фактическое значение. Ибо у 99.99% сайтов, я имею ввиду адекватных, а не hello world поделок школьников включен gzip.
Т.е. специально не включать gzip === максимальная степень глупости.
Вот например vite.dev

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

Тогда можно ещё указывать степень сжатия

Тоже нет смысла. Ибо между самой распространённой средней и самой максимальной разница ничтожная. Я лично проверял.

 но вообще сколько кода js выполняется. На Хабре не раз подмечали прямую зависимость тормозов от объема выполняемого кода.

А вот и не правда. "Тормоза" зависят не от кол-ва кода, а он того, какой собственно код выполняется и какую работу он делает. Если ваш JS файл весит 40mb, а из них только выполняется 2 функции, то если вынести это 2 функции в отдельный файл, который вест 500 байт, то результат в скорости будет тот же. Не считая скорости загрузки самого файла.

С такой логикой игры вообще не должны запускаться, они весят по 100 с лишни гигабайт) И кода там не на пару килобайт так то)) IDE не должны тоже работать, там тоже размер огогошечки. Однако все прекрасно работает.

Ибо не существует прямой зависимости скорости работы от объема кода как вы говорите.

А теперь внимание шок контент:
Habr:

У habr.com 5mb JS'a. А у vite.dev 560kb

Получается habr должен работать в 9 раз медленнее, чем vite.dev. Отклик на все нажатия и т.п. в 9 раз медленнее. Ведь как вы говорите, зависимость прямая.

Однако этого не происходят, даже близко.
А как же это так?)) Это другое?))

Это не мода. Это фактическое значение. Ибо у 99.99% сайтов, я имею ввиду адекватных, а не hello world поделок школьников включен gzip.Т.е. специально не включать gzip === максимальная степень глупости.Вот например vite.dev

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

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

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

Использование такого рода производных оценок (размер скрипта в архиве, а не сам размер скрипта) искажают картину анализа в общем и целом.

По поводу остального отвечу так: большие файлы грузятся дольше, парсятся дольше. Вполне вероятно и оперативки сожрут больше. Мне нет смысла спорить с Вами или кем-либо, чтобы вывести полную формулу фразы со всеми условиями, когда она справедлива. Я больше о том, что если библиотека весит 64 кб, то зачем говорить, что она весит 17 кб. Цель какова? Просто занизить показатели, чтобы выставить библиотеку в более лучшем свете?

Время загрузки зависит от размера сжатого бандла. Время парсинга в большей степени зависит от числа токенов, чем от числа байт. И оно много меньше времени загрузки. Время инициализации в большей степени зависит от архитектуры и оптимальности кода, чем от его размера. Когда сравнивают размеры бандлов - речь обычно именно о времени загрузки. Для примера, можете поперезагружать портал mol.hyoo.ru - там бандл под 2 метра, который на 3G грузится целых 10 секунд, но единожды прогрузившись, далее показывается он мгновенно.

Я больше о том, что если библиотека весит 64 кб, то зачем говорить, что она весит 17 кб Цель какова?

Потому что для пользователя, с точки зрения траффика она будет 17кб, а не 64кб. И уже много лет принято говорить gzip размер когда речь идет про js бандлы, ибо в сыром виде их все равно никто и никогда по сети не гонят.


В сыром виде реакт весит 130кб, получается он не пригоден для использования?))) А preact то 3kb gzip, почему на него все не перешли?)) Потому что размер библиотеки играет крайне посредственное значение. У нас тут 5G все дела давно. Эпоха dial-up закончилась в нулевых. Намного важнее ряд других критериев, размер может быть от силы приятным бонусом(иначе react никто и никогда бы не использовал). Просто нелепо и смешно, когда называют в минусах размер стейт менеджера, я бы понял если он реально много весил, а тут 17кб (64кб в сыром виде), это вообще нет ни что.

Просто посмотрите сколько весит 1 картинка которая у вас загружается на сайте. Например ужатая и в разрешении 640*480 весит в среднем 30kb и это в gzip, в сыром виде в среднем они в 2 раза тяжелее.

Т.е. 1 вшивая маленькая пережатая картинка весит как 2 mobx'a.

Масштаб нелепости понимаете?)

А вот я просто открыл объявление на авито:

Почти 4mb в gzip'e, 15mb в сыром виде. А вы тут рассуждаете про десяток килобайт, это смешно)

3.7mb / 0.017mb(17kb) = 217
1/217 часть карл)

Это 0.46% от того, что мы загрузили открыв страницу. А про то, что после загрузки бандла он положился в кэш и больше не будет загружаться пока он не изменится я вообще молчу.

А теперь представьте, какая разница, основа приложения(стейт менеджер) будет весить 1кб или 17кб, есть какая-то разница? Если тот, что 1кб по всем фронтам лучше и удобнее, то да, лучше его, а если нет, то конечно лучше тот что в 17 раз больше весит. Ибо по факту разницы для пользователя не будет.

Для своей функциональности mobx многовато весит. Его можно было бы оптимизировать раза так в два минимум, даже с добавленной недостающей функциональностью.

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

5G, он, конечно, "везде", но я вот буквально сейчас столкнулся с такой проблемой, что сеть постоянно обрывалась и пришлось переключиться на 2G, чтобы был хоть какой-то интернет. По понятным причинам Авито я бы даже не пытался открывать.

А ещё был случай, когда мне нужно было открыть мобильную(!) версию т-банка, чтобы повысить маме лимит по карте. Попробуйте догадаться, сколько минут она стояла на кассе, задерживая очередь.

Вы как-то пытаетесь вывернуть все через призму критики стейт-менеджера. А я его и не критикую вовсе.

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

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

Sign up to leave a comment.

Articles