**注意:**我会写多篇文章来比较说明redux和mobx的不一样,redux和mobx各有优缺点, 若是对React/Mobx/Redux都理解够深入,我我的推荐Mobx(逃跑。。。)javascript
React社区的大方向是immutable, 不论是用immutable.js 仍是函数式编程使用不可变数据结构。为何React须要不可变数据结构呢? 考虑下面的一个应用html
class Root extends Component {
state = {
something: 'sh'
}
render() {
return (
<div> <div onClick={e => { // onClick setState 空对象 this.setState({}) }}>click me!!</div> <L1/> <Dog sh={this.state.something}/> </div> ) } } ... class L1 extends Component { render() { console.log('invoke L1') return ( <div> <L11/> <L12/> </div> ) } } ... class L122 extends Component { render() { console.log('invoke L122') return ( <div>L122</div> ) } } 复制代码
当我点击 Root上的 click me 的时候, 执行了this.setState({})
,因而触发Root更新, 这个时候L1, Dog会怎么样呢? 结论是当点击的时候 控制台会打印:java
invoke L1
invoke L11
invoke L111
invoke L112
invoke L12
invoke L121
invoke L122
invoke Dog
复制代码
当一个组件须要跟新的时候,react并不知道哪里会更新,在内部react会用object(存js对象)来表明dom结构, 当有更新的时候 react暴力比较先后object的差别,增量的处理更新的dom部分。 对于刚才的这个例子, react暴力计算的结果就是没有增量。。。虽然react暴力比较算法已经很是高效了,这些无心义的计算也应该避免, 起码能够节省计算机的电 --> 少用煤 --> 减小二氧化碳排放 --> 保护地球。 毕竟 蝴蝶效应!react
ui = f(d) 相同的d获得相同的ui(设计组件的时候最好这样)。例如咱们上例的Dog,咱们能够直接比较shgit
class Dog extends Component {
shouldComponentUpdate(nextProps) {
return this.props.sh !== nextProps.sh
}
...
}
复制代码
更加通常的状况, 咱们怎么肯定组件的props和state没有变化呢? 不可变对象 ! 若是对象是不可变的, 那么当对象a !== a' 就表明这是2个对象,不相等。而在传统可变的对象中 须要deepEqual(a, a')。 若是咱们的React应用里面 props和state都是不可变对象, 那么:github
class X extends Component {
shouldComponentUpdate(nextProps, nextState) {
return !( shallowEqual(this.props, nextProps) && shallowEqual(this.state, nextState))
}
}
复制代码
react也考虑到了一点 提供了PureComponent
帮助咱们默认作了这个shouldComponentUpdate
web
把 L1, Dog, L11 ... L122改成PureComponent
, 再次点击,打印:算法
// 没有输出。。。
复制代码
拯救了地球!编程
redux 每次action发生的时候,都会返回一个全新的state,�天生是immutable。 Redux + PureComponent 轻松开发出高效web应用redux
Mobx恰好相反,它依赖反作用(so 全部组件不在继承PureComponent), 那它是怎么工做的呢?
mobx-react的 @observer经过收集组件 render函数依赖的状态, 当状态有修改的时候精确的控制组件的更新。
好比如今 Root组件依赖状态 title
, L122 依赖状态x
(Root传递x给L1,L1传递给L12, L12传递给L122)。 那么应该:
const store = observable({
x: 'x'
title: 'title',
})
window.store = store
@observer
export default class MobxRoot extends Component {
render() {
console.log('invoke MobxRoot')
const { title, x } = store
return (
<div>
<div>{title}</div>
<L1 x={x}/>
<Dog/>
</div>
)
}
}
class L1 extends Component {
render() {
console.log('invoke L1')
return (
<div>
<L11/>
<L12 x={this.props.x}/>
</div>
)
}
}
class L12 extends Component {
render() {
console.log('invoke L12')
return (
<div>
<L121/>
<L122 x={this.props.x}/>
</div>
)
}
}
@observer
class L122 extends Component {
render() {
console.log('invoke L122')
return (
<div>
{ this.props.x || 'L122'}
</div>
)
}
}
复制代码
这样当title变化的时候, Mobx发现只有MobxRoot组件关心title,因而更新MobxRoot, 当x变化的时候 Mobx发现有MobxRoot, L122 依赖与x,因而更新MobxRoot,L122 。 工做很正常。
细想当title变化的时候,更新MobxRoot,因为更新了MobxRoot进而致使L1,Dog的递归暴力diff计算,显而易见的是无心义的计算。 当x变化的时候呢, 因为MobxRoot,L122依赖了x, 会先更新MobxRoot,而后更新L122,然而在更新MobxRoot的时候又会递归的更新到L122, 这里更加麻烦了(实际上React不会更新两次L122)。
Mobx也在文档里指出了这个问题(晚一点使用间接引用值), 对应的解决方法是 L1 先传递store。。。最后在L122里面从store里面获取x。
这里暴露了两个问题:
PureComponent
优化记住在mobx应用里, 应该把组件是否更新的绝对权彻底交给Mobx,彻底交给Mobx,彻底交给Mobx。 即便是父组件也不该该引发子组件的跟新。 因此全部的组件(没有被@observer修饰)
都应该继承与PureComponent(这里的PureComponent的做用已经不是原来的了, 这里的做用是阻止更新行为的传递)。 另一点, 因为组件是否更新取决与Mobx, 组件更新的数据又取值与Mobx,因此还有必要props传递吗? 基于这两点代码:
const store = observable({
x: 'x'
title: 'title',
})
window.store = store
@observer
export default class MobxRoot extends Component {
render() {
console.log('invoke MobxRoot')
const { title} = store
return (
<div> <div>{title}</div> <L1/> <Dog/> </div>
)
}
}
class L1 extends PureComponent {
render() {
console.log('invoke L1')
return (
<div> <L11/> <L12/> </div>
)
}
}
class L12 extends PureComponent {
render() {
console.log('invoke L12')
return (
<div> <L121/> <L122/> </div>
)
}
}
@observer
class L122 extends Component {
render() {
console.log('invoke L122')
const x = window.store // 直接从Mobx获取
return (
<div> { x || 'L122'} </div>
)
}
}
复制代码
这样当title改变的时候, 只有MobxRoot会跟新, 当x改变的时候只有L122 会更新。 如今咱们能够把应用里面的全部组件分为两类: 关注状态的@observer组件, 其余PureComponent组件。这样每当有状态改变的时候, Mobx精确控制须要更新的@observer组件(最小的更新集合),其余PureComponent阻止无心义的更新。 问题的关键是开发者必定要搞清楚 哪些组件须要 @observer。 这个问题先放一下, 咱们在看一个mobx的问题
假设L122复用了一个第三方库提供的组件(代表咱们不能修改这个组件)
@observer
class L122 extends Component {
render() {
console.log('invoke L122')
const x = window.store // 直接从Mobx获取
return (
<div> <BigComponent x={x}/> </div> ) } } 复制代码
组件 BigComponent 正如其名 是一个很‘大’的组件,他接收一个props对象 x,x结构以下:
x = {
name: 'n'
addr: '',
}
复制代码
此时当咱们执行: window.store.x.name = 'fcdcd'
的时候, 咱们期待的是BigComponent按照咱们的意愿,根据改变后的x从新渲染, 其实不会。 由于在这里没有任何组件 依赖name, 为了让L122 正常工做, 咱们必须:
@observer
class L122 extends Component {
render() {
console.log('invoke L122')
const x = window.store.x
const nx = {
name: x.name,
addr: x.addr
}
return (
<div> <BigComponent x={nx}/> </div> ) } } 复制代码
若是不明白mobx的原理, 可能会很疑惑,疑惑这里为何要这么写, 疑惑哪里为啥不更新, 疑惑哪里为啥莫名其妙更新了。。。
什么组件须要@observer? 当一个render方法里,出现咱们不能控制的组件(包括原生标签, 第三方库组件)依赖于状态的时候, 咱们应该使用@observer, 其余组件应该继承PureComponent。 这样咱们的应用在状态发送改变的时候,更新的集合最小,性能最高。
除此以外,Mobx还有一个性能隐患,但愿mobx的拥护者可以清楚的认知到,假设如今 L122 不只也依赖title, 还依赖状态a, b, c, d, e, f, g, h:
class L122 extends Component {
render() {
console.log('invoke L122')
const { title, a, b, c, d, e, f, g, h } = window.store
return (
<div> <span>{title}</span> <span>{a}</span> <span>{b}</span> ... <span>{h}</span> </div>
)
}
}
function changeValue() {
window.store.title = 't'
window.store.a = 'a1'
window.store.b = 'b1'
window.store.c = 'c1'
}
复制代码
当执行 changeValue()
的时候 会发生什么呢?控制台会打印:
invoke MobxRoot
invoke L122
invoke L122
invoke L122
invoke L122
复制代码
一身冷汗!!得好好想一想这里的数据层设计, 是否把这几个属性组成一个对象,状态愈来愈复杂的时候可能不是那么简单。
redux与第三方库结合没有好说的,工做的很好。 不少库如今已经假定了 传人的状态是 不可变的。
mobx正如前文所说 不论是发布为第三方库, 仍是使用第三方库
这里咱们只说 immutable的开发效率,mutable的开发效率应该是最低的。 0. 结合对象展开浮, js裸写。 也不难
若是你能无痛的处理immutable, 那么Redux + PureComponent 很方便写出高性能的应用。
若是你对Mobx掌握的足够好, 那么Mobx绝对会迅速的提升开发效率。
本文代码github地址