通常,當客戶端請求一個包含React組件頁面的時候,服務端首先響應輸出這個頁面,客戶端和服務端有了第一次交互。然後,如果載入組件的過程需要向服務端發出Ajax請求等,客戶端和服務端又進行了一次交互,這樣,耗時相對較長。服務端是否可以在頁面初次載入時把所有方面渲染好再一次性響應給客戶端呢? 「Reac ...
通常,當客戶端請求一個包含React組件頁面的時候,服務端首先響應輸出這個頁面,客戶端和服務端有了第一次交互。然後,如果載入組件的過程需要向服務端發出Ajax請求等,客戶端和服務端又進行了一次交互,這樣,耗時相對較長。服務端是否可以在頁面初次載入時把所有方面渲染好再一次性響應給客戶端呢?
「React同構直出」就是用來解決這個問題的,做到「秒開」頁面。過程大致是這樣滴:
1、在需要同構直出的頁面(比如是index.html)放上占位符
<div id="root">@@@</div>
###
以上,當客戶端發出首次請求,服務端渲染出組件的html內容放@@@這個位置,然後服務端再渲染出類似<script>renderApp()</script>
這樣的js代碼段把組件最終渲染到DOM上。也就是說,renderApp方法實際上就是在渲染組件。
2、而為了直接調用renderApp方法,必須讓renderApp方法成為window下的方法
window.renderApp = function(){ReactDOM.render(...)}
3、服務端取出index.html,渲染出占位符的內容,替代占位符,並一次性響應給客戶端
通過一個例子來體會。
文件結構
browser.js(在這裡把渲染組件的過程賦值給window.renderApp)
bundle.js(把browser.js內容bundle到這裡)
Component.js(組件在這裡定義)
express.js(服務端)
index.html(同構直出的頁面)
package.json
index.html,直出頁面放上占位符
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
</head>
<body>
<div id="root">@@@</div>
<script src="bundle.js"></script>
###
</body>
</html>
Component.js,在這裡定義組件
var React = require('react');
var ReactDOM = require('react-dom');
var Component = React.createClass({
clickHandler: function(){
alert(this.props.msg)
},
render: function(){
return React.createElement('button', {onClick: this.clickHandler}, this.props.msg)
}
})
module.exports = Component;
browser.js,把組件渲染過程賦值給window對象
var React = require('react');
var ReactDOM = require('react-dom');
var Component = React.createFactory(require('./Component'));
window.renderApp = function(msg){
ReactDOM.render(Component({msg: msg}), document.getElementById('root'));
}
可以通過<script>render()</script>
來觸發組件的渲染。稍後,在服務端會把這段代碼渲染出來。
express.js,服務端
以上,需要直出的頁面有了占位符,定義了組件,並把渲染組件的過程賦值給了window對象,服務端現在要做的工作就是:生成組件的html和渲染組件的js,放到直出頁面index.html的占位符位置。
var express = require('express');
var React = require('react');
var ReactDOMServer = require('react-dom/server');
var fs = require('fs');
var Component = React.createFactory(require('./Component'));
//原先把文件讀出來
var BUNDLE = fs.readFileSync('./bundle.js',{encoding:'utf8'});
var TEMPLATE = fs.readFileSync('./index.html',{encoding:'utf8'});
var app = express();
function home(req, res){
var msg = req.params.msg || 'Hello';
var comp = Component({msg: msg});
//@@@占位符的地方放組件
var page = TEMPLATE.replace('@@@', ReactDOMServer.renderToString(comp));
//###占位符的地方放js
page = page.replace('###', '<script>renderApp("'+msg+'")</script>')
res.send(page);
}
//路由
app.get('', home);
app.get('/bundle.js', function(req, res){
res.send(BUNDLE);
})
app.get('/:msg', home);
app.listen(4000);
package.json中的配置
"scripts": {
"start": "watchify ./browser.js -o ./bundle.js"
},
運行:npm start
運行:node express.js
瀏覽:localhost:4000
項目地址:https://github.com/darrenji/ReactIsomorphicSimpleExample