要回答這個問題,首先要問什麼是同構。所謂同構,顧名思義就是同一套代碼,既能夠運行在客戶端(瀏覽器),又能夠運行在服務器端(node)。html
咱們知道,在前端的開發過程當中,咱們通常都會有一個index.html
, 在這個文件中寫入頁面的基本內容(靜態內容),而後引入JavaScript腳本根據用戶的操做更改頁面的內容(數據)。在性能優化方面,一般咱們所說的種種優化措施也都是在這個基礎之上進行的。在這個模式下,前端全部的工做彷佛都被限制在了這一畝三分地之上。前端
那麼同構給了咱們什麼樣的不一樣呢?前面說到,在同構模式下,客戶端的代碼也能夠運行在服務器上。換句話說,咱們在服務器端就能夠將不一樣的數據組裝成頁面返回給客戶端(瀏覽器)。這給頁面的性能,尤爲是首屏性能帶來了巨大的提高可能。另外,在SEO等方面,同構也提供了極大的便利。除此之外,在整個開發過程當中,同構會極大的下降先後端的溝通成本,後端更加專一於業務模型,前端也能夠專一於頁面開發,中間的數據轉換大能夠交給node這一層來實現,省去了不少來回溝通的成本。node
說了這麼多,如何作同構開發呢?
這還得歸功於 React提供的服務端渲染。react
ReactDOMServer.renderToString ReactDOMServer.renderToStaticMarkup
不一樣於 ReactDom.render
將DOM結構渲染到頁面, 這兩個函數將虛擬DOM在服務端渲染爲一段字符串,表明了一段完整的HTML結構,最終以html的形式吐給客戶端。web
下面看一個簡單的例子:redux
// 定義組件 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組件是否知足同構要求的自動化檢測等。這些事情都是後續要一步一步去作的,到時候也會作一些整理和積累。