优化建议
MobX 和 React 结合使用时,有一些常用的优化技巧可以帮助提高组件的渲染效率。
在单独的组件中渲染列表数据
列表数据的渲染是比较耗费性能的,尤其是在列表数据量大的情况下,例如:
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 的改变会导致重新创建一个 TodoView 元素的数组,虽然这并不会导致重复渲染这些 TodoView,但 React 比较新旧 TodoView 元素的过程本身也很耗费性能。所以,更好的写法是:
class MyComponent extends Component {
render() {
const { todos, user } = this.props;
return (
<div>
{user.name}
<TodosView todos={todos} />
</div>
);
}
}
class TodosView extends Component {
render() {
const { todos } = this.props;
return <ul>{todos.map(todo => <TodoView todo={todo} key={todo.id} />)}</ul>;
}
}
尽可能晚地解引用(dereference)对象属性
MobX 通过追踪对象属性的访问来追踪值的变化,所以在层级越低的组件中解引用对象属性,由这个属性的变化导致的重新渲染的组件的数量就越少。(只有解引用对象属性的组件及其子组件会重新渲染)。例如:
// 方式1
class DisplayName extends Component {
render() {
const {person} = this.props
return <div>{person.name}</div>
}
}
class MyComponent extends Component {
render() {
const {person} = this.props
return <DisplayName person={person} />
}
}
// 方式2
@observer
class DisplayName extends Component {
render() {
const {name} = this.props
return <div>{name}</div>
}
}
class MyComponent extends Component {
render() {
const {person} = this.props
return <DisplayName name={person.name} />
}
}
person 是一个可观测对象,对于方式 1,当 person 的属性 name 发生变化时,DisplayName 会自动重新渲染,而不需要通过父组件 MyComponent 的重新渲染来触发。对于方式 2,DisplayName 使用的是 name 这个值,是不可观测的,因此,要想让 DisplayName 重新渲染,首先必须让 DisplayName 的父组件 MyComponent 重新渲染,这样就导致更多组件会发生重复渲染。
但方式 2 更容易理解,对于 DisplayName 组件仅需要接收 name 作为属性就足够了,接收 person 作为属性反而有些多余。为了兼顾效率和可读性,可以这样实现:
const PersonNameDisplayer = observer(({props}) => <DisplayName name={props.person.name} />)
这里新增了一个组件 PersonNameDisplayer,由这个组件负责渲染 DisplayName,PersonNameDisplayer 会自动响应 name 的变化,重新渲染 DisplayName 组件,同时 DisplayName 组件仍然只需要接收 name 属性即可。本质上还是使用小组件的思路进行优化。
提前绑定函数
例如:
@observer
class MyComponent extends Component {
render() {
return <MyWidget onClick={() => { alert('hi')}} />
}
}
这种写法,MyComponent 的 render 方法每次被调用时,MyWidget 的 onClick 属性的值都是一个新的函数,导致 MyWidget 的 render 方法一定会被重新调用,而无论其他属性是否发生变化,MobX 对组件渲染做的优化工作都会浪费。更好的写法是:
@observer
class MyComponent extends Component {
handleClick = () => {
alert('hi')
}
render() {
return <MyWidget onClick={this.handleClick} />
}
}
本节介绍的这几种优化方法虽然看似很基础,但读者真正掌握并理解这些优化方法后,相信会对 MobX 有更加深入的理解。