Open JNUfeatherwit opened 5 years ago
先来看下在class里两种定义observable对象的方式:
myObservableObject = observable({a:'1'})
@observable myObservableObject = {a:'1'}
这两种有什么区别呢?我们尝试一下改变myObservableObject的a属性
myObservableObject.a='2'
运行之后结果是两种定义方式都会触发render 再来,我们这次改变一下myObservableObject引用的对象
myObservableObject={b:'1'}
结果是什么样的呢?只有第二种写法,即用了装饰符定义的对象,会触发render,这是为什么呢?因为
而observable({...})只会将属性值变为可观察
使用 mobx-react 时,推荐尽可能晚的使用间接引用值。 这是因为当使用 observable 间接引用值时 MobX 会自动重新渲染组件。 如果间接引用值发生在组件树的层级越深,那么需要重新渲染的组件就越少。
快的:
<DisplayName person={person} />
慢的:
<DisplayName name={person.name} />.
后者并没有什么错,但是当 name 属性变化时,第一种情况只会触发 DisplayName 组件重新渲染,而第二种情况组件的拥有者需要重新渲染.
这点在渲染大型数据集合时尤为重要。 React 在渲染大型数据集合时表现非常糟糕,因为协调器必须评估每个集合变化的集合所产生的组件。 因此,建议使用专门的组件来映射集合并渲染这个组件,且不再渲染其他组件:
不好的:
@observer class MyComponent extends Component { render() { const {todos, user} = this.props; return (<div> {user.name} <ul> {todos.map(todo => <TodoView todo={todo} key={todo.id} />)} </ul> </div>) } }
在上面的示例中,当 user.name 改变时,React 会不必要地协调所有的 TodoView 组件。尽管TodoView 组件不会重新渲染,但是协调的过程本身是非常昂贵的。
好的:
@observer class MyComponent extends Component { render() { const {todos, user} = this.props; return (<div> {user.name} <TodosView todos={todos} /> </div>) } } @observer class TodosView extends Component { render() { const {todos} = this.props; return <ul> {todos.map(todo => <TodoView todo={todo} key={todo.id} />)} </ul>) } }
比如:
class MyCompoent extend React.Component{ @observable text='' render(){ return <FlatList data={this.data} ListHeaderComponent={()=><Text>{this.text}</Text>}/> } }
起初看上去是没什么问题,但是结果是text的改变不会引起重新渲染,因为Text组件不是由MyComponent渲染的,而是由Flatlist渲染的.所以应该确保Flatlist也是一个observer组件,但是Flatlist是来源于外部库,所以可以用到mobx-react 内置的一个组件,Observer 组件不接受参数,只需要单个的 render 函数作为子节点
class MyCompoent extend React.Component{ @observable text='' render(){ return <FlatList data={this.data} ListHeaderComponent={()=><Observer>()=><Text>{this.text}</Text></Observer>}/> } }
或者直接
renderFooter={<Text>{this.text}</Text>}
这两种方式都可以保证目标组件是在当前组件渲染。而如果使用函数的形式同时组件没包裹在里,这样由于函数是由Flatlist在本身的组件里调用,函数返回的目标组件相当于是在Flatlist里渲染,而因为Flatlist不是observer组件(,所以不响应observable值的变化,同理,renderItem,ListFooterComponent也是如此
记住,几乎所有子组件都可以加上@observer,不仅不会对性能有影响,还会减少组件的渲染(类似pureCompoent的效果),不然可能会遇到各种坑,比如:
@observer class Parent extends Component{ @observable data=[] render(){ return <View> <Button onPress={()=>{this.data.push('a')}}></Button> <A data={this.data}/> </View> } } class A extends Compoent{ render(){ return <Text>{this.props.data}</Text> } }
点击一下button,会发生什么?你以为A组件会重新render?不,太天真了,Parent和A都不会render,因为Parent只监测data引用的改变,而data的引用并没有改变,所以如果换成
this.data=['a']
parent组件就会重新渲染,子组件自然也跟着重新渲染了(注意,只要父组件重新渲染,子组件都会通过shouldUpdateComponent()去判断是否需要渲染),data内部的数组元素的是由A监测,但是A组件不是observer组件所以不会响应变化,必须给A组件加上@observer之后才可以响应变化。
类似< Flatlist /> 这种无法给子组件加@observer的外部库,可以使用data=[this.data.slice()]这种方式
computed是采用浅比较的方式判断是否响应更新,所以返回值是数组、对象这样的结构,可以使用computed.struct进行深比较来减少更新次数
1、observable vs @observable
先来看下在class里两种定义observable对象的方式:
这两种有什么区别呢?我们尝试一下改变myObservableObject的a属性
运行之后结果是两种定义方式都会触发render 再来,我们这次改变一下myObservableObject引用的对象
结果是什么样的呢?只有第二种写法,即用了装饰符定义的对象,会触发render,这是为什么呢?因为
@observable不仅会将observable对象里的所有属性值变为可观察的,还会在class的实例将我们定义的当前对象变为可观察的
而observable({...})只会将属性值变为可观察
2、传递给子组件的props尽量延迟解构
使用 mobx-react 时,推荐尽可能晚的使用间接引用值。 这是因为当使用 observable 间接引用值时 MobX 会自动重新渲染组件。 如果间接引用值发生在组件树的层级越深,那么需要重新渲染的组件就越少。
快的:
慢的:
后者并没有什么错,但是当 name 属性变化时,第一种情况只会触发 DisplayName 组件重新渲染,而第二种情况组件的拥有者需要重新渲染.
3、在专用组件中渲染列表
这点在渲染大型数据集合时尤为重要。 React 在渲染大型数据集合时表现非常糟糕,因为协调器必须评估每个集合变化的集合所产生的组件。 因此,建议使用专门的组件来映射集合并渲染这个组件,且不再渲染其他组件:
不好的:
在上面的示例中,当 user.name 改变时,React 会不必要地协调所有的 TodoView 组件。尽管TodoView 组件不会重新渲染,但是协调的过程本身是非常昂贵的。
好的:
4、MobX 只会为数据是直接通过 render 存取的 observer 组件进行数据追踪
比如:
起初看上去是没什么问题,但是结果是text的改变不会引起重新渲染,因为Text组件不是由MyComponent渲染的,而是由Flatlist渲染的.所以应该确保Flatlist也是一个observer组件,但是Flatlist是来源于外部库,所以可以用到mobx-react 内置的一个组件,Observer 组件不接受参数,只需要单个的 render 函数作为子节点
或者直接
这两种方式都可以保证目标组件是在当前组件渲染。而如果使用函数的形式同时组件没包裹在 里,这样由于函数是由Flatlist在本身的组件里调用,函数返回的目标组件相当于是在Flatlist里渲染,而因为Flatlist不是observer组件(,所以不响应observable值的变化,同理,renderItem,ListFooterComponent也是如此
5、不要吝啬使用@observer
记住,几乎所有子组件都可以加上@observer,不仅不会对性能有影响,还会减少组件的渲染(类似pureCompoent的效果),不然可能会遇到各种坑,比如:
点击一下button,会发生什么?你以为A组件会重新render?不,太天真了,Parent和A都不会render,因为Parent只监测data引用的改变,而data的引用并没有改变,所以如果换成
parent组件就会重新渲染,子组件自然也跟着重新渲染了(注意,只要父组件重新渲染,子组件都会通过shouldUpdateComponent()去判断是否需要渲染),data内部的数组元素的是由A监测,但是A组件不是observer组件所以不会响应变化,必须给A组件加上@observer之后才可以响应变化。
类似< Flatlist /> 这种无法给子组件加@observer的外部库,可以使用data=[this.data.slice()]这种方式
6、使用可深层次的检测结构变化的computed.struct
computed是采用浅比较的方式判断是否响应更新,所以返回值是数组、对象这样的结构,可以使用computed.struct进行深比较来减少更新次数