替换可观察数据时的MobX性能

Dav*_*ing 11 javascript reactjs mobx

当我从套接字获取新转储时,我需要替换我的observable对象中的数据:

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
Run Code Online (Sandbox Code Playgroud)

但是,我注意到数据对象扩展时的性能命中,可能是因为即使某些属性/值相同,MobX也会在任何地方触发反应.

有一种"更聪明"的方式吗? - 我在想f.ex只替换对象的受影响部分会比替换整个observable更好吗?

我在这里做了一个小演示,解释了我的意思:https://jsfiddle.net/yqqxokme/.即使数据完全相同(预期),更换对象也会引起新的反应.但我确信有一种方法可以像在merge()函数中一样改变数据对象的受影响部分.

Tar*_*ani 5

所以这里有一些事情和案例.我已将转储功能更改为以下模拟更改

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])
}
Run Code Online (Sandbox Code Playgroud)

使用extendObservable

现在,如果你使用下面的代码

replaceFromDump(newData) {
  extendObservable(this.data, newData)
}
Run Code Online (Sandbox Code Playgroud)

并通过转储循环运行它,输出如下

输出1

事件bar将在您获得更改之前不会开始提升foo,这发生在以下更改

{foo: 'barnew', bar: {name: "zoo2", new: "yes"} },
Run Code Online (Sandbox Code Playgroud)

结果:新密钥只能观察现有的可观察密钥更改

使用地图

在这里我们改变代码如下

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

replaceFromDump(newData) {
  this.data.merge(newData)
}
Run Code Online (Sandbox Code Playgroud)

输出2

结果:数据仅合并,不会删除.您还将获得重复事件,因为它是仅合并选项

使用对象差异

您可以使用如下所示的对象差异库

https://github.com/flitbit/diff

您可以更新如下代码

  @observable data = {
    foo: 'bar'
  }

replaceFromDump(newData) {
    if (diff(mobx.toJSON(this.data), newData)){
        this.data = newData;
    } 
}
Run Code Online (Sandbox Code Playgroud)

输出3

结果:事件仅在数据更改时发生,而不是在重新分配到同一对象时发生

使用Diff和应用Diff

使用我们之前使用的相同库,我们可以只应用所需的更改

如果我们改变代码如下

replaceFromDump(newData) {
    observableDiff(toJSON(this.data), newData, d => {
          applyChange(this.data, newData, d);
    })
  } 
Run Code Online (Sandbox Code Playgroud)

如果运行上面的命令,我们得到以下输出

输出4

结果:仅观察到对初始密钥集的更改,请不要删除其间的密钥

它还为您提供以下格式的差异

{"kind":"E","path":["foo"],"lhs":"bar2","rhs":"barnew"}
{"kind":"N","path":["bar","new"],"rhs":"yes"}
Run Code Online (Sandbox Code Playgroud)

这意味着您可以根据需要更好地控制字段名称

下面是我使用的小提琴,大多数代码都有评论,但是如果你需要查看下面的导入使用

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


Joe*_*kes 1

您可以阅读有关优化组件的信息:https ://mobx.js.org/best/react-performance.html

默认情况下,Mobx 仅触发在渲染函数中使用状态的组件。因此,并非所有组件都会被触发渲染。

React 渲染所有使用已更改的 props 的子组件。

也就是说,更改的状态越多,需要重新渲染的次数就越多。因此,我建议仅同步更改并使用@action装饰器来确保渲染仅完成一次,而不是在每次对状态进行更改时进行渲染。

@observable data = {}

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

Mobx 不会检查更改后的状态是否实际相同。因此,即使更改同一对象的状态也可能会触发重新渲染。(https://mobx.js.org/best/react.html

是的,正如您所说:您还可以仅针对已更改的属性在旧状态上深度合并/覆盖新状态。这也会触发更少的重新渲染。

如果您正确编写代码(例如:不要在 React 渲染方法中使用 Labmda 语句),您的代码应该非常有效地重新渲染。