React 同構開發(一)


為什么要做同構

要回答這個問題,首先要問什么是同構。所謂同構,顧名思義就是同一套代碼,既可以運行在客戶端(瀏覽器),又可以運行在服務器端(node)。

我們知道,在前端的開發過程中,我們一般都會有一個index.html, 在這個文件中寫入頁面的基本內容(靜態內容),然后引入JavaScript腳本根據用戶的操作更改頁面的內容(數據)。在性能優化方面,通常我們所說的種種優化措施也都是在這個基礎之上進行的。在這個模式下,前端所有的工作似乎都被限制在了這一畝三分地之上。

那么同構給了我們什么樣的不同呢?前面說到,在同構模式下,客戶端的代碼也可以運行在服務器上。換句話說,我們在服務器端就可以將不同的數據組裝成頁面返回給客戶端(瀏覽器)。這給頁面的性能,尤其是首屏性能帶來了巨大的提升可能。另外,在SEO等方面,同構也提供了極大的便利。除此以外,在整個開發過程中,同構會極大的降低前后端的溝通成本,后端更加專注於業務模型,前端也可以專注於頁面開發,中間的數據轉換大可以交給node這一層來實現,省去了很多來回溝通的成本。

基於React的同構開發

說了這么多,如何做同構開發呢?
這還得歸功於 React提供的服務端渲染。

ReactDOMServer.renderToString  
ReactDOMServer.renderToStaticMarkup

不同於 ReactDom.render將DOM結構渲染到頁面, 這兩個函數將虛擬DOM在服務端渲染為一段字符串,代表了一段完整的HTML結構,最終以html的形式吐給客戶端。

下面看一個簡單的例子:

// 定義組件 
import React, { Component, PropTypes } from 'react';

class News extends Component {
	constructor(props) {
		super(props);
	}

	render() {
		var {data} = this.props;
		return <div className="item">
      <a href={data.url}>{ data.title }</a>
    </div>;
	}
}

export default News;

我們在客戶端,通常通過如下方式渲染這個組件:

// 中間省略了很多其他內容,例如redux等。
let data = {url: 'http://www.taobao.com', title: 'taobao'}
ReactDom.render(<News data={data} />, document.getElementById("container"));

在這個例子中我們寫死了數據,通常情況下,我們需要一個異步請求拉取數據,再將數據通過props傳遞給News組件。這時候的寫法就類似於這樣:

Ajax.request({params, success: function(data) {
	ReactDom.render(<News data={data} />, document.getElementById("container"));	
}});

這時候,異步的時間就是用戶實際等待的時間。

那么,在同構模式下,我們怎么做呢?

// 假設我們的web服務器使用的是KOA,並且有這樣的一個controller  
function* newsListController() {
	
  const data = yield this.getNews({params});

  const data = {
    'data': data
  };
  
  this.body = ReactDOMServer.renderToString(News(data));
};

這樣的話,我么在服務端就生成了頁面的所有靜態內容,直接的效果就是減少了因為首屏數據請求導致的用戶的等待時間。除此以外,在禁用JavaScript的瀏覽器中,我們也可以提供足夠的數據內容了。

什么原理

其實,react同構開發並沒有上面的例子那么簡單。上面的例子只是為了說明服務端渲染與客戶端渲染的基本不同點。其實,及時已經在服務端渲染好了頁面,我們還是要在客戶端重新使用ReactDom.render函數在render一次的。因為所謂的服務端渲染,僅僅是渲染靜態的頁面內容而已,並不做任何的事件綁定。所有的事件綁定都是在客戶端進行的。為了避免客戶端重復渲染,React提供了一套checksum的機制。所謂checksum,就是React在服務端渲染的時候,會為組件生成相應的校驗和(checksum),這樣客戶端React在處理同一個組件的時候,會復用服務端已生成的初始DOM,增量更新,這就是data-react-checksum的作用。

所以,最終,我們的同構應該是這個樣子的:

// server 端  
function* newsListController() {
	
  const data = yield this.getNews({params});

  const data = {
    'data': data
  };
  let news = ReactDOMServer.renderToString(News(data));
  this.body = '<!doctype html>\n\
                      <html>\
                        <head>\
                            <title>react server render</title>\
                        </head>\
                        <body><div id="container">' +
                            news +
                            '</div><script>var window.__INIT_DATA='+ JSON.stringify(data) +'</script><script src="app.js"></script>\
                        </body>\
                      </html>';
};

// 客戶端,app.js中  
let data = JSON.parse(window.__INIT_DATA__);  
ReactDom.render(<News props={data} />, document.getElementById("container"));

小結

最近一直在做同構相關的東西,本文主要討論react同構開發的基本原理和方式,作為一個引子,其中省去了很多細節問題。關於同構應用開發,其實有很多事情要做,比如node應用的發布、監控、日志管理,react組件是否滿足同構要求的自動化檢測等。這些事情都是后續要一步一步去做的,到時候也會做一些整理和積累。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM