巧用React Fiber中的渲染字元串新功能

雖然React Fiber還沒有正式發布,但是我們已經可以預先領教其帶來的新的編程模式了。

在React Fiber中,render函數可以直接返回一個字元串了,換言之,一個組件可以直接渲染為一個字元串,而不是必須渲染為一個HTML模樣的物體。

舉個例子,下面這個控制項LongString,顯示一個input和一個p,p中文字可以是很長的字元串,相當於一個模板,在input中輸入的字元串會用來填補p中的模板。

代碼如下。

import React from react;nnnclass LongString extends React.Component {n constructor() {n super(...arguments);nn this.onInputChange = this.onInputChange.bind(this);n this.state = {str: };n }nn onInputChange(e) {n this.setState({n str: e.target.valuen });n }nn render() {n console.log(enter render);n return <div>n <input onChange={this.onInputChange} />n <p>n 讓我們假裝這是一段超長的字元串,包含 {this.state.str} 這樣的子串,而且包含多個{this.state.str}.n </p>n </div>;n }n}n

上面組件的工作原理是通過事件處理函數onInputChange來更新組件的state,引發組件重新渲染,這樣this.state.str才能在渲染過程中被顯示。

上面的組件工作完全正確,但是有個問題,就是每一次在input中更新內容,都會引發LongString的更新過程,在瀏覽器的console中,可以看到render函數被反覆調用的痕迹。

想想看,其實LongString組件渲染了很長的字元串,每次更新的只有一小部分,卻依然走整個渲染過程,實在有那麼一點點浪費,有沒有更好的辦法呢?

在以前,也有辦法,就是把更新的子串讓另一個子組件來渲染,可以做到只更新那一個子組件,但是子組件必須把更新的子串放在某個HTML元素中,比如<span></span>,這樣就污染了LongString渲染的長字元串。假如input中是hello,產生的HTML就像下面這樣。

<p>n 讓我們假裝這是一段超長的字元串,包含<span>Hello</span>這樣的子串,而且包含多個<span>Hello</span>.n </p>n

而我們實際想要的是這樣。

<p>n 讓我們假裝這是一段超長的字元串,包含Hello這樣的子串,而且包含多個Hello.n </p>n

有了React Fiber之後,一個組件可以直接返回一個字元串了,這樣我們就可以既保持字元串局部更新,又避免被HTML標籤污染。

代碼如下。

import React from react;nconst EventstateUpdater = require(events);nnclass LongStringChunked extends React.Component {n constructor() {n super(...arguments);nn this.onInputChange = this.onInputChange.bind(this);n this.state = {str: };n this.stateUpdater = new EventstateUpdater();n }nn onInputChange(e) {n this.stateUpdater.emit(update, e.target.value);n }nn render() {n console.log(enter render);n return <div>n <input onChange={this.onInputChange} />n <p>n 讓我們假裝這是一段超長的字元串,包含 <Chunk listen={this.stateUpdater} /> 這樣的子串,而且包含多個<Chunk listen={this.stateUpdater} />.n </p>n </div>;n }n}nnclass Chunk extends React.Component {n constructor(props) {n super(...arguments);nn this.state = {str: };n }nn componentDidMount() {n this.props.listen.on(n update,n str => {n this.setState({str: str})n }n );n }nn render() {n console.log(enter chunk render);n return this.state.str;n }n}n

我們用LongStringChunked代替LongChunk,在render函數中用Chunk這個組件實例代替this.state.str,傳遞給Chunk的listen這個prop是一個EventEmitter,當input變化的時候,通過這個EventEmitter發出一個信號讓Chunk去更新自己,而不是讓LongStringChunked重新繪製。

<Chunk listen={this.stateUpdater} /> n

重新嘗試在input里寫點啥,在瀏覽器console中可以看到,LongStringChunked的render函數沒有被反覆調用,只有Chunk組件被反覆渲染,因為Chunk組件渲染的內容要比LongStringChunked少得多,所以(理論上)要節省得多。

對於LongStringChunked的render函數,既然多個Chunk實例都是一樣的,可以這樣寫顯得漂亮點。

render() {n console.log(enter render);n const chunk = <Chunk listen={this.stateUpdater} />;n return <div>n <input onChange={this.onInputChange} />n <p>n 讓我們假裝這是一段超長的字元串,包含{chunk}這樣的子串,而且包含多個{chunk}.n </p>n </div>;n }n

當然,你要問,這種優化真的能節約多少?我只能說看具體情況。

這只是展示React Fiber引入的一個新功能,並不表示你必須在實際工作中應用,實際上,只有在性能真的很成問題的時候,才需要去做這樣的優化,不過,了解一點新技巧沒什麼壞處,對吧。

我們知道有這樣一個武器,但是不一定要去用他,我們手裡握著核彈,一樣也不一定要使用它。

讓我們一起期待React Fiber正式發布吧!

相關閱讀: React Fiber是什麼 - 知乎專欄


推薦閱讀:

前端每周清單第 21 期:JS 項目開發樣式指南;基於 Vue 的分角色許可權驗證;深入 React.js 內部原理
創建 React 動畫的五種方式
在 componentWillUnmount 中到底應該清除哪些變數?
精讀 React functional setState

TAG:React | 前端开发 |