巧用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正式釋出吧!
作者:程墨,資深架構師,曾任職於摩托羅拉、雅虎和微軟,雲鳥配送平臺聯合創始人,目前服務於美國視訊服務公司Hulu。
《深入淺出React和Redux》作者,此書由淺入深介紹如何用React和Redux構建前端專案,產出高質量易維護程式碼。