首页 > 代码库 > 巧用React Fiber中的渲染字符串新功能
巧用React Fiber中的渲染字符串新功能
虽然React Fiber还没有正式发布,但是我们已经可以预先领教其带来的新的编程模式了。
在React Fiber中,render函数可以直接返回一个字符串了,换言之,一个组件可以直接渲染为一个字符串,而不是必须渲染为一个HTML模样的物体。
举个例子,下面这个控件LongString,显示一个input和一个p,p中文字可以是很长的字符串,相当于一个模板,在input中输入的字符串会用来填补p中的模板面。
代码如下。
import React from ‘react‘;
class LongString extends React.Component {
constructor() {
super(...arguments);
this.onInputChange = this.onInputChange.bind(this);
this.state = {str: ‘‘};
}
onInputChange(e) {
this.setState({
str: e.target.value
});
}
render() {
console.log(‘enter render‘);
return <div>
<input onChange={this.onInputChange} />
<p>
让我们假装这是一段超长的字符串,包含 {this.state.str} 这样的子串,而且包含多个{this.state.str}.
</p>
</div>;
}
}
上面组件的工作原理是通过事件处理函数onInputChange来更新组件的state,引发组件重新渲染,这样this.state.str才能在渲染过程中被显示。
上面的组件工作完全正确,但是有个问题,就是每一次在input中更新内容,都会引发LongString的更新过程,在浏览器的console中,可以看到render函数被反复调用的痕迹。
想想看,其实LongString组件渲染了很长的字符串,每次更新的只有一小部分,却依然走整个渲染过程,实在有那么一点点浪费,有没有更好的办法呢?
在以前,也有办法,就是把更新的子串让另一个子组件来渲染,可以做到只更新那一个子组件,但是子组件必须把更新的子串放在某个HTML元素中,比如,这样就污染了LongString渲染的长字符串。假如input中是hello,产生的HTML就像下面这样。
<p>
让我们假装这是一段超长的字符串,包含<span>Hello</span>这样的子串,而且包含多个<span>Hello</span>.
</p>
而我们实际想要的是这样。
<p>
让我们假装这是一段超长的字符串,包含Hello这样的子串,而且包含多个Hello.
</p>
有了React Fiber之后,一个组件可以直接返回一个字符串了,这样我们就可以既保持字符串局部更新,又避免被HTML标签污染。
代码如下。
import React from ‘react‘;
const EventstateUpdater = require(‘events‘);
class LongStringChunked extends React.Component {
constructor() {
super(...arguments);
this.onInputChange = this.onInputChange.bind(this);
this.state = {str: ‘‘};
this.stateUpdater = new EventstateUpdater();
}
onInputChange(e) {
this.stateUpdater.emit(‘update‘, e.target.value);
}
render() {
console.log(‘enter render‘);
return <div>
<input onChange={this.onInputChange} />
<p>
让我们假装这是一段超长的字符串,包含 <Chunk listen={this.stateUpdater} /> 这样的子串,而且包含多个<Chunk listen={this.stateUpdater} />.
</p>
</div>;
}
}
class Chunk extends React.Component {
constructor(props) {
super(...arguments);
this.state = {str: ‘‘};
}
componentDidMount() {
this.props.listen.on(
‘update‘,
str => {
this.setState({str: str})
}
);
}
render() {
console.log(‘enter chunk render‘);
return this.state.str;
}
}
我们用LongStringChunked代替LongChunk,在render函数中用Chunk这个组件实例代替this.state.str,传递给Chunk的listen这个prop是一个EventEmitter,当input变化的时候,通过这个EventEmitter发出一个信号让Chunk去更新自己,而不是让LongStringChunked重新绘制。
<Chunk listen={this.stateUpdater} />
重新尝试在input里写点啥,在浏览器console中可以看到,LongStringChunked的render函数没有被反复调用,只有Chunk组件被反复渲染,因为Chunk组件渲染的内容要比LongStringChunked少得多,所以(理论上)要节省得多。
对于LongStringChunked的render函数,既然多个Chunk实例都是一样的,可以这样写显得漂亮点。
render() {
console.log(‘enter render‘);
const chunk = <Chunk listen={this.stateUpdater} />;
return <div>
<input onChange={this.onInputChange} />
<p>
让我们假装这是一段超长的字符串,包含{chunk}这样的子串,而且包含多个{chunk}.
</p>
</div>;
}
当然,你要问,这种优化真的能节约多少?我只能说看具体情况。
这只是展示React Fiber引入的一个新功能,并不表示你必须在实际工作中应用,实际上,只有在性能真的很成问题的时候,才需要去做这样的优化,不过,了解一点新技巧没什么坏处,对吧。
我们知道有这样一个武器,但是不一定要去用他,我们手里握着核弹,一样也不一定要使用它。
让我们一起期待React Fiber正式发布吧!
巧用React Fiber中的渲染字符串新功能