Производительность MobX при замене наблюдаемых данных - PullRequest
0 голосов
/ 24 апреля 2018

Мне нужно заменить данные в моем наблюдаемом объекте, когда я получаю новый дамп из сокета:

class Store {
    @observable data = { foo: 'bar' }
    replaceFromDump(newData) {
        this.data = newData
    }
}
const store = new Store()
store.replaceFromDump({ foo: 'bar' })

// { foo: 'bar' } can be a huge amount of JSON

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

Есть ли "умнее" способ?- Я думаю, что f.ex только замена затронутых частей объекта будет лучше, чем замена всей наблюдаемой?

Я сделал небольшую демонстрацию, объясняющую, что я имею в виду: https://jsfiddle.net/yqqxokme/. Замена объекта вызывает новые реакции, даже если данные точно такие же (ожидаемые).Но я уверен, что есть способ изменять только затронутые части объекта данных, как в функции merge().

Ответы [ 4 ]

0 голосов
/ 26 апреля 2018

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

variations = [
  {foo: 'bar'},
  {foo: 'bar'},
  {foo: 'bar2' },
  {foo: 'bar2' },
  {foo: 'bar2', bar: {name: "zoo"} },
  {foo: 'bar2', bar: {name: "zoo"} },
  {foo: 'bar2', bar: {name: "zoo2"} },
  {foo: 'bar2', bar: {name: "zoo2"} },
  {foo: 'barnew', bar: {name: "zoo2", new: "yes"} },
  {foo: 'barnew', bar: {name: "zoo2", new: "no"} },
  {foo: 'barnew', bar: {name: "zoo2", new: "no"} }
]

i=0;

dump = () => {
  i++;
  i = i%variations.length;
  console.log("Changing data to ", variations[i]);
    store.replaceFromDump(variations[i])
}

Использование exteObservable

Теперь, если вы используете код ниже

replaceFromDump(newData) {
  extendObservable(this.data, newData)
}

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

Output 1

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

{foo: 'barnew', bar: {name: "zoo2", new: "yes"} },

Результат: новые ключи могут наблюдаться только при изменении существующих наблюдаемых ключей

Использование карты

В этом мы меняем код как показано ниже

  @observable data = map({
    foo: 'bar'
  })

replaceFromDump(newData) {
  this.data.merge(newData)
}

Output 2

Результат: данные только для слияния и не будут удалены. Вы также получите повторяющиеся события, так как это опция только слияния

Использование объекта Diff

Вы можете использовать библиотеку объектов diff, как показано ниже

https://github.com/flitbit/diff

Вы можете обновить код, как показано ниже

  @observable data = {
    foo: 'bar'
  }

replaceFromDump(newData) {
    if (diff(mobx.toJSON(this.data), newData)){
        this.data = newData;
    } 
}

Output 3

Результат: события происходят только при изменении данных, а не при переназначении одному и тому же объекту

Использование различий и применение различий

Используя ту же библиотеку, которую мы использовали ранее, мы можем применить только необходимые изменения

Если мы изменим код, как показано ниже

replaceFromDump(newData) {
    observableDiff(toJSON(this.data), newData, d => {
          applyChange(this.data, newData, d);
    })
  } 

Если запустить вышеуказанное, мы получим следующий вывод

Output 4

Результат: наблюдаются только изменения в исходном наборе ключей, если вы не удаляете их в ключах между

Это также дает вам разницу в формате ниже

{"kind":"E","path":["foo"],"lhs":"bar2","rhs":"barnew"}
{"kind":"N","path":["bar","new"],"rhs":"yes"}

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

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

https://jsfiddle.net/tarunlalwani/fztkezab/1/

0 голосов
/ 26 апреля 2018

Вы всегда создаете новый объект в этой строке и передаете его функции

store.replaceFromDump({ foo: 'bar' })

Два объекта даже с одинаковыми парами ключ / значение не будут возвращать true из этогосвоего рода оператор if

if( object1 === object2 )

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

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

  replaceFromDump(newData) {
    if( JSON.stringify( this.data ) !== JSON.stringify( newData ) ){
        this.data = newData
    }
  }

Я бы тогда использовал React.PureComponent для ваших классов, в которых вы делаете рендеринг, в которых вы должны избавиться от лишних / нецелевых рендеров.Так что просто расширяйте ваши классы из них

class IRenderMobX extends React.PureComponent

React.PureComponent аналогичен React.Component.Разница между ними заключается в том, что React.Component не реализует shouldComponentUpdate (), но React.PureComponent реализует его с поверхностным сравнением и состоянием.

Если функция render () вашего компонента React отдает тот же результат, что итот же реквизит и штат, вы можете использовать React.PureComponent для повышения производительности в некоторых случаях.

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

0 голосов
/ 26 апреля 2018

Использование exteObservable предотвратит запуск реакций, если значения идентичны:

class Store {
    @observable data = { foo: 'bar' }
    replaceFromDump(newData) {
        extendObservable(this.data, newData)
    }
}
const store = new Store()
store.replaceFromDump({ foo: 'bar' })
0 голосов
/ 24 апреля 2018

Вы можете прочитать об оптимизации ваших компонентов: https://mobx.js.org/best/react-performance.html

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

React рендерит все дочерние компоненты, которые используют измененные реквизиты.

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

@observable data = {}

@action
replaceChanges(partialNewData) {
    Object.keys(partialNewData).forEach(propName => {
       this.data[propName] = partialNewData[propname];
   }
}

Mobx не проверяет, является ли измененное состояние актуальнымтот же самый.таким образом, даже изменение состояния с одним и тем же объектом может вызвать повторную визуализацию.(https://mobx.js.org/best/react.html)

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

Если вы пишете свой код правильно (например, не используйте операторы labmda в вашем методе рендеринга реагирования), ваш код должен перерисовываться довольно эффективно.

...