《ReactJS》讀書筆記十五
服務端渲染
React 的虛擬 DOM 是其可被用于服務端渲染的關鍵。首先,每個 React 組件在虛擬 DOM 中完成渲染,然后 React 通過虛擬 DOM 來更新瀏覽器 DOM 中產生變化的那一部分。虛擬 DOM 作為內存中的 DOM 表現,為 React 在 Node.js 這類非瀏覽器環境下的運行提供了可能。React 可以從虛擬 DOM 中生成一個字符串,而不是更新真正的 DOM。這使得我們可以在客戶端和服務端使用同一個 React 組件
React 提供了兩個可用于服務端渲染組件的函數: React.renderToString() 和 React.renderToStaticMarkup() 。
在設計用干服務端渲染的 React 組件時需要有預見性,需要考慮以下方面。
- 選取最優的渲染函數。
- 如何支持組件的異步狀態。
- 如何將應用的初始狀態傳遞到客戶端。
- 那些生命周期函數可以用于服務端渲染。
- 如何為應用提供同構路由支持。
- 單例、實例以及上下文的用法。
渲染函數
服務端渲染對于搜索引擎優化來說至關重要。因為服務端不存在 DOM,所以無法使用標準的 React.render() 方法來渲染 React 組件,因此 React 還提供了兩個渲染函數,它們支持標準 React 組件生命周期方法的一個子集,因而能夠實現服務端渲染。
React.renderToString()
React.renderToString() 是兩個服務端渲染函數中的一個,也是開發中主要使用的一個函數。和 React.render() 不同,該函數去掉了用于表示渲染位置的參數。取而代之,該函數只返回一個字符串。這是一個快速的同步(阻塞式)函數,非常快。
var MyComponent = React.createClass({ render: function(){ return <div>Hello World!</div>; } }); var world = React.renderToString(<MyComponent/>); <div data-reactid=".fgvrzhg2yo" data-react-checksum="-1663559667"> Hello World! </div>
你會注意到,React 為這個 <div> 元素添加了兩個 data 前綴的屬性。
在瀏覽器環境下,React 使用 data-reactid 來區分 DOM 節點。這也是每當組件的 state 及 props 發生變化時,React 都可以精準地更新指定 DOM 節點的原因。
data-react-checksum 僅僅存在于服務端。顧名思義,它是已創建 DOM 的校驗和。這準許 React 在客戶端復用與服務端結構上相同的 DOM 結構。該屬性只會添加到根元素上。
React.renderToStaticMarkup()
React.renderToStaticMarkup() 是第二個服務端渲染函數。
除了不會包含 React 的 data 屬性外,它和 React.renderToString() 沒有區別。
var MyComponent = React.createClass({ render: function(){ return <div>Hello World!</div> } }); var world = React.renderToStaticMarkup(<MyComponent/>); <div>Hello World!</div>
用 React.renderToString() 還是用 React.renderToStaticMarkup()
每個渲染函數都有自己的用途,所以你必須明確自己的需求,再去決定使用哪個渲染函數。當且僅當你不打算在客戶端渲染這個 React 組件時,才應該選擇使用 React.renderToStaticMarkup() 函數。
下面是一些示例:
- 生成 HTML 電子郵件。
- 通過 HTML 到 PDF 的轉化來生成 PDF。
- 組件測試。
大多數情況下,我們都會選擇使用 React.renderToString() 。這將準許 React 使用 data-react-checksum 在客戶端更迅速地初始化同一個 React 組件因為 React 可以重用服務端提供的 DOM,所以它可以跳過生成 DOM 節點以及把它們掛載到文檔中這兩個昂貴的進程。對于復雜些的站點,這樣做會顯著地減少加載時間,用戶可以更快地與站點進行交互。
確保 React 組件能夠在服務端和客戶端準確地渲染出一致的結構是很重要的。如果 data-react-checksum 不匹配,React 會舍棄服務端提供的 DOM,然后生成新的 DOM 節點,并且將它們更新到文檔中。此時,React 也不再擁有服務端渲染帶來的各種性能上的優勢。
服務端組件生命周期
一旦渲染為字符串,組件就會只調用位于 render() 之前的組件生命周期方法。需要指出, componentDidMount() 和 componentWillUnmount() 不會在服務端渲染過程中被調用,而 componentWillMount() 在兩種渲染方式下均有效。
當新建一個組件時,你需要考慮到它可能既在服務端又在客戶端進行渲染。這一點在創建事件監聽器時尤為重要,因為并不存在一個生命周期方法會通知我們該 React 組件是否已經走完了整個生命周期。
在 componentWillMount() 內注冊的所有事件監聽器及定時器都可能潛在地導致服務端內存泄漏。
最佳做法是只在 componentDidMount() 內部創建事件監聽器及定時器,然后在 componentWillUnmount() 內清除這兩者。
總結
服務端渲染對于搜索引擎優化來說至關重要。而 React 支持在服務端和客戶端瀏覽器中渲染相同的 React 組件而要有效地做到這一點,則需要保證整個應用都使用這一架構方式以支持服務端渲染。
來自: http://justclear.github.io/developing-a-react-edge-notes-for-part-15