React同構直出原理淺析

一般,當客戶端請求一個包含React組件頁面的時候,服務端首先響應輸出這個頁面,客戶端和服務端有了第一次交互。而後,若是加載組件的過程須要向服務端發出Ajax請求等,客戶端和服務端又進行了一次交互,這樣,耗時相對較長。服務端是否能夠在頁面初次加載時把全部方面渲染好再一次性響應給客戶端呢?html

「React同構直出」就是用來解決這個問題的,作到「秒開」頁面。過程大體是這樣滴:node

一、在須要同構直出的頁面(好比是index.html)放上佔位符react

<div id="root">@@@</div>
###

以上,當客戶端發出首次請求,服務端渲染出組件的html內容放@@@這個位置,而後服務端再渲染出相似<script>renderApp()</script>這樣的js代碼段把組件最終渲染到DOM上。也就是說,renderApp方法實際上就是在渲染組件。git

二、而爲了直接調用renderApp方法,必須讓renderApp方法成爲window下的方法github

window.renderApp = function(){ReactDOM.render(...)}

三、服務端取出index.html,渲染出佔位符的內容,替代佔位符,並一次性響應給客戶端express


經過一個例子來體會。npm

文件結構

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>來觸發組件的渲染。稍後,在服務端會把這段代碼渲染出來。json

express.js,服務端

以上,須要直出的頁面有了佔位符,定義了組件,並把渲染組件的過程賦值給了window對象,服務端如今要作的工做就是:生成組件的html和渲染組件的js,放到直出頁面index.html的佔位符位置。app

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 startdom

運行:node express.js

瀏覽:localhost:4000

項目地址:https://github.com/darrenji/ReactIsomorphicSimpleExample

相關文章
相關標籤/搜索