1. 程式人生 > >巧用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正式釋出吧!

作者:程墨,資深架構師,曾任職於摩托羅拉、雅虎和微軟,雲鳥配送平臺聯合創始人,目前服務於美國視訊服務公司Hulu。

《深入淺出React和Redux》作者,此書由淺入深介紹如何用React和Redux構建前端專案,產出高質量易維護程式碼。

圖片描述