Professional Documents
Culture Documents
Universal Studios - Server-Side Rendering With React Is A Marvel!
Universal Studios - Server-Side Rendering With React Is A Marvel!
Universal Studios - Server-Side Rendering With React Is A Marvel!
@giltayar
@tomerdoesntliketwitter
Server-side rendering - what is it good for?
The Dawn of Computing...
A History of Clients
In the beginning...
… was the mainframe
The Mainframe Terminal
1. sends a page
The Mini-Computer
The Personal Computer (PC)
Queries & Commands
Client/Server
The Internet and the World Wide Web
1. sends a page
3. sends input values
5. returns page
Granular Interaction
mini
Granular Interaction
mini
Client/Server
PC
Granular Interaction
mini
Client/Server
PC
Pro
tot
ype Angular 1
Server vs Client Languages
Server Client
● C++ ● JavaScript
● Java
● C#
● Python
● Ruby
● Scala
● Erlang
● Haskell
● Clojure
NodeJS
The Rise of the Isomorphic Frameworks
The Rise of the Isomorphic Frameworks
Code
Code
CommonJS
Code
Module
Universal Apps
What’s So Bad About Client-Side
Rendering?
TTF
E O R
S
What’s So Bad About Client-Side
Rendering?
SEO
Search Engine Optimization
TTFR
Time To First Render
1. Get HTML
2. Get JS
3. Run JS
4. Get Data
5. Render Data
The Seven Stages Towards Full Server-
side Rendering
#1. client-side-react-rendering
app.js
const express = require('express')
app.use('/index.html',
express.static(__dirname + '/src/public/index.html'))
app.use('/bundle.js',
express.static(__dirname + '/lib/public/bundle.js'))
app.listen(process.env.PORT || 3000)
public/index.html
<html>
<body>
<div id="root" />
<script src="bundle.js"></script>
</body>
</html>
public/app.jsx
ReactDOM.render(helloWorld,
document.getElementById('root'))
public/hello.jsx
module: { loaders: [{
loader: 'babel-loader',
test: /\.jsx?$/,
include: __dirname + "/src"
}]}
};
.babelrc
{
"presets": ["es2015", "react"]
}
#2. isomorphic-react-rendering
app.js
const express = require('express')
const ReactDomServer = require('react-dom/server')
const helloWorld = require('./src/public/hello.jsx')
ReactDOM.render(helloWorld,
document.getElementById('root'))
#3. stateful-react-app
public/counter.jsx
module.exports = class Counter extends React.Component {
constructor(props) {
this.state = {value: 0}
}
render() {
const {value} = this.state;
return (<div>
<button onClick={() => this.setState({ value: value - 1 }) }>-</button>
<span>{value}</span>
<button onClick={() => this.setState({ value: value + 1 }) }>+</button>
</div>)
}
}
public/app.jsx &app.js
...
const Counter = require('./counter.jsx')
<Counter/>)}</div>
<div id="root">${ReactDomServer.renderToString(
<script src="bundle.js"></script>
</body></html>`))
#4. state-in-redux
public/app.jsx
...
const rootFactory = require('./Root.jsx')
const {createStore} = require('./createStore.jsx')
const initialState = 0
const store = createStore(initialState)
const Root = rootFactory(store)
res.send(`
<html><body>
<div id="root">${ReactDomServer.renderToString(<Root/>)}</div>
<script src="bundle.js"></script>
</body></html>`)
})
public/createStore.jsx
const {createStore} = require('redux')
res.send(`
<html><body>
<div id="root">${ReactDomServer.renderToString(<Root/>)}</div>
<script src="bundle.js"></script>
</body></html>`)
})
app.js
app.use('/index.html', (req, res) => {
const initialState = parseInt(req.query['start-from'] || '0')
const store = createStore(initialState)
const Root = rootFactory(store)
res.send(`
<html><body>
<div id="root">${ReactDomServer.renderToString(<Root/>)}</div>
<script>window.__INITIAL_STATE__ =
${JSON.stringify(store.getState())}</script>
<script src="bundle.js"></script>
public/app.jsx
res.send(`
<html><body>
<div id="root">${ReactDomServer.renderToString(<Root/>)}</div>
<script src="bundle.js"></script>
</body></html>`)
})
public/counter.jsx
class Counter extends React.Component {
render() {
const {value} = this.props;
return (<div>
<button onClick={() => this.props.dispatch({ type: 'DEC' }) }>-</button>
<span>{value}</span>
<button onClick={() => this.props.dispatch({ type: 'INC' }) }>+</button>
<button onClick={() => doSave(value)}>Save</button>
</div>)
}
}
const doSave = (value) => fetch('/save?value=' + value, {method: 'POST'})
app.js
res.end()
})
#7. universal-app
public/counter.jsx
class Counter extends React.Component {
render() {
const {value} = this.props;
return (<div>
<button onClick={() => this.props.dispatch({ type: 'DEC' }) }>-</button>
<span>{value}</span>
<button onClick={() => this.props.dispatch({ type: 'INC' }) }>+</button>
<button onClick={() => doSave(value)}>Save</button>
<button onClick={() => dispatch(doRefresh())}>Refresh</button>
</div>)
}
}
public/refresher.js
const fetch = require('isomorphic-fetch')
store.dispatch(doRefresh()).then(() => {
res.send(`
<html><body>
<div id="root">${ReactDomServer.renderToString(<Root/>)}</div>
<script src="bundle.js"></script>
</body></html>`)
})
})
public/app.jsx
...
const doRefreshFactory = require('./refresher')